动态内存分配

1.1 为什么使用动态内存分配

直接声明数组的方式的缺点:

  1. 声明数组必须指定长度限制.无法处理超过声明长度的数组.
  2. 如果声明更大的常量来弥补第一个缺点,会造成更多的内存浪费.

    3)如果输入数组的数据超过来数组的容纳范围,程序必须以一种合理的方式作出响应.但是程序员一般不会做这个判断.

1.2 malloc 和 free

malloc 和 free 分别用于执行动态分配内存和释放.

stdlib.h 中声明来这两个函数的原型

void *malloc( size_t size );

void free(void *point );

malloc 分配的是一块连续的内存,不会分开位于两块或多块不同的内存.不同的编译器可能会分配给你要求的或要求多一点的内存.比如你malloc(100),编译器可能分配100 或 105.

如果内存池空了,malloc 函数会向操作系统请求,要求得到更多的内存,并在这块内存上执行分配任务,如果操作系统无法向malloc提供更多的内存,会返回NULL指针.

所以要对每个从malloc返回的指针都进行检测,确保它非NULL.

free 的参数要么是NULL,要么是一个先前从malloc,calloc 或realloc返回的值.

11.3 calloc 和 realloc

  1. void *calloc( size_t num_elememts, size_t element_size);
  2. void realloc( void *ptr, size_t new_size );

calloc 也用于分配内存. calloc 相比 malloc 会自返回内存指针之前把它初始化为0. 参数需要包括所需元素的数量和每个元素的字节数.

realloc 用于修改原先已经分配的内存块的大小. 你可以扩大或者缩小使用的内存块.

如果原先的内存块无法改变大小,realloc 将分配另一块正确大小的内存卡,并把原先那块内存的内容复制到新块上.

使用realloc之后,就不能使用指向旧内存的指针,而要改用 realloc 所返回的新指针.

如果realloc 的第一个参数是 NULL,那么它的行为就和 malloc 一样.

11.4 使用动态内存分配

  1. //分配100个字节的内存
  2. int *pi;
  3. pi = malloc(100)
  4. if( pi == NULL ) {
  5. printf("Out of memory!\n");
  6. exit(1);
  7. }

这样就成功分配来100个字节,可以用来存储 25个 int,不过我们有更好的分配内存的写法,让代码的字面意义和我们想要存书的内容类型相匹配.

  1. pi = malloc(25 * sizeof(int) );

使用内存

  1. int *pi2, i;
  2. pi2 = pi;
  3. for( i = 0; i < 25; i += 1){
  4. *pi2++ = 0;
  5. }
  6. //或者
  7. int i;
  8. for (i = 0; i < 25; i += 1 )
  9. pi[i] = 0;

1.5 常见的内存错误

  1. NULL指针进行解引用
  2. 对分配的内存进行操作时越过边界
  3. 释放并非动态分配的内存
  4. 试图释放一块分配内存的一部分
  5. 试图使用被释放之后的内存块

针对忘记检查内存是否分配成功的解决方法

  1. //alloc.h
  2. //定义一个不易发生错误的内存分配器
  3. #include <stdlib.h>
  4. #define malloc 不要直接调用malloc!
  5. #define MALLOC(num ,type) (type *)alloc( (num) * sizeof(type) )
  6. extern void *alloc( size_t size );
  7. //alloc.c
  8. //不易发生错误的内存分配器的实现
  9. #include <stdio.h>
  10. #include "alloc.h"
  11. #undef malloc
  12. void * alloc( size_t size) {
  13. //请求所需的内存,并检查确实分配成功
  14. new_mem = malloc( size );
  15. if( new_mem == NULL ){
  16. printf("Out of memory!\n";
  17. exit(1);
  18. }
  19. return new_mem;
  20. }
  21. //a_client.c
  22. //使用
  23. #include "alloc.c";
  24. void function() {
  25. int *new_memory;
  26. //获得一串整型数的空间
  27. new_memory = MALLOC( 25, int );
  28. }

释放一块内存的一部分是不允许的

  1. pi = malloc( 10 *sizeof( int ) );
  2. free( pi + 5);//不被允许

不要访问已经被free函数释放了的内存.

很常见的情况,你对一个指向动态内存的指针进行了复制,而且这个指针的几份拷贝散布于程序各处,你无法保证当你使用一个指针时它所指向的内存是不是已被另一个指针释放.所以,必须保证程序中所有使用这块内存的地方在这块内存被释放之前停止对它的使用.

内存泄漏

动态分配的内存不使用时因该被释放,这样这块内存以后才可以被重新分配使用.分配的内存使用完毕之后不释放将引起内存泄漏.

1.6 内存分配的实例

内存分配一个常见的用途就是为那些长度在运行时才知道的数组分配内存空间.

  1. //读取、排序和打印一列整数
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. //该函数由qsort调用,用于比较整型值
  5. int compare_integers( void *a, void const *b ){
  6. register int const *pa = a;
  7. register int const *pb = b;
  8. return *pa > *pb ? 1 : *pa < *pb ? -1 : 0;
  9. }
  10. int main(){
  11. int *array;
  12. int n_values;
  13. int i;
  14. //观察共有多少个值
  15. printf("How many values are there?");
  16. if( scanf("%d", &n_values ) != 1 || n_value <= 0) {
  17. printf("Illegal number of values.\n");
  18. exit( EXIT_FAILURE );
  19. }
  20. //分配内存用于存储这些值
  21. array = malloc(n_values * sizeof( int ) );
  22. if(array == NULL ){
  23. printf("Can't get memory for that many values.\n");
  24. eixt( EXIT_FAILURE );
  25. }
  26. //读取这些值
  27. for( i = 0; i < n_values; i += 1 ) {
  28. printf("? ");
  29. if(scanf("%d", array + i ) != 1 ){
  30. printf("Error reading value #%d\n, i );
  31. free( array );
  32. exit(EXIT_FAILURE);
  33. }
  34. }
  35. //对这些值排序
  36. qsort( array, n_values, sizeof( int ), compare_integers );
  37. //打印这些值
  38. for(i = 0; i < n_values;i += 1)
  39. printf( "%d\n", array[i] );
  40. //释放内存并退出
  41. free( array );
  42. return EXIT_SUCCESS;
  43. }
  1. //用动态分配内存制作一个字符串的一份拷贝. 注意:调用程序应该负责检查这块内存是否分配成功,这样作允许调用程序以任何它所希望的方式对错误作出反应
  2. #include <stdlib.h>
  3. #include <string.h>
  4. char strdup( char const *string ){
  5. char *new_string;
  6. //请求足够长的内存,用于存储字符串和它的结尾NULL字节
  7. new_string = malloc( strlen( string ) + 1 );
  8. //如我我们得到内存,就复制字符串
  9. if( new_string != NULL )
  10. strcpy(new_string, string );
  11. return new_string;
  12. }

这个函数非常方便,也非常有用.尽管标准没有提及,但许多环境都把它作为函数库的一部分.

存货系统的声明:

  1. //inventor.c
  2. //存货记录的声明
  3. //包含零件专用信息的结构
  4. typedef struct {
  5. int cost;
  6. int supplier;
  7. //其他信息...
  8. } Partinfo;
  9. //存储装配件专用信息的结构
  10. typedef struct {
  11. int n_parts;
  12. struct SUBASSYPART {
  13. char partno[10];
  14. short quan;
  15. } *part;
  16. } Subassyinfo;
  17. //存货记录结构,它是一个变体记录
  18. typedef struct {
  19. char partno[10];
  20. int quan;
  21. enum {PART, SUBASSY } type;
  22. union {
  23. Partinfo *part;
  24. Subassyinfo *subassy;
  25. } info;
  26. } Invrec;

动态创建变体记录

  1. //invertor.c
  2. //用于创建 SUBASSEMBLY( 装配件) 存货记录的函数
  3. #include <stdlib.h>
  4. #include <stdio.h>
  5. #include "invertor.h"
  6. Invrec *create_subassy_record( int n_parts){
  7. Inverc *new_rec;
  8. //试图为Invrec 部分分配内存
  9. new_rec = malloc( sizeof ( Invrec ) );
  10. if( new_rec != NULL) {
  11. //内存分配成功,现在存储SUBASSYINFO 部分
  12. new_rec->info.subassy = malloc( sizeof( Subassinfo ) );
  13. if (new_rec->info.subassy != NULL){
  14. //为零件获取一个足够大的数组
  15. new_rec->info.subassy->part = malloc( n_parts * sizeof( struct SUBASSYPART ) );
  16. if( new_rec->info.subassy->part != NULL ){
  17. //获取内存,天聪我们知道的字段,然后返回
  18. new_rec->type = SUBASSY;
  19. new_rec->info.subassy->n_parts = n_parts;
  20. return new_rec;
  21. }
  22. //内存已用完,释放我们原先分配的内存
  23. free( new_rec -> info.subassy );
  24. }
  25. free( new_rec );
  26. }
  27. return NULL;
  28. }

变体记录的销毁

  1. //释放存货记录的函数
  2. #include <stdlib.h>
  3. #include "inventor.h"
  4. void discard_inventory_record( Invrec *record ) {
  5. //删除记录中的变体部分
  6. switch(record->type){
  7. case SUBASSY:
  8. free( record->info.subassy->part );
  9. free( record->info.subassy );
  10. break;
  11. case PART:
  12. free( record->info.part );
  13. break;
  14. }
  15. // 删除记录的主题部分
  16. free( record );
  17. }

更高效的释放记录的方法,不区分变体部分的零件和装配部件. 因为free 函数不区分指向联合指针内部成员的类型. 换句话说,free接受一个合法指针就会正确释放内存

  1. if(record->type == SUBASSY )
  2. free( record->info.subassy->part );
  3. free( record->info.part );
  4. free( record );

C和C指针小记(十六)-动态内存分配的更多相关文章

  1. C和指针 第十一章 动态内存分配

    声明数组时,必须指定数组长度,才可以编译,但是如果需要在运行时,指定数组的长度的话,那么就需要动态的分配内存. C函数库stdlib.h提供了两个函数,malloc和free,分别用于执行动态内存分配 ...

  2. C++学习笔记(十一):void*指针、类型转换和动态内存分配

    void*指针 void关键字表示“空类型”的概念.但是,这里的“空类型”不表示“任意类型”,而是表示不存在的意思,也就是说C/C++不允许你写语句void a,不存在类型为void的东西. void ...

  3. <c和指针>学习笔记5动态内存分配和预处理器

    1 动态内存 比如声明数组得时候,我们需要提前预估数组长度,分配大了浪费,少了就更不好操作了.从而引入动态分配,需要的时候再分配. (1)malloc和free void *malloc(size_t ...

  4. C++指针和动态内存分配

    指针和动态内存分配 数组与指针 数组 数组名是一个指针常量. 数组名传递数据时,传递的是地址. 数组作为函数参数时不指定第一维大小. 对象数组 A a[2] = {A(1,2)}; 执行时先调用有参数 ...

  5. 《C和指针》 读书笔记 -- 第11章 动态内存分配

    1.C函数库提供了两个函数,malloc和free,分别用于执行动态内存分配和释放,这些函数维护一个可用内存池. void *malloc(size_t size);//返回指向分配的内存块起始位置的 ...

  6. C动态内存分配(C与指针实例)

    主要初步介绍malloc.free.calloc.realloc的基本.日后会有更详细的内容. malloc.free分别用于动态内存分配和释放. malloc会从内存池里提取一块合适的内存(连续的) ...

  7. 数据结构基础——指针及动态内存分配(malloc)

    一.指针 C语言中的指针是一种数据类型,比如说我们用int *a;就定义了一个指针a,它指向一个int类型的数.但是这个指针是未初始化的,所以,一般的,我们都在创建指针时初始化它,以免出错,在还不吃的 ...

  8. 《C++ Primer Plus》读书笔记之十—类和动态内存分配

    第12章 类和动态内存分配 1.不能在类声明中初始化静态成员变量,这是因为声明描述了如何分配内存,但并不分配内存.可以在类声明之外使用单独的语句进行初始化,这是因为静态类成员是单独存储的,而不是对象的 ...

  9. c++ 动态数组,指针与动态内存分配

    教学内容: 内存的使用 动态内存分配malloc函数 分配内存时使用sizeof运算符 用指针访问内存 以数组的形式访问内存 一.内存的使用 堆(heap) 在程序执行期间分配内存时,内存区域中的这个 ...

随机推荐

  1. FileClassify文件日期分类工具

    FileClassify是一款免费的文件按日期分类工具,能够根据文件修改日期,将文件移动或复制到对应的目录中 如果对您有较大的帮助,欢迎捐赠我们,我们对您表示衷心的感谢! 1.输入文件夹和输出文件可以 ...

  2. Eclipse和MyEclipse使用技巧--解决MyEclipse中的js报错的小方法

    今天,下了个模版,但是导进去的时候发现js会报错.看了下其他都没有错误.而有一个js报错误,请原谅我有点红色强迫症,不能留一点红色 . 错误如下:Syntax error on token " ...

  3. window炫丽cmd的别名cmder

    windows下的cmd,色彩度用起来不爽,cmder是对cmd的补充,界面很清爽 01.下载 https://github.com/cmderdev/cmder http://cmder.net/ ...

  4. Python并行实例

    任务 def single(): # 单进程单线程实现 s = 0 for i in range(1, N): s += math.sqrt(i) return s 结论 Python多线程无法利用多 ...

  5. 使用viewport中的vm来适配移动端页面

    前言 作为一个小前端,经常要和H5打交道,这就面临着不同终端的适配问题. Flexible方案通过Hack手段来根据设备的dpr值相应改变<meta>标签中viewport的值,给我更贴切 ...

  6. Troubleshooting Scheduler Autotask Issues (Doc ID 1561498.1)

    In this Document   Purpose   Troubleshooting Steps   References APPLIES TO: Oracle Database - Enterp ...

  7. Spring-boot之 swagger2

    Swagger是一个简单但功能强大的API表达工具. 结合springboot 配置起来很简单,附上教程 :https://www.xncoding.com/2017/07/08/spring/sb- ...

  8. Tensorflow学习资源

    https://tensorflow.google.cn/ 中文官网 https://www.w3cschool.cn/tensorflow_python/tensorflow_python-gnwm ...

  9. 磁盘 I/O 优化

    磁盘 I/O 优化 1. 性能检测 我们的应用程序通常都需要访问磁盘系统,而磁盘 I/O 通常都很耗时, 要判断 I/O 是否是一个瓶颈,有一些参数指标可以参考. 我们可以压力测试应用程序看系统的 I ...

  10. mac 互传文件

    搭建HTTP服务,然后局域网访问就行 PHP方式: php -S 172.21.205.xxx:9999 Python python -m SimpleHTTPServer 8001 在浏览器访问:h ...