为什么int a[5] = {1,2,3,4,5,6}
在int a[5] = {1,2,3,4,5}; a[5] = 6;
没有时会发出警告?
当我最初声明数组大小为5时,这样做是否是一个好习惯?
如果我不知道阵列的大小怎么办?我可以将其声明为int a[]
吗?
答案 0 :(得分:8)
为什么int a [5] = {1,2,3,4,5,6}在int [5] =时发出警告 {1,2,3,4,5}; a [5] = 6;不是吗?
该赋值会给出一个警告,因为您知道初始化语句中变量的大小,并且它显然违反了声明的大小。你没有行a
中a[6] = 6
的数组大小,所以编译器似乎没问题。当然,警告的级别从编译器变为编译器,对于某些编译器,您可以指定额外的警告。
例如,使用gcc,您可以使用标记-Wextra
和-Wall
来获取大量警告。接收警告是一件好事,因为编译器可以帮助您找到可能的警告而无需调试代码。当然,只有你修好它们才会好: - )
在我最初声明数组时执行此操作是一个好习惯 大小为5?
将一个整数分配给你没有声明的内存中的位置绝对不是一个好习惯 - 你不能确定这个值的写入位置,它可以覆盖另一个变量,或者更糟糕的是,部分覆盖其他一些变量或堆栈。由于这种东西在编译器和编译器之间是不同的,正如@PascalCuoq指出的那样,它被称为未定义的行为,并且是你想要不惜一切代价避免的东西。当然,因为它是未定义的,所以你的程序在这个声明之后会执行得很好,但这是一个非常糟糕的做法。
但是,初始化具有固定大小的数组没有任何问题,如果它不会改变的话。你应该避免使用幻数并使用常量,例如MAX_NUMBER_OF_PERMUTATIONS
或CURRENCIES_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
,并使用malloc
,realloc
,{{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.
答案 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开发人员选择投入时间和其他资源的问题。