0

总而言之,我在棋盘程序中收到了一些有趣的错误。我运行 Valgrind~$ valgrind --track-origins=yes ./a.out并得到了一些我难以解释的错误报告。

==10171== Conditional jump or move depends on uninitialised value(s)
==10171==    at 0x80488C2: diagnols.2353 (eq1.c:95)
==10171==    by 0x8048942: check.2372 (eq1.c:109)
==10171==    by 0x80485E6: recur (eq1.c:118)
==10171==    by 0x804872B: recur (eq1.c:151)
==10171==    by 0x804868C: recur (eq1.c:141)
==10171==    by 0x804868C: recur (eq1.c:141)
==10171==    by 0x804868C: recur (eq1.c:141)
==10171==    by 0x804868C: recur (eq1.c:141)
==10171==    by 0x804868C: recur (eq1.c:141)
==10171==    by 0x804868C: recur (eq1.c:141)
==10171==    by 0x804868C: recur (eq1.c:141)
==10171==    by 0x8048618: recur (eq1.c:123)
==10171==  Uninitialised value was created by a heap allocation
==10171==    at 0x402BB7A: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==10171==    by 0x804897A: main (eq1.c:159)

以下是代码中的一些实例:

  • 重复,第 123 行:

    if ( (result == 2)/*&&(width!=7)*/  ) {
      // go to next row, try first column
      puts("Option 1\n");
      recur(key, (depth + 1), 0);  // line 123
    } 
    
  • 重复,第 141 行:

    else if (  (result != 2)&&(width<7)  ) { // didn't work, try next column
      // go to next column, same row
      puts("Option 3\n");
      key->board[depth][width] = 0;
      recur(key, depth, (width+1));  // line 141
    
  • 诊断,第 95 行:

    while (  ((abc>=0)&&(bcd>=0) && (abc<=7)&&(bcd<=7))  ) {
      if (key->board[abc][bcd] == 1) {
    counter++;
      }
    

完整的诊断功能:

  int diagnols(int PARAMETER_ONE, int PARAMETER_TWO) { // returns 0 if good
    int abc = 0;
    int bcd = 0;
    int counter = 0;

    // first check diagnol up and to the left
    abc = PARAMETER_ONE-1;
    bcd = PARAMETER_TWO-1;
    while ( bcd>=0 && abc>=0 && abc<=7 && bcd<=7  ) {
      if (key->board[abc][bcd] == 1) {
        counter++;
      } abc--;
      bcd--;
    }

    // checking diagnol up and to the right
    abc = PARAMETER_ONE-1;
    bcd = PARAMETER_TWO+1;
    while ( abc>=0 && bcd>=0  && abc<=7 && bcd<=7  ) {
      if (key->board[abc][bcd] == 1) {
        counter++;
      } abc--;
      bcd++;
    }

    // checkign diagnol down and to the left
    abc = PARAMETER_ONE+1;
    bcd = PARAMETER_TWO-1;
    while ( abc>=0 && bcd>=0 && abc<=7 && bcd<=7  ) {
      if (key->board[abc][bcd] == 1) {
        counter++;
      } abc++;
      bcd--;
    }

    // checking diagnol down and to the right
    abc = PARAMETER_ONE+1;
    bcd = PARAMETER_TWO+1;
    while ( abc>=0 && bcd>=0 && abc<=7 && bcd<=7  ) {
      if (key->board[abc][bcd] == 1) {
        counter++;
      } abc++;
      bcd++;
    }

    return counter;
  }

起源追踪:

int main() {
  struct chessboard* master = malloc(sizeof(struct chessboard));

 /* zero out board */
 int one = 0;
 int two = 0;
 for (one; one <= 7; one++) {
   for (two; two <= 7; two++) {
     master->board[one][two] = 0;
   }
 }

结构是:

struct chessboard {
  int board[8][8];  // corrected
};

第二组错误

==10171== Invalid read of size 4
==10171==    at 0x8048767: vertical.2344 (eq1.c:51)
==10171==    by 0x804892B: check.2372 (eq1.c:108)
==10171==    by 0x80485E6: recur (eq1.c:118)
==10171==    by 0x804872B: recur (eq1.c:151)
==10171==    by 0x804868C: recur (eq1.c:141)
==10171==    by 0x804868C: recur (eq1.c:141)
==10171==    by 0x804868C: recur (eq1.c:141)
==10171==    by 0x804868C: recur (eq1.c:141)
==10171==    by 0x804868C: recur (eq1.c:141)
==10171==    by 0x804868C: recur (eq1.c:141)
==10171==    by 0x804868C: recur (eq1.c:141)
==10171==    by 0x8048618: recur (eq1.c:123)
==10171==  Address 0x41f8128 is 0 bytes after a block of size 256 alloc'd
==10171==    at 0x402BB7A: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==10171==    by 0x804897A: main (eq1.c:159)

显然,出现了很多相同的行。

  • 垂直功能:

    int vertical(int PARAMETER) { // param = 2nd val in 2D || returns 1 if good
      int ab = 0;
      int counter = 0;
      for (ab; ab <= 7; ab++) {
        if (  (key->board[ab][PARAMETER] == 1)  ) {
        counter++;
        }
      } return counter; // if one, it is okay; if > 1, not okay
    }
    

我不确定问题是什么。它似乎不喜欢我的malloc,但我立即初始化它。我试过改变数组大小,改变循环边界,calloc()而不是malloc()等等。任何帮助表示赞赏!


整个程序:

#include <stdio.h>
#include <stdlib.h>

struct chessboard {
  int board[8][8];
};

void print_board(struct chessboard* key) {
  for(int a = 0; a <= 7; a++) {
    for(int b = 0; b <= 7; b++) {
      if (key->board[a][b]==0) {
    printf("[x]");
      } else {
    printf(" q ");
      }
    } 
    printf("\n");
  } 
  puts("\n");
}

int recur(struct chessboard* key, int DEPTH, int WIDTH) {

  int depth = DEPTH;
  int width = WIDTH;

  /* set a piece as a queen */
  key->board[depth][width] = 1;

  /* check for conflicts */
  int horizontal(int column) { // param = first val in 2D || returns 1 if good
    int a = 0;
    int counter = 0;
    for(;a <= 7; a++) {
      if (  key->board[column][a] == 1  ) {
    counter++;
      }
    }
    return counter; // if one, it it is okay; if > 1, not okay
  }

  int vertical(int row) { // param = 2nd val in 2D || returns 1 if good
    int ab = 0;
    int counter = 0;
    for(;ab <= 7; ab++) {
      if (  (key->board[ab][row] == 1)  ) {
    counter++;
      }
    } 
    return counter; // if one, it is okay; if > 1, not okay
  }

  int diagnols(int column, int row) { // returns 0 if good

    int counter = 0;

    // first check diagnol up and to the left
    int abc = column-1;
    int bcd = row-1;
    while (  bcd>=0 && abc>=0 && abc<=7 && bcd<=7  ) {
      if (key->board[abc][bcd] == 1) {
    counter++;
      } abc--;
      bcd--;
    }

    // checking diagnol up and to the right
    abc = column-1;
    bcd = row+1;
    while (  abc>=0 && bcd>=0 && abc<=7 && bcd<=7  ) {
      if (key->board[abc][bcd] == 1) {
    counter++;
      } 
      abc--;
      bcd++;
    }

    // checkign diagnol down and to the left
    abc = column+1;
    bcd = row-1;
    while (  (abc>=0)&&(bcd>=0) && (abc<=7)&&(bcd<=7)  ) {
      if (key->board[abc][bcd] == 1) {
    counter++;
      } 
      abc++;
      bcd--;
    }

    // checking diagnol down and to the right
    abc = column+1;
    bcd = row+1;
    while (  abc>=0 && bcd>=0 && abc<=7 && bcd<=7  ) {
      if (key->board[abc][bcd] == 1) {
    counter++;
      } 
      abc++;
      bcd++;
    }

    return counter;
  }
  int check(int param1, int param2) { // if okay returns 2 
    int h = horizontal(param1);
    int v = vertical(param2);
    int d = diagnols(param1, param2);
    int total = 0;
    total = (h + v + d); // if okay, equals 2
    return total;
  }
  print_board(key); // shows process, can be annoying


  /* choose what to do next 
  * option puts() statements are there for debugging
  */
  int result = check(depth,width);

  if ( result == 2 && depth != 7 ) { // worked, go deeper
    // go to next row, try first column
    puts("Option 1\n");
    recur(key, (depth + 1), 0);
  } 

  else if (  result == 2 && depth==7  ) { // done
    puts("Option 2\n");
    return 1;
  } 

  else if (  result != 2 && width<7  ) { // didn't work, try next column
    // go to next column, same row
    puts("Option 3\n");
    key->board[depth][width] = 0;
    recur(key, depth, (width+1));
  } 

  else if (  result !=2 && width == 7  ) { // didn't work AND no more column space
    puts("Option 1\n");
    key->board[depth][width] = 0;  // set this queen to zero
    for (int e = 0;e<=7;e++){ // find queen in previous row, set it to zero and recur
    if (key->board[(depth-1)][e] == 1) {
        key->board[(depth-1)][e] = 0;
        recur(key, (depth-1), (e+1)); // could go out of bounds
    }
    }
  }
  return 0;
}

int main() {
  struct chessboard* master = malloc(sizeof(struct chessboard));

  /* zero out board */
  for(int one = 0; one <= 7; one++) {
    for(int two = 0; two <= 7; two++) {
      master->board[one][two] = 0;
    }
  }

  /* run */
  int result = 0;
  result = recur(master, 0, 0);

  /* finish */
  printf("Result was: %i\n", result);
  free(master);
  return 0;

}
4

2 回答 2

1

发生第一个错误是因为您malloc编辑了一些东西(大概是 achessboard似乎被称为key)并且在尝试读取它们之前未能设置一些结构的内部成员。也就是说,您尝试阅读key->board[abc][bcd]而不先分配到该位置。

您还没有为其他错误发布适当的代码。另请注意,发布所有代码很有帮助,因为程序的行很少存在于真空中。

于 2013-04-03T02:25:50.103 回答
1

问题的原始版本

出于某种原因,您使用的是 7x7 棋盘而不是普通的 8x8 棋盘:

struct chessboard {
  int board[7][7];
};

此结构可以正确索引,每个维度中的索引值为 0..6。您可以摆脱(但它只是“摆脱”)一些(并且只有“一些”)索引为 7 的访问,但它们严格调用未定义的行为,这意味着“任何事情都可能发生”,包括“它几乎按预期工作'。

在第 95 行的代码中diagnols(),您有:

while (((abc >= 0) && (bcd >= 0) && (abc <= 7) && (bcd <= 7)))
{
    if (key->board[abc][bcd] == 1)
        counter++;
    ...
}

现在,如果任一索引等于 7,您可能正在访问越界内存。


修改后的问题

好吧,既然你的电路板是 8x8,而不是 7x7,那就更有意义了。8 * 8 * sizeof(int)为棋盘分配 256 字节 ( ) 的内存也很有意义。

当您有数组时,编写数组边界的标准惯用数组不是:

/* zero out board */
int one = 0;
int two = 0;
for (one; one <= 7; one++) {
  for (two; two <= 7; two++) {
    master->board[one][two] = 0;
  }
}

for (int i = 0; i < 8; i++)
{
    for (int j = 0; j < 8; j++)
        master->board[i][j] = 0;
}

惯用的一点是使用for (int index = 0; index < bound; index++),而不是选择变量名(尽管iandjoneand更传统two),或者大括号的位置或数量(两者都是完全主观的)。并且预先声明索引然后使用没有问题for (index = 0; index < bound; index++),特别是如果您的编译器不支持 C99。

第 1 组问题发生在:

while (  ((abc>=0)&&(bcd>=0) && (abc<=7)&&(bcd<=7))  ) {
  if (key->board[abc][bcd] == 1) {
    counter++;
  }

这可能意味着其中一个abc或未bcd正确初始化,因此您正在根据未初始化的变量进行条件计算。另一种方法key->board[abc][bcd]是没有正确初始化,这意味着初始化循环是错误的。

此外,循环中的条件while应与循环中的条件类似for

while (abc >= 0 && bcd >= 0 && abc < 8 and bcd < 8)

这些不对称的界限(大于或等于下限——通常为零——并且小于上限)通常被认为是更好的风格。除此之外,您可以在任何地方使用enum { SIDE = 8 };然后引用SIZE而不是 7(或 8)。

对于第 2 组错误,您没有向我们展示函数中的代码,vertical因此我们无法判断您做错了什么,除非您的数组索引看起来太大。


批评继续

现在有一些额外的代码需要审查......

int vertical(int PARAMETER) { // param = 2nd val in 2D || returns 1 if good
  int ab = 0;
  int counter = 0;
  for (ab; ab <= 7; ab++) {
    if (  (key->board[ab][PARAMETER] == 1)  ) {
    counter++;
    }
  } return counter; // if one, it is okay; if > 1, not okay
}

通常,C 编码器使用 ALL_CAPS_NAMES 来表示宏值#define SIZE 8enum常量enum { SIZE = 8 };​​。如果其他人要阅读您的代码,他们会期望的,因此我建议遵循该样式指南。另外,选择更有意义的名字;int vertical(int column)会更明智,不是吗?或者int vertical(int col)由于另一个维度是row,然后您有两个 3 字母名称。您是否考虑过使用assertfrom <assert.h>

int vertical(int column)
{
    int counter = 0;
    assert(column >= 0 && column < 8);
    for (int row = 0; row < 8; row++)
    {
        if (key->board[row][column] == 1)
           counter++;
    }
    return counter;
}

如果板上只有 0 和 1 的值,你甚至不需要条件;你可以简单地写:

int vertical(int column)
{
    int counter = 0;
    assert(column >= 0 && column < 8);
    for (int row = 0; row < 8; row++)
        counter += key->board[row][column];
    return counter;
}

目前,我能想到的发生越界内存访问的唯一方法vertical()——任何版本——是因为column参数是 8。断言将诊断这是否是问题所在。

我猜这个函数diagnols()是确定同一对角线上是否有两个皇后?你看过哈利波特系列电影吗?记住对角巷——对角线。

您显示的代码是:

  int diagnols(int PARAMETER_ONE, int PARAMETER_TWO) { // returns 0 if good
    int abc = 0;
    int bcd = 0;
    int counter = 0;

我认为参数名称应该是rowand col; 您正在从给定位置[row][col]上下搜索对角线。

    // first check diagnol up and to the left
    abc = PARAMETER_ONE-1;
    bcd = PARAMETER_TWO-1;

考虑到这里的分配,上面的abc和到 0的初始化是多余的。bcd您可能应该断言参数都在 0..7 范围内。

    while ( bcd>=0 && abc>=0 && abc<=7 && bcd<=7  ) {
      if (key->board[abc][bcd] == 1) {
        counter++;
      } abc--;
      bcd--;
    }

请不要将右大括号和另一个语句放在同一行。当然,关于不对称范围等的其他评论仍然适用,但我不会重复它们。还有关于 0、1 和简单求和的观察。

    // checking diagnol up and to the right
    abc = PARAMETER_ONE-1;
    bcd = PARAMETER_TWO+1;
    while ( abc>=0 && bcd>=0  && abc<=7 && bcd<=7  ) {
      if (key->board[abc][bcd] == 1) {
        counter++;
      } abc--;
      bcd++;
    }

    // checkign diagnol down and to the left
    abc = PARAMETER_ONE+1;
    bcd = PARAMETER_TWO-1;
    while ( abc>=0 && bcd>=0 && abc<=7 && bcd<=7  ) {
      if (key->board[abc][bcd] == 1) {
        counter++;
      } abc++;
      bcd--;
    }

    // checking diagnol down and to the right
    abc = PARAMETER_ONE+1;
    bcd = PARAMETER_TWO+1;
    while ( abc>=0 && bcd>=0 && abc<=7 && bcd<=7  ) {
      if (key->board[abc][bcd] == 1) {
        counter++;
      } abc++;
      bcd++;
    }

    return counter;
  }

这四个代码块非常相似。事实上,我认为您可以创建一个简单的函数来进行通用搜索。

int count_collisions(int row, int col, int row_inc, int col_inc)
{
    int count = 0;
    assert(row >= 0 && row < 8);
    assert(col >= 0 && col < 8);
    assert(row_inc >= -1 && row_inc <= +1);  // Not good for jumps in draughts/checkers
    assert(col_inc >= -1 && row_inc <= +1);
    assert(col_inc !=  0 || row_inc !=  0);  // Both zero is gives an infinite loop
    while (row >= 0 && row < 8 && col >= 0 && col < 8)
    {
        count += key->board[row][col];
        col += col_inc;
        row += row_inc;
    }
    return count;
}

int diagonals(int row, int col)
{
    int num = 0;
    num += count_collisions(row, col, +1, +1);
    num += count_collisions(row, col, +1, -1);
    num += count_collisions(row, col, -1, +1);
    num += count_collisions(row, col, -1, -1);
    return num;
}

int vertical(int col)
{
    return count_collisions(0, col, +1, 0);
}

int horizontal(int row)
{
    return count_collisions(row, 0, 0, +1);
}

Kernighan 和 Plauger 的优秀(但略微过时且已绝版)书“[The Elements of Programming Style][1]”中的一句格言适用:

  • 子例程调用允许我们总结参数列表中的不规则性 [...]
  • [t]子程序本身总结了代码的规律 [...]

我认为count_collisions()例程和使用它的函数合理地体现了这一点。

(另请参阅:DRY if 语句。)


我可以看到当前存在未初始化变量的唯一方法diagnols()是,如果一个或另一个参数值未初始化。确定那里发生了什么需要仔细检查调用diagnols().


存在完整代码

基本编译非常干净。我没有提到我在编译时实际使用的一些选项,所以我收到了 4 个根本不是灾难性的警告:

$ gcc -O3 -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition 8q.c -o 8q  
8q.c:8:6: warning: no previous prototype for ‘print_board’ [-Wmissing-prototypes]
8q.c:22:5: warning: no previous prototype for ‘recur’ [-Wmissing-prototypes]
8q.c:150:5: warning: function declaration isn’t a prototype [-Wstrict-prototypes]
8q.c: In function ‘main’:
8q.c:150:5: warning: old-style function definition [-Wold-style-definition]
$

我收到了一份投诉valgrind,最后是阅读。我在没有的情况下重新编译-O3并获得了更好的诊断valgrind

row >= 0 && row < 8然后,我为范围或等效范围内的行和列的函数参数添加了断言,并触发了其中一个断言:

Assertion failed: (WIDTH >= 0 && WIDTH < 8), function recur, file 8q.c, line 30.
==23867== 
==23867== Process terminating with default action of signal 6 (SIGABRT)
==23867==    at 0x2C582A: __kill (in /usr/lib/system/libsystem_kernel.dylib)
==23867==    by 0x1A85DD: __assert_rtn (in /usr/lib/system/libsystem_c.dylib)
==23867==    by 0x100001747: recur (8q.c:30)
==23867==    by 0x1000018D8: recur (8q.c:152)
==23867==    by 0x10000182F: recur (8q.c:143)
==23867==    by 0x10000182F: recur (8q.c:143)
==23867==    by 0x10000182F: recur (8q.c:143)
==23867==    by 0x10000182F: recur (8q.c:143)
==23867==    by 0x10000182F: recur (8q.c:143)
==23867==    by 0x10000182F: recur (8q.c:143)
==23867==    by 0x10000182F: recur (8q.c:143)
==23867==    by 0x1000017BB: recur (8q.c:131)
==23867== 

因此,在最后,您(可能)recur()使用 WIDTH 8 进行调用(而不是负值——您可以将断言拆分为两个单独的条件以确保)。您的recur()函数中有关于超出范围的注释:

    recur(key, (depth-1), (e+1)); // could go out of bounds

不!这不安全。确保你在通行证上避开麻烦。并且assert是一个简单但有时(实际上,经常)有效的工具。

我自信地说,但当我想得更多时,信心减弱了:

实际造成问题的那一行可能是这一行,虽然那应该是写,而不是读:

/* set a piece as a queen */
key->board[depth][width] = 1;

您还没有检查depth和(在这种情况下)width的有效性。

于 2013-04-03T02:36:15.433 回答