printf格式化字符串加空格后,数组值为何出现异常?
C语言二维数组指针操作导致的异常值问题
原始代码
#include <stdio.h> #include <stdlib.h> typedef int board[8][8]; board * initBoard(void) { board * b = malloc(sizeof(board)); if (!b) { return b; } for (int i = 0; i < 8; i++){ for (int j = 0; j < 8; j ++){ *b[i][j] = 0; } } return b; } void toggle(board * b, int x, int y) { if (*b[x][y] == 1){ *b[x][y] = 0; return; } *b[x][y] = 1; } void dumpBoard(board * b) { for (int i = 0; i < 8; i++){ for (int j = 0; j < 8; j ++){ printf("%d", *b[i][j]); }printf("\n"); } } int main(void) { board * b = initBoard(); if (!b) { return EXIT_FAILURE; } dumpBoard(b); toggle(b, 3, 5); toggle(b, 7, 7); printf("\n"); dumpBoard(b); }
问题现象
这段代码初始化一个8×8的int二维数组,初始值全为0,打印后切换部分元素为1再打印,原本输出符合预期:
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000100 00000000 00000000 00000000 00000001
但修改dumpBoard函数中的printf("%d", *b[i][j]);为printf("%d ", *b[i][j]);后,输出出现异常,第二行第二个位置(数组索引(1,1))出现了一个极大的随机数值:
0 0 0 0 0 0 0 0 0 540024864 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 540024864 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
原因分析
问题核心是运算符优先级:[]的优先级高于*,所以*b[i][j]会被编译器解释为*(b[i][j])。
b是board*类型,而board是int[8][8]的别名,因此b[i]等价于*(b + i),也就是从b的地址偏移i * sizeof(board)个字节的位置。但我们只通过malloc(sizeof(board))分配了一个8×8数组的空间,当i >= 1时,b[i]已经指向了堆内存中超出分配范围的区域,属于越界访问,这是C语言中的未定义行为。
之前没出现明显问题是因为未定义行为的表现不确定,可能刚好越界的内存区域当时是0,或者紧凑的输出格式没暴露问题。修改printf格式后,内存布局发生变化,越界访问到了存储随机数值的内存区域,异常就显现出来了。
修正方法
需要先解引用b得到二维数组,再访问对应的行列,把所有*b[i][j]改成(*b)[i][j]。修正后的代码如下:
#include <stdio.h> #include <stdlib.h> typedef int board[8][8]; board * initBoard(void) { board * b = malloc(sizeof(board)); if (!b) { return b; } for (int i = 0; i < 8; i++){ for (int j = 0; j < 8; j ++){ (*b)[i][j] = 0; } } return b; } void toggle(board * b, int x, int y) { if ((*b)[x][y] == 1){ (*b)[x][y] = 0; return; } (*b)[x][y] = 1; } void dumpBoard(board * b) { for (int i = 0; i < 8; i++){ for (int j = 0; j < 8; j ++){ printf("%d ", (*b)[i][j]); }printf("\n"); } } int main(void) { board * b = initBoard(); if (!b) { return EXIT_FAILURE; } dumpBoard(b); toggle(b, 3, 5); toggle(b, 7, 7); printf("\n"); dumpBoard(b); }
内容的提问来源于stack exchange,提问作者lafinur
相关产品推荐
相关产品推荐

