2013-04-26 70 views
4

为什么int a[5] = {1,2,3,4,5,6}发出警告,而int a[5] = {1,2,3,4,5}; a[5] = 6;不?为什么溢出数组初始化会发出警告,但溢出赋值不会?

当我最初声明数组大小为5时,这是否是一个很好的做法?

如果我不知道数组的大小,该怎么办?我可以声明这样的int a[]吗?

+1

请注意你的意思是“a [5] = 6;'。 – djechlin 2013-04-26 17:00:21

+0

@djechlin谢谢,是的,我的意思是'a [5] = 6;'。更新。 – mushroom 2013-04-26 17:09:41

+0

任何人都可以给这个更精确的标题? – djechlin 2013-04-26 17:10:05

回答

8

int a [5] = {1,2,3,4,5,6}为什么int a [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和使用mallocrealloccalloc和类似的系统调用自己管理的内存。请做好工作并了解free - 世界将在稍后谢谢你。如果您正在寻找动态内存分配,您应该阅读指针而不是数组。

+0

有人可以修复我的英语吗?我不是母语的... – fotanus 2013-04-26 17:45:57

+0

谢谢@AndrewColeson – fotanus 2013-04-26 18:37:20

+0

您在回答“我可以像这样声明int a []?”,而是使用'malloc()',...“你应该声明为你写的。需要一个指针和'int a [];'不** **声明一个指针。 – 2013-04-26 18:46:15

6

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()

+0

感谢您的明确解释。然而,我仍然对'int a []'部分感到困惑,因为它与Neil的回答相矛盾:“你可以声明数组为'int a []',但是你声明的只是一个指针到一个数组“。 – mushroom 2013-04-26 17:01:46

+3

@jon这正是为什么你应该让自己一本好书,而不是从互联网上的陌生人的答案分段学习C.这一个晚期版本应该没问题。如果你已经阅读过,我可以为你找一本好书:http://en.wikipedia.org/wiki/The_C_Programming_Language – 2013-04-26 17:04:13

+0

“不,它调用未定义的行为(翻译:它非常糟糕)。”你能否详细说明或给我一个链接?我不知道这是一件坏事。 – fotanus 2013-04-26 17:05:56

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引用。

2

的问题:为什么

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的开发者选择投入时间和其他资源的问题。

+0

我非常喜欢你的讨论,但我不会说“理论上不可能”警告在运行时可能发生的越界访问。我只想说,“出于理论上的原因,编译器制造商必须接受错误肯定或者错误否定,或者两者兼而有之,即使他们有最好的诊断意图。”正如你已经指出''[5]'不是一个困难的情况。 – 2013-04-26 17:46:56

+0

@PascalCuoq:如果用户输入的索引值或从文件读取的索引值,我会说在理论上可能发出编译时警告,说它超出了界限。你总是可以发出一个警告,说明它可能会超出范围,但是你可能会在虚假警告中淹死。有些提及停机问题可能适用于此。 – 2013-04-26 19:05:14

+1

我并不是建议引用Halting问题。 “理论上的原因”足够模糊,说得对,而我觉得Halting问题太多了。事实上,我定期讨论这个问题,所以我将它写下来,以备将来重复使用。无论如何,这肯定不适合这个评论:http://blog.frama-c.com/index.php?post/2013/04/26/Of-compiler-warnings-discussions – 2013-04-26 21:37:33