为何NULL检查在VS C11触发访问违例,Debian GCC C11却正常?
相邻块指针设置代码在VS C11中触发读取访问违例的原因与修复
我写了一段用于设置Block相邻块指针的代码,在Debian环境用GCC C11编译运行完全正常,但放到Visual Studio C11环境下就触发了读取访问违例异常,代码如下:
#include <stdio.h> #include <stdlib.h> #define CHUNK_WIDTH 16 #define CHUNK_HEIGHT 64 #define CHUNK_DEPTH 16 struct Block { // data struct { struct Block* above; struct Block* below; struct Block* left; struct Block* right; struct Block* front; struct Block* behind; } neighbors; }; struct Chunk { struct Block blocks[CHUNK_HEIGHT][CHUNK_DEPTH][CHUNK_WIDTH]; }; void SetNeighboringBlocks(struct Chunk* chunk, int blockIndexX, int blockIndexY, int blockIndexZ) { struct Block* curBlock = &chunk->blocks[blockIndexY][blockIndexZ][blockIndexX]; curBlock->neighbors.left = (blockIndexX != 0)? &chunk->blocks[blockIndexY][blockIndexZ][blockIndexX - 1] : NULL; curBlock->neighbors.right = (blockIndexX != CHUNK_WIDTH - 1)? &chunk->blocks[blockIndexY][blockIndexZ][blockIndexX + 1] : NULL; curBlock->neighbors.above = (blockIndexY != CHUNK_HEIGHT - 1)? &chunk->blocks[blockIndexY + 1][blockIndexZ][blockIndexX] : NULL; curBlock->neighbors.below = (blockIndexY != 0)? &chunk->blocks[blockIndexY - 1][blockIndexZ][blockIndexX] : NULL; curBlock->neighbors.front = (blockIndexZ != 0)? &chunk->blocks[blockIndexY][blockIndexZ - 1][blockIndexX] : NULL; curBlock->neighbors.behind = (blockIndexZ != CHUNK_DEPTH - 1)? &chunk->blocks[blockIndexY][blockIndexZ + 1][blockIndexX] : NULL; } struct Chunk* CreateChunk(int x, int z) { struct Chunk* ret = (struct Chunk*)malloc(sizeof(struct Chunk)); for (int z = 0; z < CHUNK_DEPTH; ++z) for (int y = 0; y < CHUNK_HEIGHT; ++y) for (int x = 0; x < CHUNK_WIDTH; ++x) { SetNeighboringBlocks(ret, x, y, z); } return ret; } int main(void) { struct Chunk* chunk = CreateChunk(0, 0); for (int z = 0; z < CHUNK_DEPTH; ++z) for (int y = 0; y < CHUNK_HEIGHT; ++y) for (int x = 0; x < CHUNK_WIDTH; ++x) { // If it has neighbors in all direction, then it is inside struct Block* cur = &chunk->blocks[y][z][x]; if ( cur->neighbors.above && cur->neighbors.below && cur->neighbors.left && cur->neighbors.right && cur->neighbors.front && cur->neighbors.behind ) // Do something } return 0; }
问题原因
核心问题是CreateChunk函数中的变量命名冲突:函数参数为int x, int z,但循环内部又定义了同名的局部变量int z和int x。
- GCC对这种嵌套同名变量的处理是内部变量覆盖外部参数,循环能正常执行,但这属于C标准中的未定义行为,只是GCC的实现恰好兼容这种写法。
- Visual Studio的C编译器对栈变量的布局更严格,变量覆盖后导致后续内存访问出现越界,最终触发读取访问违例。
修复方案
修改循环内的变量名,避免与函数参数冲突,比如将循环中的z改为dz,x改为dx:
struct Chunk* CreateChunk(int x, int z) { struct Chunk* ret = (struct Chunk*)malloc(sizeof(struct Chunk)); for (int dz = 0; dz < CHUNK_DEPTH; ++dz) for (int y = 0; y < CHUNK_HEIGHT; ++y) for (int dx = 0; dx < CHUNK_WIDTH; ++dx) { SetNeighboringBlocks(ret, dx, y, dz); } return ret; }
修改后,代码在Visual Studio C11环境下可以正常运行,同时也符合C标准规范,消除了未定义行为的风险。
内容的提问来源于stack exchange,提问作者Hexa
相关产品推荐
相关产品推荐

