一、概述
首先这儿介绍一本很好很好的书《c指针编程之道》根据这本书中的内容来学习这几个函数!!!
1.下面介绍第一个函数malloc,原型:void* malloc(unsigned int num_bytes),此函数在堆区可以占用一块空间,返回的是占用空间的第一个地址,可能开辟失败,一定判断是否开辟成功!开辟失败,返回NULL,对开辟成功的内存不做处理,也就是以前的随机值!
• 如果开辟成功,则返回⼀个指向开辟好空间的指针。
• 如果开辟失败,则返回⼀个 NULL 指针,因此malloc的返回值⼀定要做检查。
• 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使⽤的时候使⽤者⾃⼰来决定。
• 如果参数 size 为0,malloc的⾏为是标准是未定义的,取决于编译器。
2.free 此函数的原型是void free(void* ptr)
如果ptr指向的空间不是堆区开辟的,free函数的行为是未定义的
如果ptr是NULL,则函数什么事情也不做!!!
int main()
{
int* ptr = (int*)malloc(sizeof(int) * 10);
//是否开辟成功
if (!ptr)
{
perror("The malloc fail:");
return 1;
}
//开辟成功
for (int i = 0; i < 10; i++)
{
ptr[i] = i;
printf("%d ", ptr[i]);
}
printf("\n");
//释放内存
ptr = NULL;
return 0;
}
int main()
{
int* ptr = (int*)malloc(0);
if (!ptr)
{
perror("Fail:");
return 1;
}
*ptr = 1;
//虽然行为未定义
//数据还是放进内存了!!!
free(ptr);
ptr = NULL;
return 0;
}
3.calloc 原型是:void* calloc(unsigned num,unsigned size);
int main()
{
int*ptr = (int*)calloc(10, sizeof(int));
if (!ptr)
{
return 1;
}
for (int i = 0; i < 10; i++)
{
printf("%d ", ptr[i]);
}
free(ptr);
ptr = NULL;
return 0;
}
4.realloc 函数原型是void* realloc(void* ptr,size_size);理解此函数,注意两点,首先,此函数是扩容用的函数,如果原空间在堆区有足够大的空间,那么在后面直接开辟,如果没有足够大的空间,则另找一块空间,把原空间的内容复制到新空间进行扩容,并且会释放原来的空间.
int main()
{
int* ptr = (int*)malloc(sizeof(int)*10);
if (!ptr)
{
perror("malloc fail:");
return 1;
}
for (int i = 0; i < 10; i++)
{
ptr[i] = i;
}
int* tmp = (int*)realloc(ptr, sizeof(int) * 11);
if (!tmp)
{
return 1;
}
ptr = tmp;
for (int i = 0; i < 11; i++)
{
printf("%d ", ptr[i]);
}
free(ptr);
ptr = NULL;
return 0;
}
二、常见动态内存错误
1.对NULL的解引用操作!
void test()
{
int *p = (int *)malloc(INT_MAX/4);
*p = 20;//如果p的值是NULL,就会有问题
//所以这儿一定要检测NULL
free(p);
}
2.对动态开辟空间的越界访问
void test()
{
int i = 0;
int *p = (int *)malloc(10*sizeof(int));
if(NULL == p)
{
exit(EXIT_FAILURE);
}
for(i=0; i<=10; i++)
{
*(p+i) = i;//当i是10的时候越界访问
}
free(p);
}
3.对⾮动态开辟内存使⽤free释放
void test()
{
int a = 10;
int *p = &a;
free(p);//栈区内存不能释放,会自动释放!!!
}
4.使⽤free释放⼀块动态开辟内存的⼀部分
void test()
{
int *p = (int *)malloc(100);
p++; //p已经移动了,不在指向首位置
free(p);//p不再指向动态内存的起始位置
}
5.对同⼀块动态内存多次释放
void test()
{
int *p = (int *)malloc(100);
free(p);
free(p);//重复释放
}
6.动态开辟内存忘记释放(内存泄漏)
void test()
{
int *p = (int *)malloc(100);
if(NULL != p)
{
*p = 20;
}
//一定要在内存用完,要free,不然会内存泄漏!!!
}
int main()
{
test();
while(1);
}
三、 动态内存经典笔试题分析
1.题⽬1:
void GetMemory(char* p)
{
p = (char*)malloc(100);
}
int main()
{
char* str = NULL; //创建了char*的变量,用来存储地址
//所有的传参都是复制
GetMemory(str);
strcpy(str, "hello world");
printf(str);
return 0;
}
2.题目2
char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}
`
3.题目3
void GetMemory(char** p, int num)
{
*p = (char*)malloc(num);
}
int main()
{
char* str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
return 0;
}
4.题目4
int main()
{
char* str = (char*)malloc(100);
strcpy(str, "hello");
free(str);
if (str != NULL)
{
strcpy(str, "world");
printf(str);
}
return 0;
}
四、柔性数组
/*****************柔性数组使用******************/
struct S
{
int n;
int arr[0];
};
int main()
{
//为柔性数组开辟40个字节的空间
struct S* ps = (struct S*)malloc(sizeof(struct S) + 40);
if (!ps)
{
perror("malloc fail:");
return 1;
}
ps->n = 10;
for (int i = 0; i < 10; i++)
{
ps->arr[i] = i;
}
for (int i = 0; i < ps->n; i++)
{
printf("%d ", ps->arr[i]);
}
free(ps);
ps = NULL;
return 0;
}
struct S
{
int n;
int* ps;
};
int main()
{
struct S s;
int* tmp = malloc(sizeof(int) * 10);
if (!tmp)
{
perror("malloc fail:");
return 1;
}
s.ps = tmp;
s.n = 10;
for (int i = 0; i < 10; i++)
{
(s.ps)[i] = i;
printf("%d ", (s.ps)[i]);
}
free(s.ps);
s.ps = NULL;
return 0;
}
完结!!!