为什么gcc不能一致地编译这段代码?

4
我正在为我参加的 C 编程课程的实验分配工作。我在本地的 Cygwin 目录中编写了代码,使用了 gcc 进行编译,生成的可执行文件没有任何错误,功能也与我的预期完全一致。
当我将代码复制到学校的 UNIX 服务器上并使用 gcc 进行编译时,没有出现任何错误,但运行时没有任何反应。
我尝试使用 gcc 2darray.c -Wall -pedantic,返回了以下内容:
2darray.c: In function 'main':
2darray.c:5:3: warning: missing braces around initializer [-Wmissing-braces]
2darray.c:5:3: warning: (near initialization for 'M[0]') [-Wmissing-braces]
2darray.c:5:24: warning: C++ style comments are not allowed in ISO C90 [enabled by default]
2darray.c:5:24: warning: (this will be reported only once per input file) [enabled by default]

错误信息提到了初始化数组M的问题,但我没有看出我初始化它的方式有任何问题。这是我正在尝试编译的代码:
#include <stdio.h>

int main(void)
{
  int M[10][10] = {0}; // creating a 10x10 array and initializing it to 0
  int i, j; // loop variables
  int sum[10] = {0}; // creating an array to hold the sums of each column of 2d array M

  for (i = 1; i < 10; i++) // assigning values to array M as specified in directions
    {
      for (j = i - 1; j < i; j++)
        {
          M[i][j] = -i;
          M[i][j+1] = i;
          M[i][j+2] = -i;
        }
    }

  for (i = 0; i < 10; i++) // printing array M
    {
      for(j = 0; j < 10; j++)
        {
          printf("%3d", M[i][j]);
        }
      printf("\n");
    }

  printf("\n");
  for (i = 0; i < 10; i++) // calculating sum of each column
    {
      for (j = 0; j < 10; j++)
        {
          sum[i] = M[j][i] + sum[i];
        }
      printf("%3d", sum[i]);  // printing array sum
    }

  return 0;
}

我尝试在变量声明和第一个for循环之间插入了一个printf语句,该语句已经输出,因此可能是我的循环出了问题?

如果相关,请参考以下是来自我的Cygwin目录的输出以及在我学校的UNIX目录中应该是什么样子:

  0  0  0  0  0  0  0  0  0  0
 -1  1 -1  0  0  0  0  0  0  0
  0 -2  2 -2  0  0  0  0  0  0
  0  0 -3  3 -3  0  0  0  0  0
  0  0  0 -4  4 -4  0  0  0  0
  0  0  0  0 -5  5 -5  0  0  0
  0  0  0  0  0 -6  6 -6  0  0
  0  0  0  0  0  0 -7  7 -7  0
  0  0  0  0  0  0  0 -8  8 -8
  0  0  0  0  0  0  0  0 -9  9

 -1 -1 -2 -3 -4 -5 -6 -7 -8  1

7
尝试将 int M[10][10] = {0}; 改为 int M[10][10] = {{0}}; - user1969104
4
我会尽力修复警告,但我还在努力理解您的问题。您可以更好地解释一下“可执行文件无法工作”的含义。 - user1969104
4
请使用选项“-std=c99”。 - BLUEPIXY
5
“doesn't work” 不是一个问题描述。 - melpomene
5
i : 9 时,执行 M[i][j+2] = -i; 会导致越界。该语句意为将 -i 赋值给二维数组 M 中第 i 行、第 j+2 列的元素。 - BLUEPIXY
显示剩余17条评论
2个回答

7

您正在超出数组 M 的边界访问一行,这会导致未定义的行为。

for (i = 1; i < 10; i++) 
// i ranges from 1 to 9
  {
    for (j = i - 1; j < i; j++)
    // j ranges from i-1 (0 when i==1) to i-1 (8 when i==9)
    // Consider what happens when j==8
      {
        M[i][j] = -i;       // j == 8
        M[i][j+1] = i;      // j == 9
        M[i][j+2] = -i;     // j == 10, out of bounds
      }
  }

当我查看您的代码时,j+2索引似乎是最可能发生越界访问的地方。我复制了您的程序并添加了一行:

      M[i][j] = -i;
      M[i][j+1] = i;
      if (j+2 >= 10) puts("OUT OF RANGE");
      M[i][j+2] = -i;

当我运行程序时,它打印了"OUT OF RANGE"。
至于你从 "gcc -Wall -pedantic" 得到的警告,它们并不是真正的问题。关于 "//" 注释的警告可以通过使用 "-std=c99" 编译来避免。"缺少大括号" 的警告是虚假的。对于嵌套数据结构的初始化来说,嵌套的大括号是可选的,"{0}" 是将整个数据结构(数组、结构体、联合体)初始化为零的完全有效的惯用语。最近的gcc版本(特别是5.2.0)不会警告这个问题。

好的,但问题出在更早一步。M的第二个索引可以高达9,所以当j为9时,带有j+1的那一行就会有问题。 - donjuedo
不是真的,我认为9在范围内,不是吗? - Ely
1
@donjuedo 因为 j < ii < 10,所以 j 的最大值为 8。 - user3386109
谢谢你的帮助,Keith。我在我的第一个嵌套的for循环中,在第三个表达式之前添加了一个 if (j+2 > 9) break; 语句,然后它就起作用了。 - suhh_drude
@user10721:添加break可以避免未定义的行为;我不知道这是否是最佳解决方案。这取决于程序应该做什么,而您还没有告诉我们要求。 - Keith Thompson
@MarcGlisse:在gcc 4.8.4中不存在该选项。它存在于5.2.0中,但生成的代码取决于libubsan.so.0,而我没有这个库。 - Keith Thompson

2

最近版本的clang和gcc都带有一个工具,可以轻松解决这样的问题。请始终使用它,它将为您节省大量查找错误的时间。

$ gcc s.c -Wall -Wextra -fsanitize=undefined
$ ./a.out
s.c:15:15: runtime error: index 10 out of bounds for type 'int [10]'
s.c:15:21: runtime error: store to address 0x7fff6c53f2c0 with insufficient space for an object of type 'int'
0x7fff6c53f2c0: note: pointer points here
 09 00 00 00  e5 ef 74 8a 11 7f 00 00  08 00 00 00 09 00 00 00  b0 10 40 00 00 00 00 00  00 00 00 00
              ^

显然,cygwin可能还不支持libubsan,所以您可能需要使用-fsanitize-undefined-trap-on-error,它将把漂亮的错误消息替换为简单的陷阱,您可以使用gdb进行调查。是的,调试器是另一个工具,需要一点时间学习,但不需要太长时间,而且它会节省您寻找错误的更多时间。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接