1.函数介绍
1.1malloc free1.2calloc1.3realloc 2.常见错误
2.1对NULL指针解引用2.2对动态开辟的空间越界访问2.3对非动态开辟空间free释放2.4free释放动态开辟空间的一部分内容2.5对同一块空间多次释放2.6动态开辟空间忘记释放 3.试题分析
3.1第一题3.2第二题3.3第三题3.4第四题 4.柔性数组
1.函数介绍 1.1malloc free
malloc会在堆中开辟一块连续的空间,空间大小是size,以字节为单位。开辟成功返回一个指向开辟好的空间的指针,开辟失败返回NULL指针,所以在使用malloc函数后一定要进行检查。malloc函数返回的是void*的空指针,所以在使用时需要将其强转成对应类型。
free函数能释放动态开辟过的内存,函数参数就是指向动态开辟空间首地址的指针。如果指针为NULL,free函数什么都不做,free函数指向的空间不是动态开辟的,程序会出错。
使用模式
void test() { int* p = (int*)malloc(40); if (p == NULL)//判断指针是否为空 { printf("%s", strerror(errno));//打印错误信息并且退出程序 return; } //使用 free(p);//使用后将p指向的空间释放 p = NULL;//这是p是野指针,将p置为NULL }
malloc和free是一对函数,使用了malloc函数一定要使用free函数释放内存。
1.2calloc
calloc和malloc一样是开辟动态内存的函数,不一样的是calloc会将开辟空间初始化为0。参数中的num是要开辟的元素个数,size是一个元素的字节数。返回类型也是void*,使用时要注意强转类型。
void test1() { int* p = (int*)calloc(4, 4); if(p == NULL) { printf("%s", strerror(errno)); return; } free(p); p = NULL; }1.3realloc
使用realloc函数调整动态内存空间的大小,memblock是指向一块动态内存的指针,size是调整后的空间大小,以字节为单位。如果调整空间失败,函数返回NULL。在函数调整空间成功的情况下,有两种情况
- 原有空间后有足够空间,函数返回的指针与原来相同原有空间后没有足够空间,函数会找到一块足够大的空间,返回的指针与原来不同
使用
void test1() { int* p = (int*)calloc(4, 4); if(p == NULL) { printf("%s", strerror(errno)); return; } p = (int*)realloc(p, 80); free(p); p = NULL; }2.常见错误 2.1对NULL指针解引用
int main() { int* p = (int*)malloc(INT_MAX); *p = 1; free(p); p = NULL; rturn 0; }
没有检查malloc返回的指针是否为空,INT_MAX是一个二十几亿的数字,malloc函数开辟失败返回空指针,p为空指针,对p解引用是非法的。
2.2对动态开辟的空间越界访问int main() { int* p = (int*)malloc(10 * 4); if (p == NULL) return 0; int i = 0; for (i = 0; i <= 10; i++) { *(p + i) = i; } free(p); p = NULL; return 0; }
i为10的时候,p + i指向第11个整形,而malloc只开辟了10个整形大小的空间,*(p + i)属于越界访问。当使用动态开辟的空间时,注意开辟空间的大小,小心越界。
2.3对非动态开辟空间free释放void test() { int a = 1; int* p = &a; free(p); }
p指向a,不是动态内存开辟的空间,使用free释放p会导致错误
2.4free释放动态开辟空间的一部分内容void test() { int* p = (int*)malloc(40); p++; free(p); }
当p不指向动态开辟空间的起始位置时,使用free释放会导致程序错误。在动态开辟内存时,一定要有一个指针始终指向空间的起始位置。
2.5对同一块空间多次释放void test() { int *p = (int *)malloc(100); free(p); free(p);//重复释放 }2.6动态开辟空间忘记释放
void test() { int *p = (int *)malloc(100); if(NULL != p) *p = 20; }
当忘记释放动态开辟空间时,会导致内存的浪费,称作内存泄漏。当使用了malloc时一定要对应使用free。
3.试题分析 3.1第一题void GetMemory(char* p) { p = (char*)malloc(100); } void Test(void) { char* str = NULL; GetMemory(str); strcpy(str, "hello world"); printf(str); }
问:Test函数有什么问题?
两个
- 调用GetMemory函数时传递的是值,并没用传递地址,因此str没有指向动态内存开辟的空间,而是一个空指针,将"hello world"的内容拷贝到str中是错误的。p所指向的动态内存没有释放,导致内存泄漏。
char* GetMemory(void) { char p[] = "hello world"; return p; } void Test(void) { char* str = NULL; str = GetMemory(); printf(str); }
打印结果乱码。
GetMemory函数返回栈空间变量的地址,这是一个严重的错误,GetMemory函数执行完创建的局部变量销毁,p指针指向的hello world同样销毁,此时p为一个野指针,这时不能再使用p指针。
3.3第三题void GetMemory(char** p, int num) { *p = (char*)malloc(num); } void Test(void) { char* str = NULL; GetMemory(&str, 100); strcpy(str, "hello"); printf(str); }
与第一题相同,传址调用改变了str的内容,此时str指向一块动态开辟的空间,但是没有free释放,导论了内存泄漏。
3.4第四题void Test(void) { char* str = (char*)malloc(100); strcpy(str, "hello"); free(str); if (str != NULL) { strcpy(str, "world"); printf(str); } }
释放内存之后继续使用原有空间。
总结,在开辟动态内存后一定不能忘了free释放掉内存并置空指针,同时也不能继续使用之前的内存。
4.柔性数组结构体的最后一个成员为柔性数组,sizeof该结构体得到的是除了柔性数组其他成员的大小,除了柔性数组以外至少还应该有一个成员。目前我还不知道应用场景,先放一个使用
两种写法,一种在结构体中创建arr[]或arr[0]数组,一种创建指针
struct S1 { int i; int arr[]; }; struct S2 { int i; int* p; }; int main() { struct S1* p = (struct S1*)malloc(sizeof(struct S1) + 80); p->i = 0; int i = 0; if (p != NULL) for (i = 0; i < 10; i++) { p->arr[i] = i; } free(p); p = NULL; struct S2* ps = (struct S2*)malloc(sizeof(struct S2)); if (ps != NULL) { ps->p = (int*)malloc(100); if (ps->p != NULL) { //使用 } free(ps->p); ps->p = NULL; free(ps); ps = NULL; } return 0; }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)