C语言二维数组存入结构体后指针访问数据异常问题
二维数组存入结构体的指针错误问题解决
问题描述
我有一个固定列数的二维数组const unsigned long test_tileset[n][8],想要存入定义好的DynamicTileset结构体中。赋值后校验指针地址一致,但通过结构体访问数组元素时会得到固定的错误随机数据,直接访问原数组却完全正常。
核心原因
二维数组和二级指针的类型语义完全不兼容:
- 二维数组
test_tileset在内存中是连续的整块区域,退化为指针时类型是unsigned long (*)[8](指向包含8个unsigned long的数组的指针)。 - 结构体中的
unsigned long **tileset是指向指针的指针,它的内存布局是一个指针数组,每个指针指向独立的一维数组。 - 当你把
test_tileset强制传给unsigned long **类型参数时,虽然地址值相同,但访问tileset[0][i]时,编译器会错误地把数组首地址当作指针值去解引用(取到的是数组第一个元素的内容,而非下一级指针),再计算偏移,最终导致非法内存访问,得到错误数据。
解决方案
方法一:修改结构体成员类型匹配二维数组(推荐)
直接把结构体的tileset成员类型改为二维数组对应的指针类型,同时可以额外存储行数方便后续使用:
typedef struct DynamicTileset { // 指向包含8个unsigned long的数组的指针 unsigned long (*tileset)[8]; LinkedList* usedTiles; short* tilemap; int rows; // 存储数组的行数 } DynamicTileset;
对应的创建函数修改参数类型:
DynamicTileset* createDynamicTileset(unsigned long (*tileset)[8], int rows) { DynamicTileset* dynamicTileset = malloc(sizeof(DynamicTileset)); dynamicTileset->tileset = tileset; dynamicTileset->rows = rows; dynamicTileset->usedTiles = createLinkedList(); dynamicTileset->tilemap = NULL; return dynamicTileset; }
调用时直接传入数组和行数:
DynamicTileset* title_tileset = createDynamicTileset(test_tileset, 1);
方法二:手动转换为二级指针(兼容原有结构体)
如果不想修改结构体定义,可以手动创建一个指针数组,每个元素指向二维数组的一行,再把这个指针数组赋值给tileset:
DynamicTileset* createDynamicTileset(const unsigned long (*tileset)[8], int rows) { DynamicTileset* dynamicTileset = malloc(sizeof(DynamicTileset)); // 分配存储行指针的数组 unsigned long** ptr_array = malloc(rows * sizeof(unsigned long*)); for (int i = 0; i < rows; i++) { ptr_array[i] = (unsigned long*)tileset[i]; } dynamicTileset->tileset = ptr_array; dynamicTileset->usedTiles = createLinkedList(); dynamicTileset->tilemap = NULL; return dynamicTileset; }
注意:这种方法需要额外分配内存,后续记得释放ptr_array避免内存泄漏;如果原数组是const类型,建议把结构体的tileset改为const unsigned long **以避免编译警告。
完整修正代码(方法一示例)
// 放在单独的.c文件或.h中 #include <stdio.h> #include <stdlib.h> const unsigned long test_tileset[1][8] = { { 0x12345678, 0x23456789, 0x3456789A, 0x456789AB, 0x56789ABC, 0x6789ABCD, 0x789ABCDE, 0x89ABCDEF } }; typedef struct Node { int data; struct Node *next; } Node; typedef struct LinkedList { Node *head; Node *tail; int size; } LinkedList; typedef struct DynamicTileset { unsigned long (*tileset)[8]; LinkedList* usedTiles; short* tilemap; int rows; } DynamicTileset; LinkedList* createLinkedList() { LinkedList* list = malloc(sizeof(LinkedList)); list->head = NULL; list->tail = NULL; list->size = 0; return list; } DynamicTileset* createDynamicTileset(unsigned long (*tileset)[8], int rows) { DynamicTileset* dynamicTileset = malloc(sizeof(DynamicTileset)); dynamicTileset->tileset = tileset; dynamicTileset->rows = rows; dynamicTileset->usedTiles = createLinkedList(); dynamicTileset->tilemap = NULL; return dynamicTileset; } // 主文件代码 int main() { DynamicTileset* title_tileset = createDynamicTileset(test_tileset, 1); if (title_tileset->tileset != test_tileset) { printf("tileset pointer is wrong\n"); } int is_correct = 1; for (int i=0; i<8; i++) { if (title_tileset->tileset[0][i] != test_tileset[0][i]) { printf("first tile index %d is wrong\n", i); is_correct = 0; break; } } if (is_correct) { printf("All tiles are accessed correctly\n"); } // 释放内存避免泄漏 free(title_tileset->usedTiles); free(title_tileset); return 0; }
内容的提问来源于stack exchange,提问作者Minater 247
相关产品推荐
相关产品推荐

