目录
1,内存分配
2.内存分配函数
1.malloc函数
2.free函数
?3.calloc函数
4.realloc函数
3.常见错误
1.对NULL指针的操作
2.free函数的错误使用
3.free一部分空间
4.越界访问
5.多次释放同一空间
6.不释放空间---内存泄漏
4.柔性数组
5.面试题
1,内存分配
?静态区:存放全局变量,static修饰的变量? ?这类数据在函数结束后不会被销毁,生命周期得以延长,在整个程序结束时才销毁
堆区:动态申请的空间
栈区:存放局部变量,函数形参,函数调用结束时自动销毁。
2.内存分配函数
1.malloc函数
函数功能:在堆区上分配所需的连续 内存空间,并返回一个指向它的指针?。
函数声明:void* malloc (size_t num)? -----num:申请空间的大小,单位是字节。
返回值:函数返回一个指针 ,指向已分配大小的内存。如果请求失败,则返回 NULL。
头文件:stdlib.h
2.free函数
函数功能:free()是C语言中释放内存空间的函数,通常与申请内存空间的函数malloc()结合使用,可以释放由 malloc()、calloc()、realloc() 等函数申请的内存空间。
头文件:stdlib.h
函数声明:void * free(void *ptr)
ptr-- 指针指向一个要释放内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的。如果传递的参数是一个空指针,则不会执行任何动作。
返回值:无返回值
#include<stdio.h>
#include<stdlib.h>
int main()
{
int arr[3];
int* p = (int*)malloc(sizeof(int) * 3);//在堆区申请12个字节的空间,p指向这块空间
if (p == NULL)
{
printf("内存申请失败\n");
exit(-1);
}
for (int i = 0; i < 3; i++)
*(p + i) = i;
for (int i = 0; i < 3; i++)
printf("%d ", *(p + i));
free(p);//释放掉动态内存申请的空间,这时候p指向了一个被释放的空间
p=NULL;//p置为空
return 0;
}
?3.calloc函数
函数功能:在堆区申请num个字节长度是size的连续空间,返回空间的起始地址,如果开辟失败,返回NULL。
函数原型:void * calloc(unsigned int num,unsigned int size)
头文件:stdlib.h
返回值:无返回值
calloc在动态分配完内存后,自动初始化该内存空间为零,而malloc不做初始化,分配到的空间中的数据是随机数据。
#include<stdlib.h>
#include<stdio.h>
int main()
{
int* p = (int*)calloc(10, sizeof(int));//开辟10个字节数是4的空间
if (p == NULL)
{
perror("错误");
return -1;
}
for (int i = 0; i < 10; i++)
printf("%d ", *(p + i));
free(p);
p = NULL;
return 0;
}
打印出 10个0
4.realloc函数
函数功能:更改动态内存分配的空间大小
?函数原型:void *realloc(void *ptr, size_t size)?尝试重新调整之前调用?malloc?或?calloc?所分配的?ptr?所指向的内存块的大小。
- ptr?-- 指针指向一个要重新分配内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的。如果为空指针,则会分配一个新的内存块,且函数返回一个指向它的指针。
- size?-- 内存块的新的大小,以字节为单位。如果大小为 0,且 ptr 指向一个已存在的内存块,则 ptr 所指向的内存块会被释放,并返回一个空指针。
返回值:该函数返回一个指针 ,指向重新分配大小的内存。如果请求失败,则返回 NULL。
头文件:stdlib.h
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
int* p = (int *)malloc(5 * sizeof(int));
if (p == NULL)
{
printf("错误%s", strerror(errno));
return -1;
}
for (int i = 0; i < 5; i++)
{
*(p + i) = i + 1;
}
int* ptr = (int*)realloc(p, 10 * sizeof(int));
if (ptr == NULL)
{
printf("错误%s", strerror(errno));
return -1;
}
p = ptr;
for (int i = 5; i < 10; i++)
{
*(p + i) = i + 1;
}
for (int i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
free(p);
p = NULL;
return 0;
}
3.常见错误
1.对NULL指针的操作
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
int* p = (int*)malloc(1000000000 * sizeof(int));
if (p == NULL)
{
printf("错误:%s", strerror(errno));
//这时候 想要分配空间过大,空间不足,p已经是NULL指针了
*p = 0;//程序出错:对NULL指针访问
}
return 0;
}
2.free函数的错误使用
#include<stdio.h>
#include<stdlib.h>
int main()
{
int a = 10;
int* p = &a;
free(p);//出错:p是指向了a,但是a是局部变量,在栈上存储,占有的并不是动态分配的空间
return 0;
}
3.free一部分空间
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
int* p = (int*)malloc(10 * sizeof(int));
if (p == NULL)
{
printf("错误:%s", strerror(errno));
return -1;
}
p = p + 3;
free(p);//出错:分配了40个字节的空间,p现在指向了分配空间的一部分,部分内存空间没办法释放
return 0;
}
4.越界访问
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
int* p = (int*)malloc(100);//100个字节空间
if (p == NULL)
{
printf("错误:%s", strerror(errno));
return -1;
}
for (int i = 0; i < 30; i++)
*(p + i) = i;//120个空间 越界访问
for (int i = 0; i < 30; i++)
printf("%d ", *(p + i));
free(p);
p = NULL;
return 0;
}
5.多次释放同一空间
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
int* p = (int*)malloc(100);
if (p == NULL)
{
printf("错误:%s", strerror(errno));
return -1;
}
free(p);
free(p);//错误:多次释放
return 0;
}
6.不释放空间---内存泄漏
动态申请的内存释放两种方式:free和程序结束时自动释放。当不free,程序也不结束时,如果这个代码被多次使用,内存一直被开辟,那么剩余内存就会越来越少。
内存泄漏:是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
int* p = (int*)malloc(100);
if (p == NULL)
{
printf("错误:%s", strerror(errno));
return -1;
}
getchar();//要求输入 但是不输入-程序不结束
return 0;
}
4.柔性数组
1、什么是柔性数组?
柔性数组是数组大小待定的数组, C语言中结构体的最后一个元素可以是大小未知的数组,也就是所谓的0长度,所以我们可以用结构体来创建柔性数组。
2、柔性数组有什么用途 ?
它的主要用途是为了满足需要变长度的结构体,为了解决使用数组时内存的冗余和数组的越界问题。
3.柔性数组的特点
?使用柔性数组,对结构体的所有成员一起分配空间
#include<stdio.h>
#include<stdlib.h>
struct s
{
int i;
int arr[0];//或者 int arr[]; 柔性数组
};
int main()
{
struct s* s = (struct s*)malloc(sizeof(int) + sizeof(int) * 10);
//为i分配了4个字节的空间,为arr分配了40个字节空间
if (s == NULL)
{
perror("错误");
return -1;
}
s->i = 10;
for (int i = 0; i < 10; i++)
{
s->arr[i] = i;
}
for (int i = 0; i < 10; i++)
{
printf("%d ", s->arr[i]);
}
printf("%d ", s->i);
//空间不够 可以增容
struct s* p = (struct s*)realloc(s, sizeof(int) + 20 * sizeof(int));
if (p == NULL)
{
perror("错误");
return -1;
}
s = p;
for (int i = 0; i < 20; i++)
{
s->arr[i] = i;
}
for (int i = 0; i < 20; i++)
{
printf("%d ", s->arr[i]);
}
free(s);
s = NULL;
return 0;
}
?只为指针分配连续空间
#include<stdio.h>
#include<stdlib.h>
struct s
{
int i;
int *arr;
};
int main()
{
struct s p;
p.i = 100;
p.arr = (int*)malloc(sizeof(int) * 10);
//为arr分配了40个字节空间
if (p.arr == NULL)
{
perror("错误");
return -1;
}
for (int i = 0; i < 10; i++)
{
p.arr[i] = i;
}
for (int i = 0; i < 10; i++)
{
printf("%d ", p.arr[i]);
}
printf("%d ", p.i);
//空间不够 可以增容
int* ps = (int*)realloc(p.arr, 20 * sizeof(int));
if ( ps== NULL)
{
perror("错误");
return -1;
}
p.arr = ps;
for (int i = 0; i < 20; i++)
{
p.arr[i] = i;
}
for (int i = 0; i < 20; i++)
{
printf("%d ", p.arr[i]);
}
free(p.arr);
p.arr = NULL;
return 0;
}
使用柔性数组,i和arr的空间是连续的,不使用柔性数组,arr有可能由于连续空间不足,重新查找空间来分配,导致i和arr不连续,而连续的内存有利于提高访问效率。
5.面试题
1.调用test函数,p和str相互独立,p指向开辟的连续空间,调用?GetMemory结束后,p释放,str仍然是NULL指针,对NULL指针复制字符串,错误。并且存在内存泄漏。
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
2..调用test函数,GetMemory函数中,创建局部数组p,返回p的地址,但是GetMemory调用结束,p空间释放,归还给了操作系统,str指向了一段被释放了的空间,如果说这块空间内容没有被别的程序更改,那么打印hello world,如果空间内容被更改,得到随机值。
char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}
|