目录
2. 运行test的结果?//虽然打印出来10,但是代码是错误的,只是空间可能还没占用罢了
3. 运行test的结果?//非法内存访问,但是会输出world
这里返回的是变量本身,返回变量本身,返回的时候会在寄存器上临时保存,再返回,虽然还是会销毁,但是销毁与否与寄存器无关
calloc、malloc、realloc函数的区别及用法?
三者都是分配内存,都是stdlib.h库里的函数,但是也存在一些差异。
1).malloc函数。其原型void *malloc(unsigned int num_bytes);
num_byte为要申请的空间大小,需要我们手动的去计算;
如int *p = (int *)malloc(20*sizeof(int)),如果编译器默认int为4字节存储的话,那么计算结果是80Byte,一次申请一个80Byte的连续空间,并将空间基地址强制转换为int类型,赋值给指针p,此时申请的内存值是不确定的。
2).calloc函数,其原型void *calloc(size_t n, size_t size);
其比malloc函数多一个参数,并不需要人为的计算空间的大小,比如如果他要申请20个int类型空间,会int *p = (int *)calloc(20, sizeof(int)),这样就省去了人为空间计算的麻烦。但这并不是他们之间最重要的区别,malloc申请后空间的值是随机的,并没有进行初始化,而calloc却在申请后,对空间逐一进行初始化,并设置值为0;
既然calloc不需要计算空间并且可以直接初始化内存避免错误,那为什么不直接使用calloc函数,那要malloc要什么用呢?
实际上,任何事物都有两面性,有好的一面,必然存在不好的地方。这就是效率。calloc函数由于给每一个空间都要初始化值,那必然效率较malloc要低,并且现实世界,很多情况的空间申请是不需要初始值的,这也就是为什么许多初学者更多的接触malloc函数的原因。
3).realloc函数和上面两个有本质的区别,其原型void realloc(void *ptr, size_t new_Size)
用于对动态内存进行扩容(及已申请的动态空间不够使用,需要进行空间扩容操作),ptr为指向原来空间基址的指针, new_size为接下来需要扩充容量的大小。
如果size较小,原来申请的动态内存后面还有空余内存,系统将直接在原内存空间后面扩容,并返回原动态空间基地址;如果size较大,原来申请的空间后面没有足够大的空间扩容,系统将重新申请一块(20+size)*sizeof(int)的内存,并把原来空间的内容拷贝过去,原来空间free;如果size非常大,系统内存申请失败,返回NULL,原来的内存不会释放。注意:如果扩容后的内存空间较原空间小,将会出现数据丢失,如果直接realloc(p, 0);相当于free(p).
realloc实例:栈的c语言实现中:
void StackPush(ST* ps, STDataType x)//入栈或者说压栈
{
assert(ps);
//判断栈是否满了,满了就扩容,
if (ps->capacity == ps->top)
{
//考虑第一次入栈,栈为空的时候(加上判断),给栈初始容量4
int newcapcity = (ps->capacity == 0) ? 4 : (ps->capacity * 2);
ps->a = (STDateType*)realloc(ps->a, newcapcity);
if (ps->a == NULL)
{
printf("realloc error!\n");
exit(-1);
}
}
ps->a[ps->top] = x;
++ps->top;
}
#include<stdio.h>
#include<stdlib.h>
void test4()
{
int* p = (int*)malloc(100);
free(p);
free(p);//重复释放
}
void test3()
{
int* p = (int*)malloc(100);
p++;
free(p);//p不再指向动态内存的起始位置
}
void test2()
{
int a = 10;//栈上的空间是自动释放的
int* p = &a;
free(p);//ok?//对非动态开辟的空间进行释放
}
void test1()
{
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);
}
void test()
{
//申请空间过大,可能开辟失败
int* p = (int*)malloc(INT_MAX / 4);
*p = 20;//如果p的值是NULL,就会有问题
free(p);
}
int main()
{
test();
test1();
test2();
test3();
test4();
return 0;
}
请指出以下代码的错误?//忘记释放,造成内存泄露
#include<stdio.h>
#include<stdlib.h>
//void test()
int* test()
{
int* p = (int*)malloc(100);
if (NULL != p)
{
*p = 20;
}
//free(p);
//p = NULL;
return p;
}
int main()
{
int* ptr = test();
free(ptr);
//test();
return 0;
}
笔试题:
1. 运行test函数的结果,为什么?
void GetMemory(char* p)//刚传过来的时候p也是空指针
{
p = (char*)malloc(100);//p指向堆区的这一块空间
}//但是p是临时变量,出了函数就销毁了,但是malloc的空间还在,只是找不到了
void Test(void)
{
char* str = NULL;
GetMemory(str);//把str本身直接传过去了
strcpy(str, "hello world");
//p的改变不会改变str,str还是空指针,这个时候用helloworld去覆盖NULL指针,会非法访问内存
printf(str);
}
int main()
{
Test();
return 0;
}
运行结果:程序崩溃
2. 运行test的结果?//虽然打印出来10,但是代码是错误的,只是空间可能还没占用罢了
int* test()
{
int a = 10;
return &a;
}
int main()
{
int* p = test();
//printf("hehe\n");//加上这句话,就打印不出10了
printf("%d\n", *p);
return 0;
}//虽然打印出来10,但是代码是错误的,只是空间可能还没占用罢了
3. 运行test的结果?//非法内存访问,但是会输出world
void Test(void)
{
char* str = (char*)malloc(100);
strcpy(str, "hello");
free(str);//free不会让str置成空
if (str != NULL)
{
//此时str指向的空间已经被free了,造成非法访问内存
strcpy(str, "world");
//free后房子可能还在,只是你没有了钥匙,你再访问就取决于房东和新的租客了
//回收只是从你手里回收给房东
printf(str);
}
}
int main()
{
Test();
return 0;
}
4.运行结果是?打印出来烫烫烫,返回栈空间地址的问题
char* GetMemory(void)
{
char p[] = "hello world";
return p;//注意:这里不是返回的变量,而是栈空间上的地址
//数组名是数组首元素的地址,所以会销毁
}
void Test(void)
{
char* str = NULL;
str = GetMemory();
printf(str);
}
int main()
{
Test();
return 0;
}
p在GETMEMORY创建,出了空间就被销毁,返回的是数组的地址,
但是出了函数后,p的空间回收了,
str虽然记着p的地址,这个时候str就变成了野指针。
因为p的所有权已经不属于我们了。
可以加上static,空间就还在
就是本来租的房间,到期后不属于我们了,返给房东了
房东可以把房子再租出去,我们没有权利管理了
加上static,他就不是在栈区了,变成了 静态数据,延长了寿命,所以能正常打印出hello,world
5. 运行结果是?(最易错,重点)
这里返回的是变量本身,返回变量本身,返回的时候会在寄存器上临时保存,再返回,虽然还是会销毁,但是销毁与否与寄存器无关
int test()
{
int a = 10;
return a;//这里返回的是变量本身,返回变量本身,返回的时候会在寄存器上临时保存,再返回,销毁与否与寄存器无关
}
int main()
{
int m = test();
printf("%d\n", m);
return 0;
}