如何实现无需显式长度变量的动态数组?解决内存释放与指针问题
动态数组实现问题
我想实现一个支持Array_Push(array, val)和Array_Del(array, index)操作的动态数组。一开始我用结构体来存数据和大小:
struct Array { int *data; int size; }
但访问数组元素时得写array.data[i],感觉太啰嗦。
于是我想把数组大小存在数组的某个索引里,不想用[0]怕混淆,就打算存在[-1]的位置。但[-1]超出了常规数组的范围,所以我先通过int *array = malloc(sizeof(int))分配内存,然后把指针自增1(array += 1),这样返回的指针指向的位置,它的[-1]就是之前分配的内存,用来存size。但现在释放内存的时候,直接free(array)会释放非malloc分配的地址,而且我怀疑在函数里调用malloc/realloc的时候没法修改外部的原指针,不知道怎么解决。
下面是相关代码:
array.h
#pragma once #include <stdio.h> #include <stdlib.h> int *Array_Create() { int *array = malloc(sizeof(int)); // 如果malloc分配失败 if (array == NULL) { printf("Error creating array: malloc() failed to allocate memory"); exit(12); } array[0] = 0; array += 1; return array; } void Array_Push(int *array, int value) { if (array[-1] == 0) { array = malloc(sizeof(int)); } else { // array = realloc(array, sizeof(int) * array[-1]); printf("ERROR READING ARRAY SIZE IN: Array_Push()"); } }
main.c
#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <dynamic_array.h> int main() { int *array = Array_Create(); printf("%d", array[-1]); Array_Push(array, 1); free((array - 1)); free(array); return 0; }
解决方案
核心问题分析
- 内存释放错误:返回的指针是
malloc地址+1,直接free(array)会释放非法内存,且main里的两次free属于双重释放,是严重错误。 - 指针修改无效:
Array_Push里的array = malloc(...)只修改了函数内的局部指针,外部原指针不会同步变化,必须用指针的指针传递参数才能修改外部指针变量。
修改后的完整实现
array.h
#pragma once #include <stdio.h> #include <stdlib.h> // 创建动态数组,返回指向数据区的指针,size存在[-1]位置 int *Array_Create() { // 初始分配1个存size的空间 + 0个数据空间 int *base = malloc(sizeof(int) * (1 + 0)); if (base == NULL) { fprintf(stderr, "Error creating array: malloc() failed\n"); exit(12); } base[0] = 0; // 初始size为0 return base + 1; // 返回数据区起始指针 } // 向数组末尾添加元素,需传指针的指针以同步指针变化 void Array_Push(int **array, int value) { int *size_ptr = *array - 1; int current_size = *size_ptr; // 重新分配内存:原size+1个数据 + 1个size存储位 int *new_base = realloc(size_ptr, sizeof(int) * (current_size + 1 + 1)); if (new_base == NULL) { fprintf(stderr, "Error pushing element: realloc() failed\n"); exit(13); } // 更新size和数据区指针 *new_base = current_size + 1; *array = new_base + 1; // 存入新元素 (*array)[current_size] = value; } // 删除指定索引的元素,需传指针的指针 void Array_Del(int **array, int index) { int *size_ptr = *array - 1; int current_size = *size_ptr; if (index < 0 || index >= current_size) { fprintf(stderr, "Error deleting element: index out of bounds\n"); return; } // 把索引后元素前移覆盖 for (int i = index; i < current_size - 1; i++) { (*array)[i] = (*array)[i + 1]; } // 缩小内存分配 int *new_base = realloc(size_ptr, sizeof(int) * (current_size - 1 + 1)); if (new_base == NULL) { fprintf(stderr, "Error deleting element: realloc() failed\n"); exit(14); } // 更新size和数据区指针 *new_base = current_size - 1; *array = new_base + 1; } // 释放数组内存,只需传入数据区指针 void Array_Free(int *array) { free(array - 1); // 释放最初分配的基地址 }
main.c
#include <stdio.h> #include "array.h" int main() { int *array = Array_Create(); // 推入测试元素 Array_Push(&array, 10); Array_Push(&array, 20); Array_Push(&array, 30); // 打印当前数组 int size = array[-1]; printf("Array size: %d\nElements: ", size); for (int i = 0; i < size; i++) { printf("%d ", array[i]); } printf("\n"); // 删除索引1的元素 Array_Del(&array, 1); size = array[-1]; printf("After delete index 1:\nSize: %d\nElements: ", size); for (int i = 0; i < size; i++) { printf("%d ", array[i]); } printf("\n"); // 释放内存 Array_Free(array); return 0; }
关键改进点
- 规范内存管理:始终通过
array - 1访问存储size的基地址,释放时仅需free该基地址,避免内存泄漏或双重释放。 - 指针同步更新:
Array_Push和Array_Del使用int **array参数,确保realloc后的新指针能同步到外部变量。 - 封装操作逻辑:提供
Array_Free函数,避免用户手动计算基地址出错,同时简化调用流程。
内容的提问来源于stack exchange,提问作者Debuholden
相关产品推荐
相关产品推荐

