试图理解C中的以下代码有什么问题

我有一个任务,找到这个代码错误的原因。

#include  #include  #define fail(a) ((test == 0 || test == a) ? fail##a() : 0) #define N (10) int a[N] = { 1 }; int* b = &a[0]; void fail1() { printf("a[0] = %d\n", a[0]); printf("b[0] = %d\n", b[0]); printf("*b = %d\n", *b); *b = 2; a[N] = 3; printf("*b = %d\n", *b); } ... int main(int argc, char **argv) { int test = 0; if (argc > 1) { sscanf(argv[1], "%d", &test); printf("doing test %d\n", test); } else puts("doing all tests"); fail(1); fail(2); fail(3); fail(4); fail(5); fail(6); puts("lab6 reached the end"); exit(0); } 

通过使用valgrind,它告诉我printf("*b = %d\n", *b);中有一个失败printf("*b = %d\n", *b); 。 我注意到通过评论a[N] = 3; valvgrind没有错误。 但我不明白为什么。 我知道这与内存有关,我知道a[N]要求数组之外的元素。

C数组具有基于0的索引。 对于定义a[N]的数组,最大有效元素将是a[N-1]

a[N]指向超出记忆的范围。 尝试访问超出范围的内存,调用未定义的行为 。

现在,虽然您可能知道上述事实,但您可能忽略了UB的影响。 一旦你击中UB,没有任何保证。 简单的解决方案,不要编写可以调用UB的代码。

数组索引从0开始,因此a的最后一个索引是N-1而不是N 通过写入a[N]您访问的内存不是数组的一部分,从而导致未定义的行为 。

在C.中没有数组边界检查.a [N]不在数组的末尾,这是[0] .. [N-1]

如果你在数组边界外读或写,你会得到一个随机结果(这些天人们称之为UB“未定义的行为”)。 这可能是

x = a[outside];

  • 代码崩溃了
  • 你得到一个随机值
  • 你得到了你期望的价值

a[outside] = x;

  • 代码崩溃了
  • 写入发生,但在读取它时,值已发生变化。
  • 写入发生,并将其读回即可(即,似乎没有任何事情发生)
  • 写入发生,但您在代码的其他部分崩溃。

例如,假设[]是char数组。

  a[0] | a[1] | ... | a[N-1] | s[0] | s[1] | s[2] | s[3] | s[4] | s[5] 1 | 0 | | 7 | 'H' | 'e' | 'l' | 'l' | '0' | 0 

写入[N]不会更改数组,而是覆盖不同的变量(在本例中为字符串的一部分)。 你破坏了程序的内存,导致混乱。

  a[N] = 255; /*bug*/ a[0] | a[1] | ... | a[N-1] | s[0] | s[1] | s[2] | s[3] | s[4] | s[5] 1 | 0 | | 7 | 255 | 'e' | 'l' | 'l' | '0' | 0 

在这个例子中,读取和写入[N]似乎可以工作,但它损坏了另一个字符串变量(曾经说过“Hello”但现在没有)。 这可能会导致以后的错误,或者多年后也许没有人注意到!

在C语言编程时,您需要非常小心和严谨。