为什么argc是'int'类型(而不是'unsigned int')?

65

为什么命令行参数数量变量(传统上是argc)是int而不是unsigned int?这背后是否有技术原因?

当我试图消除所有签名无符号比较警告时,我总是忽略了它,但从未理解过它为什么是这样。

13个回答

58

原始的 C 语言默认情况下将任何变量或参数定义为 int 类型,这可能是另一个因素。换句话说,你可以有:

  main(argc, char* argv[]);  /* see remark below... */

与其

int main(int argc, char *argv[]);

编辑:正如Aaron提醒我们的那样,最初的语法实际上应该是类似于下面这样的:

  main(argc, argv) char **argv {... } 

由于“原型”是在稍后才引入的。那大约是在每个人都至少花费了10小时追踪微妙(或不那么微妙)与类型相关的错误之后。


16
实际上,如果没有 ANSI C 引入的内联参数声明和原型,这将是 main(argc, argv) char* argv { } 的形式(啊 - 那些美好的过去...我很高兴它们已经过去了)。 - Aaron
5
@对的,艾伦!我忘了……我们最好在我们暴露出年龄太多之前停止;-) - mjv
1
@Aaron 没有见过旧式的函数定义 main(argc, argv) char **argv {... },但见过/使用过 main(argc, argv) int argc; char* argv; { ... } - chux - Reinstate Monica

33

几个原因:

  • 因为这并不重要
  • 因为C最初没有unsigned关键字或无符号整数类型
  • 因为C最初没有检查参数类型,甚至没有原型。
    结果,通常的做法是不声明int类型,因为这是默认值。
  • 因为在某种意义上,int更重要。 一切都是int。 C部分演变自一种甚至没有类型的语言。每个变量都是一个word,这就是int最初用于的目的。

更新:Jason S要求提供来源。我认为除了"这并不重要"之外,您可以从C语言开发的一篇由dmr撰写的论文中找到所有这些内容。您可能需要在通常的位置查找早期的语言BCPL和B。


12
因为C语言太老了,并且它从一开始就是这样设计的。现在改变它已经太晚了。

9
C是老语言,从一开始就是这样设计的。但我不同意最后一句话(虽然不至于让我踩反对票)。ISO很容易添加第三个main函数原型定义而影响很小。实际上,实现者也可以自由添加其他main函数原型——这在标准中是明确提到的。 - paxdiablo
3
@paxdiablo赞同,标准可以和应该经过谨慎地演变和更改。然而,在这种情况下,这个未签名的argc参数如何有助于世界和平呢? - mjv
1
@mjv 在互联网上少一些关于不完全正确的事情的抱怨意味着总体上少一些抱怨,这意味着世界和平会比以前略微更多。 - Dai

12

这里是dmr自己讲述C编程语言历史的文章。虽然没有明确说明(至少我快速浏览时没有找到),但较早版本的C不支持无符号类型。mjv关于隐式类型转换为int的观点也很相关。

编辑

Bell Labs的链接已经损坏了一段时间:这里是同一篇论文的替代链接。


1
你比我先说了。是的,我不相信B语言(C语言的前身)除了charint之外还有其他类型,并且每个函数都返回一个int - Earlz
2
B语言没有char类型,也没有int类型。它是无类型的。char == int == void*。 - user14554
链接又坏了。 - alx - recommends codidact

8
另一个原因可能是无符号类型在迭代时可能会不方便。例如,以下代码片段进行迭代:
for (size_t i = SIZE - 1; i >= 0; --i)
  ...

实际上,这是一个错误。当在最后一次迭代中i达到0时,在32位机器上它会继续增加到4294967295,循环不会终止。

因此,我个人发现使用普通的整数更方便进行迭代。使用整数时,当你从计数变为递减计数时,不必特别小心。


这是因为C++缺乏适当的for_each结构。我多次遇到了同样的问题,但如果我能使用带有适当迭代器的std容器,我总是很高兴。 - xtofl
同意,但在C语言中循环是你唯一拥有的。 - Eli Bendersky
你应该将代码改为 for (size_t i = SIZE - 1; i < SIZE; i--),然后就完成了。;) 来源:https://gustedt.wordpress.com/2013/07/15/a-praise-of-size_t-and-other-unsigned-types/ - alx - recommends codidact

7

Google C++ Style Guide建议除非你正在使用实际的位模式,否则永远不要使用unsigned int类型。他们的理由也适用于C语言。简单概括一下:

...... C的类型提升机制导致无符号类型的行为与人们的预期不同...... 不要使用无符号类型。

这可能不是C语言最初创建者的想法,但谁知道呢‽


4
我不理解谷歌声称无符号类型的行为是“出乎意料的”(在上下文中必须意味着比有符号类型更出乎意料)。当有符号和无符号类型混合使用时,其中一个将被提升为另一个的符号。因此,类型提升系统会导致所有整数类型的“行为与人们可能预期的不同”。 - Steve Jessop
同上。什么鬼?在进行简单算术运算之前,位就是位,此时无符号和有符号对于特定应用程序都是完全有效的。 - Jason S
有关 signed 的一个好处是,你总是有一种表示无效值的方法。不过,你确实需要浪费一个比特位来表示它。 - Adam Goode
-1U在大多数情况下都是一个同样有用的“无效值”。例如,没有什么东西实际上可以具有等于地址空间整个大小减1的大小。实际上,我喜欢Jason的评论。我希望C语言能够做出的一项改变是消除有符号/无符号类型,并用有符号/无符号操作符变体来替换它们。但唉... - R.. GitHub STOP HELPING ICE
那份文件毫无意义。如果你想要由专业人士编写的严肃编码准则,请阅读MISRA或CERT。 - Lundin
有些人赞扬无符号类型的循环:https://gustedt.wordpress.com/2013/07/15/a-praise-of-size_t-and-other-unsigned-types/。它们似乎更安全、更简单。 - alx - recommends codidact

5
作为解决您的警告问题的方法,您可以像这样做以抑制警告:
const unsigned int uargc = (unsigned int) argc;

4
好的,既然这个问题被标记为 c,我认为我可以使用适用于两种语言的东西。 - Greg Hewgill

4

将C程序更轻松地移植到Java中是一个有远见的设计决策,因为Java中没有无符号类型。


6
你可能不是很了解C语言程序员。如果他们有先见之明,肯定会努力让将代码移植到Java变得更加困难而不是更容易;) - Catskul
@Rob: 我不会说它更“容易”——只有对于做有符号算术的人来说更容易。对于处理无符号算术和位串,Java 真是个大麻烦。 - Jason S

3

main()的声明是在无符号类型添加到语言之前定义的 - 参见DMR关于 '原始 C' 的页面。当无符号类型添加时,更改已经太晚了。


1

我知道这看起来很奇怪:argc 不应该是负数!但从另一个角度看:如果你有 2^31 个命令行参数,那么 intunsigned int 都可以覆盖你接受的值范围,而且 int 更短。

面试谜题问题:如果 C 采用了 unsigned int argc,会使用多少个键盘?


2
C语言本可以将它们命名为int和uint,这样只多了一个字符。 - Zan Lynx
2
@Zan:我一直希望如此。 - Catskul
整数并不一定是32位的值,它们的大小是未指定的(除了最小值)。 - paxdiablo
8
很少有人知道,大多数冗长的编程语言都是由键盘公司赞助的。 - cobbal
3
我认为COBOL得到了大小写锁定键和换挡键设计师与工匠联合会的支持。 - caf
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接