C语言自定义动态数组内存异常问题:多数组交替添加元素时出错
哥们,我一眼就看出你的问题核心了——realloc扩容后没有同步更新外部的数组指针!这直接导致了野指针访问,进而出现诡异的内存错误。
问题根源分析
你的动态数组设计思路是对的:把元数据(长度、步长、容量)存在用户可见指针的前面,用户拿到的指针直接指向数据区。但在_arr_add函数里,当需要扩容调用realloc时,会出现一个关键问题:realloc可能会把整个内存块(元数据+数据区)移动到新的内存地址,这时候你手里的raw指针已经指向新地址了,但用户传入的arr指针(也就是用户代码里的arr1、arr2)还是指向旧的内存地址——而旧地址已经被realloc释放或复用了。
当你单独操作一个数组时,有时候realloc会直接在原地址后追加空间(不需要移动),所以看起来没问题,但这是运气好的未定义行为;而交替操作两个数组时,内存分配更复杂,realloc移动了其中一个数组的内存,导致另一个数组的元数据被覆盖,就出现了你看到的诡异LENGTH值(1544099056),后续的memcpy也会因为野指针踩内存导致程序崩溃。
另外还有个小问题:你用%d打印size_t类型的变量,这会导致类型不匹配,也是出现奇怪数字的原因之一,size_t应该用%zu格式符。
解决方案
要让_arr_add能更新用户手里的数组指针,必须把函数参数改成指针的指针(void**),这样扩容后就能把新的指针地址同步回用户的变量里。
修改后的代码片段
- 先修改
_arr_add的函数定义:
void _arr_add(void **arr, void *num) { // 从用户传入的指针(现在是指针的指针)获取元数据起始地址 size_t *raw = ((size_t *)*arr) - NUM_PROPERTIES; // 用%zu打印size_t类型,避免类型不匹配 printf("LENGTH: %zu\n", raw[LENGTH]); if (raw[LENGTH] == raw[CAPACITY]) { raw[CAPACITY] *= 2; size_t *new_raw = realloc(raw, sizeof(size_t) * NUM_PROPERTIES + raw[CAPACITY] * raw[STRIDE]); if (new_raw == NULL) { fprintf(stderr, "Error reallocating memory in _arr_add()\n"); exit(1); } raw = new_raw; // 关键:更新外部的数组指针,指向新的数据区起始地址 *arr = (void *)(raw + NUM_PROPERTIES); } // 转成char*做指针运算(C标准不允许void*直接加减) memcpy((char*)*arr + raw[LENGTH] * raw[STRIDE], num, raw[STRIDE]); raw[LENGTH]++; }
- 同步修改
arr_add宏,传入数组指针的地址:
#define arr_add(arr, num) _arr_add(&arr, &num)
验证效果
修改后,当你交替给arr1和arr2添加元素时,每次realloc都会自动更新用户手里的数组指针,不会再出现野指针访问的情况,LENGTH值会正常递增,程序也能稳定运行。
备注:内容来源于stack exchange,提问作者champson1864

