6

C 标准5.1.2.2.1 Program startup)说:

程序启动时调用的函数名为 main。[...]
它应定义为返回类型为 int 且不带参数:
int main(void) { /* ... */ }

或使用两个参数[...]
int main(int argc, char *argv[]) { /* ... */ }

后来说:

argc 的值应为非负数。

  • 为什么不应该 argc被定义为一个unsigned intargc据说意思是“参数计数”?
  • 应该argc用作argv?

所以我开始怀疑 C 标准是否说明了数组索引的类型。签了吗?

6.5.2.1 数组下标

其中一个表达式的类型应为''<em>pointer to object type'',另一个表达式应为整数类型,结果的类型为''<em>type''。

它没有说明它的签名(或者我没有找到它)。使用负数数组索引 ( ) 的代码很常见,array[-1]但这不是未定义的行为吗?

  • 数组的索引应该是无符号的吗?
4

4 回答 4

6

main() 中 int 的原因是历史性的 - 早在语言标准化之前就一直如此。数组索引的要求是它在数组的范围内(或在某些情况下,超过末尾)- 其他任何内容都是未定义的,因此符号性无关紧要。

于 2010-03-14T14:13:18.673 回答
3

1)关于 main() argc 类型:恕我直言,该标准延续了一个非常古老的传统(超过 30 年!),现在......改变一切为时已晚(注意:在大多数系统上,无论是编译器还是链接器,如果“argc”被定义为“unsigned”,CPU也不会抱怨,但你不符合标准!)

2) 在大多数实现中,argv[argc] 是合法的并且计算结果为 NULL。实际上,找到参数列表末尾的另一种方法是从 0 开始迭代 argv,当 argv[i] 为 NULL 时终止。

3) 只要从 (pn) 到 p 的地址范围属于同一个内存对象,带有负数的数组/指针算术是合法的。你可以拥有的IE

char array[100];
char *p;

p = &array[50];
p += -30; /* Now p points to array[20]. */

指针算术的这种用法是合法的,因为生成的指针仍然保留在原始内存对象(“数组”)中。在大多数系统上,指针算法可用于违反此规则在内存中导航,但这不是可移植的,因为它完全依赖于系统。

于 2010-03-14T14:31:20.463 回答
3

一般来说,在 C 语言中,“最少意外原则”意味着最好使变量带符号,除非有充分的理由让它不带符号。这是因为当你混合有符号和无符号值时,类型提升规则可能会导致意想不到的结果:例如,如果argc是无符号的,那么这个简单的比较会导致令人惊讶的结果:

if (argc > -1)

-1被提升为unsigned int,因此其值被转换为UINT_MAX,几乎可以肯定大于argc)。

于 2010-03-14T23:13:31.290 回答
-2

1) Argc 是一个参数计数,但老实说,你怎么能在程序名 which 之前添加一个参数argv[0]。想象一个名为 的程序foo,你不能简单地说args1 foo args2那是没有意义的,尽管它argc是一个有符号的类型int,即没有这样的东西argv[-1]会让你得到'args1'......

2)原因 argc 不是真正的参数向量的索引(因此' argv '),因为运行时将可执行程序名称填充到第零个偏移量中,即argv[0]因此argc将关闭1。

3)数组索引,就指针操作而言,只要您在指针所在的内存块的边界内,使用数组下标作为负数是合法的,因为数组下标是指针的快捷方式,而不仅仅是,它们是可交换的,例如

字符 v[100];
字符 *p = &v[0];

你可以这样做:

p[55] = 'a';

这与

*(p + 55) = 'a';

你甚至可以这样做:

p = &v[55];

p[-10] = 'b' /* 这会将 'b' 填充到第 45 个偏移量中!*/

这与

*(p - 10) = 'b';

此外,如果您以超出边界的方式使用和操作数组 - 这是未定义的行为,并且将取决于运行时的实现如何处理它,可能是分段错误或程序崩溃.. ..

4) 在 *nix 环境中,有些会为 main 提供第三个参数char **endvp,这在 DOS/Windows 的 Microsoft 世界中也很少使用。一些 *nix 运行时实现,出于史前原因,您可以通过运行时传入环境变量。

于 2010-03-14T15:04:04.720 回答