我可以使用 int8_t 代替 char 吗?
Posted
技术标签:
【中文标题】我可以使用 int8_t 代替 char 吗?【英文标题】:Can I use int8_t instead of char? 【发布时间】:2022-01-23 00:25:30 【问题描述】:我做了一个如下所示的短代码。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
int32_t main(int32_t argc, int8_t *argv[])
int32_t i;
if (argc < 1)
printf("Error\n");
for (i = 0 ; i < argc ; i++)
printf("argv[%d] = %s\n", i, argv[i]);
return 0;
如下所示编译,然后我看到如下警告。
$ gcc -W -Wall main.c
main.c:6:9: warning: second argument of ‘main’ should be ‘char **’ [-Wmain]
int32_t main(int32_t argc, int8_t *argv[])
使用 int8_t 的最佳做法是什么?
【问题讨论】:
char
和 signed char
(int8_t
可能是 typedef 的)是不同的类型,即使 char
已签名。见***.com/questions/2054939/…
【参考方案1】:
你的方法有很多缺点:
main()
的原型与任何标准原型都不兼容,即使 int
具有 32 位并且如果 char
已签名,因为 char
和 signed char
是不同但兼容的类型,但 @987654326 @ 和 signed char *
是不兼容的类型。
如果int
与int32_t
不一样,则原型肯定与标准不兼容,行为未定义。
如果int
类型与int
不完全相同,printf("argv[%d] = %s\n", i, argv[i]);
将具有未定义的行为。您可以使用来自<inttypes.h>
的宏,但它们相当繁琐,并且代码可读性较差。
因此main
函数的原型应该是int main(int argc, char *argv[])
,并且为了保持一致性,i
应该定义为与argc
相同的类型:int
。
生成的代码非常简单易读:
#include <stdio.h>
int main(int argc, char *argv[])
int i;
if (argc < 1)
printf("Error\n");
for (i = 0; i < argc; i++)
printf("argv[%d] = %s\n", i, argv[i]);
return 0;
我认为关于int8_t
与char
的最佳实践是对main
和所有库函数使用未更改的标准原型。还建议使用char
用于文本的实际字符,而不是int8_t
和uint8_t
用于从二进制内容读取的有符号和无符号字节。字符串文字应被视为const
并通过const char *
进行操作。无论 char 类型的符号性如何,代码都应以定义的方式运行。这不仅仅是风格问题,这是提高代码可读性和坚固性、避免混淆和一些错误的明智习惯。
【讨论】:
【参考方案2】:从根本上讲,我相信您是在问一个风格问题,这意味着您不太可能得到明确的答案。对风格的意见,嗯,意见。
有些人认为你应该使用 C 的“自然”类型——char
、short
、int
、long
,以及它们的 unsigned
变体——大多数时候,你应该使用精确-size 类型,如int32_t
,仅在您绝对需要时使用。
有些人认为“自然”类型所暗示的可变性是错误的源头,他们认为您应该始终使用精确大小的类型。
现在,说了这么多,具体的写法
int32_t main(int32_t argc, int8_t *argv[])
客观上是错误的,至少有三个原因:
-
在
int
类型为16 位的平台上,这会错误地将main
的返回类型和argc
参数的类型声明为32 位类型。
在char
类型为无符号的平台上,这会错误地将argv
声明为有符号字符指针数组。这可能就是 gcc 为您抱怨的原因。
在更哲学的层面上,main
不是您可以选择其函数签名的函数。其他人声明main
,其他人调用main
,您的工作只是为main
提供定义。因此,您只需使用其他人指定的类型,即使您的规则是尽可能使用精确大小的类型。在这里,你不能。
底线:请为main
使用这两种(等效)形式之一,并带有参数:
int main(int argc, char *argv[])
int main(int argc, char **argv)
除非您正在编写“独立”代码,否则任何其他内容都是令人困惑、误导、不标准或错误的。 (定义一个不带参数的main
也是可以接受的。)
使用 int8_t 的最佳做法是什么?
我会说,当您真的需要一个很小的 8 位有符号整数时,或者当您将内存作为有符号字节进行操作时。但我不会到处使用int8_t
而不是char
,因为它会给你带来很多问题,而且不会给你带来任何好处。
【讨论】:
这不是风格问题。更准确地说:int8_t
可能是 signed char
的别名,它与 char
的类型不同,即使 char
默认已签名。
该标准包含文本“...或等价物”,通过脚注“因此,int 可以替换为定义为 int 的 typedef 名称,或者可以编写 argv 的类型如 char ** argv 等等。”
@chqrlie 问题的标题是一个风格问题。关于main
的具体例子不是。
我同意这是风格问题。实际上,我喜欢使用精确尺寸类型而不是自然/传统类型的样式。但如前所述,我不能将它用于 main() 的 int8_t。即使 main() 只是我遇到问题的一种情况,这也让我感到困扰。因此,这意味着最佳实践是接受 main() 作为例外情况。好像。
@Cprogrammer:我认为最佳实践是对main
和所有库函数使用未更改的标准原型。还建议将char
用于文本的实际字符,而int8_t
和uint8_t
用于从二进制内容读取的有符号和无符号字节。字符串文字应被视为const
并通过const char *
进行操作。无论char
类型的签名如何,代码都应以定义的方式运行。这不仅仅是风格问题,这是提高可读性和坚固性、避免混淆和一些错误的明智习惯。【参考方案3】:
使用 int8_t 的最佳做法是什么?
当您需要一个非常小的有符号整数时。这与char
不同。 char
有三种风格:
signed char
unsigned char
char
如果您知道您想要一个签名的int8_t
,请使用它。如果您正在处理标准字符串 API:s,例如 main
,请使用 char
。
int8_t
甚至不需要存在。它是特定于实现的。有些平台不存在。
您使用 int32_t
代替 int
也是如此。它不一定存在,即使它存在,也不一定是 typedef
对应于 int
- 如果您想保持便携性,请使用 int
。
【讨论】:
【参考方案4】:您发布的是main()
的实现定义形式。仅在两种情况下允许:
int main (int argc, char** argv)
100% 兼容,要么
这是一种实现定义的形式,编译器文档告诉您可以使用。
决定 main() 的可接受形式的是 C 标准和编译器,而不是程序员。
值得注意的是,int8_t
可能与 char
兼容,也可能不兼容,因为 char
具有实现定义的签名。
【讨论】:
即使char
默认签名,signed char
和char
也是不同的类型。
@chqrlie 是的,但如果它们具有相同的签名,则它们是兼容的类型。 “char、signed char 和 unsigned char 这三种类型统称为字符类型。实现应将 char 定义为与有符号字符或无符号字符具有相同的范围、表示和行为。”
@Lundin 但是作为不同的类型,char**
与 signed char**
不兼容。不管怎样,它们是不同的类型意味着main
函数定义不是一个有效的标准。
@Kevin main()
可以有各种签名int main(void)
、int main(int argc, char *argv[])
、等效或一些其他实现定义的方式。 signed char **argv
不一定是无效的,但可能是。
@chux-ReinstateMonica 够公平的。我没有意识到允许实现为 main 提供其他有效签名:/* another implementation-defined signature */
(since C99)以上是关于我可以使用 int8_t 代替 char 吗?的主要内容,如果未能解决你的问题,请参考以下文章
int32、int、int32_t、int8和int8_t的区别
列值采用 0 或 nul 代替 HIVE 中的 char 数据类型