网站建设qq群,怎么找到php网站的首页面html,网站定制开发什么意思,国内wordpress主机推荐为什么要动态内存分配#xff1f; 之前#xff0c;我们向内存申请空间#xff0c;有两种方式#xff0c;一种是定义变量#xff0c;一种是建立数组#xff1b;但是#xff0c;这两种方式都有缺陷#xff0c;创建的空间大小是固定的#xff0c;在程序的运行过程中… 为什么要动态内存分配 之前我们向内存申请空间有两种方式一种是定义变量一种是建立数组但是这两种方式都有缺陷创建的空间大小是固定的在程序的运行过程中不能随着我们的需要改变而改变这就需要我们申请动态内存了 1. 动态内存函数 1.1 malloc和free
void* malloc (size_t size);函数功能 开辟一块size字节大小的空间如果开辟成功返回开辟空间首地址如果开辟失败返回NULL如果size是0标准未定义取决于编译器由于返回值是void*类型因此返回的地址需要我们另做处理 void free (void* ptr);函数功能 释放ptr指向的空间前提是ptr指向的空间是动态开辟的如果ptr指向的空间不是动态开辟的编译器会报错如果ptr为NULL则什么都不做另外释放完空间ptr此时是一个野指针需要置空 动态内存函数要和free一同使用动态开辟的空间有两种方式释放 free主动释放程序结束操作系统会帮我们回收 虽然程序结束申请的动态空间也会被回收但如果程序在退出之前开辟了多处动态内存而没有释放又去开辟动态内存很可能会导致内存泄漏因此每次申请的动态内存都要记得free释放 int main()
{int* p (int*)malloc(10 * sizeof(int));if (p NULL){perror(malloc);return 1;}for (int i 0; i 10; i){p[i] i;}for (int i 0; i 10; i){printf(%d , p[i]);}free(p);p NULL;return 0;
}
//输出0 1 2 3 4 5 6 7 8 9内存非为三大部分动态内存函数申请的空间是在堆上开辟的
1.2 calloc函数
void* calloc (size_t num, size_t size);函数功能 num是开辟元素的个数size是每个元素的大小开辟num*size字节的空间calloc开辟空间后会将空间自动初始化为0 1.3 realloc函数
void* realloc (void* ptr, size_t size);动态开辟的内存用完了想进行增容这时就可以考虑使用realloc 函数功能 ptr是要进行扩容的地址size为新的空间大小返回新空间的地址如果ptr为空此时就相当于malloc函数 开辟空间有两种情况 ptr后面的空间够存放新空间的大小此时直接在ptr后面的空间扩容ptr后面的空间不够存放新空间的大小此时会另开辟一块size大小的空间并把原数据拷贝到新空间释放掉旧空间返回新空间的地址 int main()
{int* p (int*)malloc(10 * sizeof(int));if (p NULL){perror(malloc);return 1;}//...//想扩容到100个int的大小//写法1p realloc(p, 100 * sizeof(int));//写法2int* ptr (int*)realloc(p, 100 * sizeof(int));if (ptr NULL){perror(realloc);return 1;}p ptr;//...free(p);p NULL;return 0;
}用realloc开辟完空间更推荐使用写法2因为realloc开辟空间可能会失败此时返回NULL不仅没有扩容成功还把原来的空间给弄没了
2.动态内存常见的错误 2.1对空指针进行解引用 这种情况通常是因为使用完动态内存函数没有对返回值进行检查 2.2对动态内存的越界访问
int main()
{int* p (int*)malloc(10 * sizeof(int));if (p NULL){perror(malloc);return 1;}for (int i 0; i 10; i){*(p i) i;}free(p);p NULL;return 0;
}i10的内存已经不属于我们的了这种情况编译器是不会报错的需要我们自己擦亮眼睛
2.3对非动态内存使用free
int main()
{int p[10] { 0 };for (int i 0; i 10; i){p[i] i;}free(p);return 0;
}p指向的空间不是动态开辟的不能进行free释放
2.4使用free释放一部分动态内存
int main()
{int* p (int*)malloc(10 * sizeof(int));if (p NULL){perror(malloc);return 1;}for (int i 0; i 5; i){*p i;p;}free(p);p NULL;return 0;
}p最终指向动态内存的一部分free§只释放了一部分最终仍有可能造成内存泄漏
2.5对一块动态内存多次释放
int main()
{int* p (int*)malloc(10 * sizeof(int));if (p NULL){perror(malloc);return 1;}//...free(p);//...free(p);p NULL;return 0;
}前面说过free指向的空间必须是动态内存第二次free时p指向的空间已经不是动态的了
2.6忘记释放动态内存
void Print(int n)
{int* p (int*)malloc(n * sizeof(int));if (p NULL){perror(malloc);return;}if (n 3)return;free(p);p NULL;
}int main()
{Print(3);return 0;
}上面的代码写了free且置空看似没有问题但在执行free之前函数已经返回不会执行free因此没有释放成功 内存泄漏是非常严重的问题在日常写代码的过程中一定要注意动态开辟的内存要记得释放 3.笔试题讲解 题目1
void GetMemory(char* p)
{p (char*)malloc(100);
}void Test(void)
{char* str NULL;GetMemory(str);strcpy(str, hello world);printf(str);
}int main()
{Test();return 0;
}上述代码的运行结果是什么 由于p是str的一份临时拷贝出了GetMemory函数就销毁了malloc开辟出来的空间就找不到了导致内存泄漏执行完GetMemory函数str仍然是NULL在strcpy函数中会对str解引用对空指针进行解引用最终导致程序崩溃 怎么修改上述代码让它达到我们想要的功能
char* GetMemory()
{char* p (char*)malloc(100);return p;
}void Test(void)
{char* str NULL;str GetMemory();strcpy(str, hello world);printf(str);free(str);str NULL;
}int main()
{Test();return 0;
}题目2
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接受str就变成了野指针打印的是一堆乱码这是一种返回栈空间地址的问题 上面的代码也可以简化为
int* Test()
{int a 10;return a;
}int main()
{int* p Test();printf(%d\n, *p);//10return 0;
}同样是犯了返回栈空间地址的错误我们发现该代码输出是正常的这是为什么 虽然结果正确但这并不代表代码没有问题结果正确的原因是Test函数即使销毁了p位置处的值仍没有被修改因此误打误撞结果是对的 题目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);
}int main()
{Test();return 0;
}该代码正常打印唯一的缺点就是少了释放内存存在内存泄漏的问题 题目4
void Test(void)
{char* str (char*)malloc(100);strcpy(str, hello);free(str);if (str ! NULL){strcpy(str, world);printf(str);}
}int main()
{Test();return 0;
}str指向的空间释放后没有置空str此时是野指针对野指针进行了访问非法访问内存空间 题目5
int* Test()
{int* p;*p 10;return p;
}创建p时没有对其初始化p为随机值随机指向一块空间是野指针对野指针进行了操作 4.C/C的内存区域 栈区执行函数时函数内的局部变量都是在该区域创建的当函数结束时自动销毁创建的区域堆区动态开辟的空间在该区域创建通常由程序员自己释放当程序结束时也会由操作系统自动回收数据段存放全局变量静态数据程序结束由系统释放代码段存放函数体的二进制代码 6.柔性数组 定义在结构体中最后一名成员是数据且数组的大小未知我们把该数组叫做柔性数组 struct S
{char c;int i;int arr[];
};6.1柔性数组的特点 柔性数组必须在结构体当中必须是最后一名成员柔性数组前面至少有一名成员该结构体的大小不包括数组的大小必须用动态内存函数对结构体开辟空间且开辟空间的大小要大于结构体的大小确保柔性数组有有一定的空间 6.2柔性数组的使用
//代码1
struct S
{char c;int i;int arr[];
};int main()
{struct S* p;p (struct S*)malloc(sizeof(struct S) 20);if (p NULL){perror(malloc);return 1;}p-c a;p-i 5;for (int i 0; i 5; i){p-arr[i] i;}//空间不够了进行增容struct S* ptr (struct S*)realloc(p, sizeof(struct S) 40);if (ptr NULL){perror(realloc);return 1;}p ptr;ptr NULL;for (int i 0; i 10; i){p-arr[i] i;}free(p);p NULL;return 0;
}实际上不用柔性数组也能完成上面的操作
//代码2
struct S
{char c; int i; int* arr;
};int main()
{struct S* ps (struct S*)malloc(sizeof(struct S));if (ps NULL) {perror(malloc);return 1;}int* ptr (int*)malloc(sizeof(int) * 5);if(ptr NULL){perror(malloc);return 1;}ps-arr ptr;ptr NULL;for (int i 0; i 5; i){ps-arr[i] i;}//增容int* p (int*)realloc(ps-arr, sizeof(int) * 10);if (p NULL){perror(malloc);return 1;}ps-arr p;p NULL;for (int i 0; i 10; i){ps-arr[i] i;}free(ps-arr);ps-arr NULL;free(ps);ps NULL;
}那么使用柔性数组的代码1相较于代码2有什么优势呢 方便内存的释放使用柔性数组只需要释放一次内存空间而代码2你必须先将结构体成员开辟的空间释放后才能释放结构体多释放意味着风险越多 动态内存的内容就到这