为什么溢出数组初始化会发出警告但溢出的赋值不会?

时间:2013-04-26 16:55:09

标签: c arrays

为什么int a[5] = {1,2,3,4,5,6}int a[5] = {1,2,3,4,5}; a[5] = 6;没有时会发出警告?

当我最初声明数组大小为5时,这样做是否是一个好习惯?

如果我不知道阵列的大小怎么办?我可以将其声明为int a[]吗?

4 个答案:

答案 0 :(得分:8)

  

为什么int a [5] = {1,2,3,4,5,6}在int [5] =时发出警告   {1,2,3,4,5}; a [5] = 6;不是吗?

该赋值会给出一个警告,因为您知道初始化语句中变量的大小,并且它显然违反了声明的大小。你没有行aa[6] = 6的数组大小,所以编译器似乎没问题。当然,警告的级别从编译器变为编译器,对于某些编译器,您可以指定额外的警告。

例如,使用gcc,您可以使用标记-Wextra-Wall来获取大量警告。接收警告是一件好事,因为编译器可以帮助您找到可能的警告而无需调试代码。当然,只有你修好它们才会好: - )

  

在我最初声明数组时执行此操作是一个好习惯   大小为5?

将一个整数分配给你没有声明的内存中的位置绝对不是一个好习惯 - 你不能确定这个值的写入位置,它可以覆盖另一个变量,或者更糟糕的是,部分覆盖其他一些变量或堆栈。由于这种东西在编译器和编译器之间是不同的,正如@PascalCuoq指出的那样,它被称为未定义的行为,并且是你想要不惜一切代价避免的东西。当然,因为它是未定义的,所以你的程序在这个声明之后会执行得很好,但这是一个非常糟糕的做法。

但是,初始化具有固定大小的数组没有任何问题,如果它不会改变的话。你应该避免使用幻数并使用常量,例如MAX_NUMBER_OF_PERMUTATIONSCURRENCIES_SIZE

  

我可以这样声明:int a []?

在初始化固定数组时,将其声明为int a[]是一种简写,编译器可以指定元素的数量。例如:

int a[] = {1,2,3}; //this is good
int b[3] = {1,2,3}; //same from above

过去通常声明int a[];但是它不适用于每个编译器,因此应该避免。 (感谢@PascalCuoq指出这一点)

  

如果我不知道阵列的大小怎么办?

如果您不知道数组的大小,则应将其声明为指针,例如int * a,并使用mallocrealloc,{{1}自行管理内存和类似的系统调用。请做好工作并了解calloc - 世界将在以后感谢你。如果您正在寻找动态内存分配,您应该阅读指针而不是数组。

答案 1 :(得分:6)

  

为什么int a [5] = {1,2,3,4,5,6}在int [5] =时发出警告   {1,2,3,4,5}; a [6] = 6;不是吗?

警告只是编译器试图帮助您。每次做错事时,编译器都不必发出警告。编写错误程序的方法太多了,编译器无法对所有程序发出警告。

  

当我最初声明数组大小为5时,这样做是否是一个好习惯?

没有。当a是大小为5的数组时,访问a[6]a[5]会调用未定义的行为(翻译:非常糟糕)。关于未定义行为的传统说法是它允许编译器使守护进程飞出nose

  

在1992年初讨论该小组时,经常评论   “当编译器遇到[给定的未定义构造]时,它就是   合法的让它让你的鼻子飞出你的鼻子“(暗示是   编译器可以选择任意奇怪的解释方式   代码不违反ANSI C标准)。

因此,简而言之,C程序中始终存在avoid未定义的行为。

  

如果我不知道阵列的大小怎么办?我可以像这样声明一个[]吗?

不,你不能。在声明数组时,您必须知道数组的大小。 int a[];会声明一个不完整的数组,这不是你想要的(here是一个关于不完整类型的链接,但如果你问的是访问五元素数组的第六个元素,你只需要我还想听听不完整的类型。如果您不知道最终需要的尺寸,请了解malloc()realloc()

答案 2 :(得分:2)

在第二个示例中,编译器正在执行指针数学运算以确定放置整数的位置。在较旧的C实现中,数组不是第一类,因此没有可用的范围检查。一些较新的可以检测到越界错误。

如果需要动态分配数组,这是正确的模式:

int *a;
// calloc( elements, size)
// create an array of 6 elements, 0..5
a = calloc(6, sizeof(int));
// use like a[5]=6, be sure to free(a) later. 

检查callocfree引用。

答案 3 :(得分:2)

问题:为什么

int a[5] = {1,2,3,4,5,6};

时发出警告
 int a[5] = {1,2,3,4,5};
 a[5] = 6;

没有?

这实际上是一个非常好的问题,我对它没有很好的答案。

至于C语言标准所说的,初始化是约束违规,这意味着符合标准的编译器必须发出诊断,而可以< / em>拒绝该计划。 (gcc使用-pedantic-errors选项,我推荐使用它。)

在第二种情况下,a[5] = 6;具有未定义的行为。该语言不需要诊断,但它肯定允许一个,并且一个足够聪明的编译器可以警告它。当编译器看到a[5]的赋值时,它知道(或者可能知道)a只有5个元素,并且您正在尝试访问第6个元素。 gcc,至少不这样做,至少不是我试过的选项。

为什么标准要求对第一种情况进行诊断而不对第二种情况进行诊断?因为始终可以在编译时检测到第一个错误。编译器知道a有多大,因为声明正在创建它;它必须为它分配内存。在这样的声明中,大小总是在编译时知道。 (有可变长度的数组,但你不能使用那种初始化器。)

a[5] = 6;中,编译器必须执行更多分析以检测错误,这种分析并非总是可行。你可以很容易地写出:

a[n] = 6;

如果n恰好是5,那将同样糟糕 - 但编译器必须在编译时确定 n的值将在运行时 进行诊断。编译器有时可以执行这种分析,但有些情况下理论上是不可能的。

那么为什么gcc不诊断a[5] = 6;的特定情况呢?这可能只是gcc开发人员选择投入时间和其他资源的问题。