网站无法连接到服务器,销售类网站开发,怎么制作网站下载软件,网站建设合同百度文库目录
一.动态内存函数
1.malloc
2.free
3.calloc
4.malloc和calloc的区别
5.realloc
二.动态内存分配的常见错误
1.对null进行解引用操作
2.对动态开辟空间的越界访问
3.对非动态开辟内存使用free释放
4.使用free释放动态开辟内存的一部分
5.对同一块动态内存多次…目录
一.动态内存函数
1.malloc
2.free
3.calloc
4.malloc和calloc的区别
5.realloc
二.动态内存分配的常见错误
1.对null进行解引用操作
2.对动态开辟空间的越界访问
3.对非动态开辟内存使用free释放
4.使用free释放动态开辟内存的一部分
5.对同一块动态内存多次释放
6.动态开辟内存忘记释放内存泄漏
三.习题讲解
1.代码找错
1内存泄漏
2返回栈空间地址的问题
3非法访问内存
四.柔性数组 一.动态内存函数 1.栈区(stack):在执行函数时函数内局部变量的存储单元都以在栈上创建函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中效率很高但是分配的内存容量有限。栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。 2.堆区(heap):一般由程序员分配释放若程序员不释放程序结束时可能由OS(操作系统)回收。分配方式类似于链表。3.数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放 实际上普通的局部变量是在栈区分配空间的栈区的特点是在上面创建的变量出了作用域就销毁 但是被static修饰的变量存放在数据段(静态区)数据段的特点是在上面创建的变量直到程序结束才销毁所以生命周期变长 4.代码段:存放函数体(类成员函数和全局函数)的二进制代码。 动态内存分配是在堆区进行的 int val 20;//在栈空间上开辟四个字节 char arr [ 10 ] { 0 }; // 在栈空间上开辟 10 个字节的连续空间开辟空间的方式有两个特点 1. 空间开辟大小是固定的。 2. 数组在申明的时候必须指定数组的长度它所需要的内存在编译时分配 但是对于空间的需求不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道数组的编译时开辟空间的方式就不能满足了这时候就只能试试动态内存开辟 例如 虽然C语言是可以支持变长数组--c99中增加了但是很多编译器是不支持c99的所以变长数组没有办法使用即arr[n],所以已有的分配内存空间的方式是局限的所以要进行动态内存分配
struct S
{char name[20];int age;
}int main()
{int n0;scanf(%d,n);struct S arr[n];return 0;
}1.malloc void* malloc (size_t size) 动态内存开辟 这个函数向内存申请一块 连续可用 的空间并返回指向这块空间的指针。 如果开辟成功则返回一个指向开辟好空间的指针。 如果开辟失败则返回一个 NULL 指针因此 malloc 的返回值一定要做检查。 返回值的类型是 void* 所以 malloc 函数并不知道开辟空间的类型具体在使用的时候使用者自己来决定。 如果参数 size 为 0 malloc 的行为标准是未定义的取决于编译器。 malloc包含的几个要素
//1.
#includestdlib.h
int main()
{
//2.int* p(int*)malloc(10*sizeof(int));//malloc是void*型,所以要进行强制类型转换但是在Gcc环境下或者说linux环境下是不需要进行转换的
}
代码如下(还没有回收释放空间)
#includestdio.h
#includestdlib.h
#includeerrno.h
#includestring.h
int main()
{int* p(int*)malloc(10*sizeof(int));if(pNULL)printf(%s,strerror(errno));//开辟空间失败可以用strerror显示错误结果else{for(int i0;i10;i){//把每个元素打印出来 *(pi)i;}for(int i0;i10;i){printf(%d,*(pi));} }return 0;
} 如果将其中的 int* p(int*)malloc(10*sizeof(int)); 改为 int* p(int*)malloc(10*sizeof(INT_MAX)); 系统会报错错误信息为not enough space
1INT_MAX INT_MAX 是 C 中 climits 头文件中定义的一个宏用于表示 int 类型的最大值。该宏在 C 和 C 中都可以使用他不是数据类型。
如果想正确使用INT_MAX开辟空间代码如下
//在C中使用new开辟一块新的空间
#include iostream
#include climits // 包含 INT_MAX 的头文件using namespace std; // 引入命名空间int main() {int* p new int[10]; // 使用 new 关键字动态分配内存for (int i 0; i 10; i) {p[i] INT_MAX; // 给每个元素赋值为 INT_MAX}for (int i 0; i 10; i) {cout p[i] ;}cout endl;delete[] p; // 释放动态分配的内存return 0;
}2.free C语言提供了另外一个函数free专门是用来做动态内存的释放和回收的堆区函数原型如下 void free ( void* ptr ); free 函数用来释放动态开辟的内存。 如果参数 ptr 指向的空间不是动态开辟的那 free 函数的行为是未定义的。 如果参数 ptr 是 NULL 指针则函数什么事都不做。 完整代码如下
#includestdio.h
#includestdlib.h
#includeerrno.h
#includestring.h
int main()
{int* p(int*)malloc(40);if(pNULL)printf(%s,strerror(errno));//开辟空间失败可以用strerror显示错误结果else{for(int i0;i10;i){//把每个元素打印出来 *(pi)i;}for(int i0;i10;i){printf(%d,*(pi));}}//当动态申请的空间不再使用的时候//就应该还给操作系统free(p);//即使我们将p还给了操作系统但是p依然指向这块空间,所以要进行pNULLpNULL;//使p不指向这块内存空间return 0;
} 3.calloc calloc 函数也用来动态内存分配。原型如下void* calloc ( size_t num , size_t size ); 函数的功能是为 num 个大小为 size 的元素开辟一块空间并且把空间的每个字节初始化为 0 。 与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全 0 calloc的几个要素
//1.
#includestdlib.h
#includemalloc.h
int main()
{
//2.
//malloc(10*sizeof(int))int *p(int*)calloc(10,sizeof(int));return 0;}
代码如下
#includestdio.h
#includestdlib.h
#includeerrno.h
#includestring.h
#includemalloc.h
int main()
{int* p(int*)calloc(10,sizeof(int));if(pNULL)printf(%s,strerror(errno));//开辟空间失败可以用strerror显示错误结果else{for(int i0;i10;i){//把每个元素打印出来 *(pi)i;}for(int i0;i10;i){printf(%d,*(pi));} }free(p);//free函数是用来释放动态开辟的空间的pNULL;return 0;
} 4.malloc和calloc的区别
1.malloc和calloc的开辟空间形式不同
2.calloc会初始化空间为0而malloc不会初始化。所以malloc开辟空间效率更高但不会将空间的每个字节初始化为0 5.realloc realloc 函数的出现让动态内存管理更加灵活。 有时会我们发现过去申请的空间太小了有时候我们又会觉得申请的空间过大了那为了合理的时候内存我们一定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小的调整。 函数原型如下void* realloc ( void* ptr , size_t size ); ptr 是要调整的内存地址 size 调整之后新大小 返回值为调整之后的内存起始位置。 这个函数调整原内存空间大小的基础上还会将原来内存中的数据移动到 新 的空间。 realloc 在调整内存空间的是存在两种情况 情况 1 原有空间之后有足够大的空间 对策要扩展内存就直接原有内存之后直接追加空间原来空间的数据不发生变化 情况 2 原有空间之后没有足够大的空间 对策在堆空间上另找一个合适大小的连续空间来使用。这样函数返回的是一个新的内存地址。 代码如下
#includestdio.h
#includestdlib.h
#includeerrno.h
#includestring.h
int main()
{int* p(int*)malloc(20);if(pNULL)printf(%s,strerror(errno));//开辟空间失败可以用strerror显示错误结果else{for(int i0;i10;i){//把每个元素打印出来 *(pi)i;}}//假设这里20个字节的空间不够用了,我们希望使用40个字节的空间//这时候就可以使用realloc来调整动态开辟的空间int* p2realloc(p,40);for(int i5;i10;i){*(p2i)i;}for(int i0;i10;i){printf(%d,*(p2i));}return 0;
} 在这里用了p,p2两个指针指向不同的内存空间这样p就不是统一管理所有的空间所以这样做不对追加空间有有以下两种情况
1.开辟的空间后面正好有足够的空间能够追加内存开辟内存空间后也是p指向内存首元素返回的是p旧的指针 2.如果开辟的空间后没有足够的空间那么就重新开辟一块新的空间把原来的地址里面的数据拷贝到新开辟的更大的空间中 第一种方法返回的是旧的地址 第二种方法返回的是不同的新开辟的内存空间地址旧的空间free() 注意 int* prealloc(p,40); 如果realloc内存开辟失败返回NULL(空指针)那么p原来开辟的空间也找不到了,所以不能赋值到原来开辟的内存空间应该这样写 int* ptrrealloc(p,40);//用新变量接收realloc的返回值
if(ptr!NULL)
{pptr;//仍然用p维护新的内存
}
int i0;完整代码如下
#includestdio.h
#includestdlib.h
#includeerrno.h
#includestring.h
int main()
{int* p(int*)malloc(20);if(pNULL)printf(%s,strerror(errno));//开辟空间失败可以用strerror显示错误结果else{for(int i0;i5;i){//把每个元素打印出来 *(pi)i;}}//假设这里20个字节的空间不够用了,我们希望使用40个字节的空间//这时候就可以使用realloc来调整动态开辟的空间int* p2realloc(p,40);if(p2!NULL){pptr;for(i5;i10;i){*(pi)i;}for(i0;i10;i){printf(%d,*(pi));}}free(p);pNULL;return 0;
} 二.动态内存分配的常见错误
1.对null进行解引用操作
int main()
{int *p(int*)malloc(40);//万一解引用失败,p就被赋值为null*p0//err;int i0;for(int i0;i10;i){*(pi)i; }free(p);pNULL;return 0;}
所以在解引用操作前要作出判断
int main()
{int *p(int*)malloc(40);//万一解引用失败,p就被赋值为nullif(p!NULL){*p0//err;int i0;for(int i0;i10;i){*(pi)i; }free(p);pNULL;return 0;}
} 2.对动态开辟空间的越界访问
int main()
{int *p(int*)malloc(5*sizeof(int));if(pNULL)return 0;else {for(int i0;i10;i)//本来只有5个整型元素,访问10个的话会越界{*(pi)i;}}//free(p);pNULL;return 0;
} 3.对非动态开辟内存使用free释放
int a10;
int* pa;
*p20;
free(p);
pNULL;a的空间是在栈区存放的程序会出错 4.使用free释放动态开辟内存的一部分
#includestdio.h
{
int *p(int*)malloc(40);
if(pNULL)
{return 0;
}
int i0;
for(i0;i10;i)
{*pi;
}
free(p);
pNULL;//在这里p已经变化了不是最初指向的空间了
return 0;
} 代码应该改为
for(i0;i10;i)
{*(pi)i;
} 5.对同一块动态内存多次释放
int *p(int*)malloc(40);
if(pNULL)return 0;
else{....free(p);free(p);pNULL;return 0;
}
或者这样,这样不会报错每次释放完后p所指向的地址置为空指针
free(p);
pNULL;
free(p);
6.动态开辟内存忘记释放内存泄漏
while(1)
{malloc(1);sleep(1000);
}
return 0; 三.习题讲解
1.代码找错
1内存泄漏 这里有几个错误
(1)调用完GetMemory之后p是GetMemory中的一个形参变量p在这个函数内有效出了这个函数之后就无效了等GetMemory函数返回之后动态开辟内存尚未释放并且无法找到所以会造成内存泄漏所以在这里str还是空指针不是有效的地址所以 strcpy(str,hello world);//str并没有指向有效的地址而是一块空指针 2没有free(),会出现内存泄露问题
void GetMemory(char **p)//对char *的地址解引用就是**p,p中存放的是str的地址,那么*p就是str
{*p(char *)malloc(100);
}void Test(void)
{char *strNULL;GetMemory(str);strcpy(str,hello world);printf(str);free(str);strNULL;
}int main()
{Test();return 0;
}
或者
char* GetMemory(char *p)//对char *的地址解引用就是**p,p中存放的是str的地址,那么*p就是str
{p(char *)malloc(100);return p;
}void Test(void)
{char *strNULL;strGetMemory(str);strcpy(str,hello world);printf(str);free(str);strNULL;
}int main()
{Test();return 0;
} 2返回栈空间地址的问题 1strGerMemory();//确实将返回值放到了str中但是执行完该代码后p的空间就还给操作系统了所以当printf(str)打印时str指向哪块空间就不清楚了同理
int *test()
{int a10;return a;
}int main()
{int *ptest();//非法访问内存空间*p20;return 0;}
可以改为
int *test()
{//int a10;//栈区static int a10;//将a放在静态区中栈空间的地址返回存在风险但是静态区不会return a;
}int main()
{int *ptest();*p20;return 0;}
也可以写为
int* test()
{int *ptrmalloc(100);//在堆区,如果不free依然存在return ptr;}int main()
{int *ptest();return 0;} 3非法访问内存
篡改动态内存区的内容后果难以预料非常危险。 因为free(str);之后str成为野指针if(str!NULL)语句不起作用
void test(void)
{char *str(char*)malloc(100);strcpy(str,hello);free(str);//虽然str开辟的区域已经还给操作系统了,但是str还是指向这块区域if(str!NULL)//这里判断为真world覆盖hello,打印了world,但是这块空间已经被释放了不能使用了但是还是打印了world非法访问{strcpy(str,world);printf(str);}
}int main()
{test();return 0;
}
代码修改为
void test(void)
{char *str(char*)malloc(100);strcpy(str,hello);free(str);//虽然str开辟的区域已经还给操作系统了,但是str还是指向这块区域strNULL;//这样下面的判断(str!NULL)才有意义if(str!NULL){strcpy(str,world);printf(str);}
}int main()
{test();return 0;
} 四.柔性数组 C99中结构中的最后一个元素允许是未知大小的数组这就叫做[柔性数组]成员 运用柔性数组
struct S
{int n;int arr[];//未知大小的,柔性数组成员数组大小是可以改变的
}int main()
{struct S s;printf(%d\n,sizeof(s));//结果为4struct S* ps(struct S*)malloc(sizeof(struct S)5*sizeof(int));
//为arr开辟了5个int型的地址空间ps-n100;for(int i0;i5;i){ps-arr[i]i//0 1 2 3 4 5 }Struct *ptrrealloc(ps,44);//原来是24个字节,现在是44个字节多个5个整型变量if(ptr!NULL)psptr;for(int i5;i10;i){ps-arr[i]i;}for(int i0;i10;i){printf(%d,ps-arr[i]); }free(ps);psNULL;return 0;
} 另一种写法不适用柔性数组开辟空间的方式有一些区别但是总体得到的结果相同
struct S
{int n;int* arr;}int main()
{struct S*ps (struct S*)malloc(sizeof(struct S));ps-arrmalloc(5*sizeof(int));int i0;for(i0;i5;i){ps-arr[i]i;}for(i0;i5;i){printf(%d,ps-arr[i]); }int *ptrrealloc(ps-arr,10*sizeof(int));if(ptr!NULL)ps-arrptr;for(int i5;i10;i)ps-arr[i]i;for(int i0;i10;i)printf(%d,ps-arr[i]);//释放内存,注意先后顺序不能改变free(ps-arr);ps-arrNULL;free(ps);psNULL;return 0;
} 柔性数组的优点第一种对于第二种而言的优点 1.第二种运用了两次malloc就要使用两次free(),出错概率更高 2.柔性数组相当于第二种方法内存碎片更少了内存利用率更高 3.柔性数组开辟的空间内存是连续的访问效率更高而第二种方法不是连续的