为什么int a[5] = {1,2,3,4,5,6}
发出警告,而int a[5] = {1,2,3,4,5}; a[5] = 6;
不?为什么溢出数组初始化会发出警告,但溢出赋值不会?
当我最初声明数组大小为5时,这是否是一个很好的做法?
如果我不知道数组的大小,该怎么办?我可以声明这样的int a[]
吗?
为什么int a[5] = {1,2,3,4,5,6}
发出警告,而int a[5] = {1,2,3,4,5}; a[5] = 6;
不?为什么溢出数组初始化会发出警告,但溢出赋值不会?
当我最初声明数组大小为5时,这是否是一个很好的做法?
如果我不知道数组的大小,该怎么办?我可以声明这样的int a[]
吗?
int a [5] = {1,2,3,4,5,6}为什么int a [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
,calloc
和类似的系统调用自己管理的内存。请做好工作并了解free
- 世界将在稍后谢谢你。如果您正在寻找动态内存分配,您应该阅读指针而不是数组。
int a [5] = {1,2,3,4,5,6}为什么int a [5] = {1,2,3,4,5}; a [6] = 6;才不是?
警告只是编译器试图帮助你。每次你做错了事,编译器都不必警告。编写不正确的程序的方法太多,编译器无法警告所有的程序。
当我最初宣布数组大小为5时,这样做是否是一种好的做法?
No.当a
是大小5的阵列,访问a[6]
或a[5]
调用未定义的行为(译注:它是非常差)。关于未定义行为的传统说法是,它允许编译器使守护进程飞出你的nose:
在1992年初在该组的讨论,经常说 “当编译器遇到[给定未定义构造]它是合法的,它使恶魔飞出你的鼻子“(暗示 ,编译器可以选择任何任意离奇的方式来解释代码而不违反ANSI C标准)。
因此,总之,总是avoid未定义的行为在你的C程序中。
如果我不知道阵列的大小怎么办?我可以像这样int a []声明它吗?
不,你不能。在声明数组时,您必须知道数组的大小。 int a[];
会声明一个不完整的数组,这不是你想要的(here是一个关于不完整类型的链接,但是如果你想要访问五元素数组的第六个元素,你只是不想听到不完整的类型然而)。如果您不知道最终需要的尺寸,请了解malloc()
和realloc()
。
感谢您的明确解释。然而,我仍然对'int a []'部分感到困惑,因为它与Neil的回答相矛盾:“你可以声明数组为'int a []',但是你声明的只是一个指针到一个数组“。 – mushroom 2013-04-26 17:01:46
@jon这正是为什么你应该让自己一本好书,而不是从互联网上的陌生人的答案分段学习C.这一个晚期版本应该没问题。如果你已经阅读过,我可以为你找一本好书:http://en.wikipedia.org/wiki/The_C_Programming_Language – 2013-04-26 17:04:13
“不,它调用未定义的行为(翻译:它非常糟糕)。”你能否详细说明或给我一个链接?我不知道这是一件坏事。 – fotanus 2013-04-26 17:05:56
的问题:为什么
int a[5] = {1,2,3,4,5,6};
给予警告,同时
int a[5] = {1,2,3,4,5};
a[5] = 6;
不?
这实际上是一个很好的问题,我没有一个很好的答案。
至于C语言标准说什么,初始化是约束违反,这意味着符合标准的编译必须问题进行诊断,并可以拒绝该方案。 (海湾合作委员会这样做,-pedantic-errors
选项,我建议使用)。
在第二种情况下,a[5] = 6;
有未定义的行为。该语言不需要诊断,但它肯定允许一个,并且足够聪明的编译器可能警告它。当编译器看到a[5]
的赋值时,它知道(或可能知道)a
只有5个元素,并且您试图访问第6个元素。海湾合作委员会,至少不这样做,至少不是我试过的选项。
为什么标准要求诊断第一个病例但不是第二个病例?因为在编译时总能检测到第一个错误。编译器有知道a
有多大,因为声明正在创建它;它必须为它分配内存。在这样的声明中,编译时总是知道大小。 (有可变长度数组,但不能使用这种初始化工具。)
在a[5] = 6;
中,编译器将不得不执行更多的分析来检测错误,这并非总是可能。你可以将其轻松地写:
a[n] = 6;
这将是一样糟糕,如果n
恰好是5
- 但是编译器必须确定在编译时是n
将不得不的价值运行时间来诊断它。编译器有时可以进行这种分析,但有些情况下理论上不可能。
那么为什么不gcc诊断a[5] = 6;
的特殊情况?这可能只是gcc的开发者选择投入时间和其他资源的问题。
我非常喜欢你的讨论,但我不会说“理论上不可能”警告在运行时可能发生的越界访问。我只想说,“出于理论上的原因,编译器制造商必须接受错误肯定或者错误否定,或者两者兼而有之,即使他们有最好的诊断意图。”正如你已经指出''[5]'不是一个困难的情况。 – 2013-04-26 17:46:56
@PascalCuoq:如果用户输入的索引值或从文件读取的索引值,我会说在理论上可能发出编译时警告,说它超出了界限。你总是可以发出一个警告,说明它可能会超出范围,但是你可能会在虚假警告中淹死。有些提及停机问题可能适用于此。 – 2013-04-26 19:05:14
我并不是建议引用Halting问题。 “理论上的原因”足够模糊,说得对,而我觉得Halting问题太多了。事实上,我定期讨论这个问题,所以我将它写下来,以备将来重复使用。无论如何,这肯定不适合这个评论:http://blog.frama-c.com/index.php?post/2013/04/26/Of-compiler-warnings-discussions – 2013-04-26 21:37:33
请注意你的意思是“a [5] = 6;'。 – djechlin 2013-04-26 17:00:21
@djechlin谢谢,是的,我的意思是'a [5] = 6;'。更新。 – mushroom 2013-04-26 17:09:41
任何人都可以给这个更精确的标题? – djechlin 2013-04-26 17:10:05