动态内存分配

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

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

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

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

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

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

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

11.4 使用动态内存分配

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

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

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

使用内存

int *pi2, i;
pi2 = pi;
for( i = 0; i < 25; i += 1){
*pi2++ = 0;
}
//或者
int i;
for (i = 0; i < 25; i += 1 )
pi[i] = 0;

1.5 常见的内存错误

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

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

//alloc.h
//定义一个不易发生错误的内存分配器
#include <stdlib.h>
#define malloc 不要直接调用malloc!
#define MALLOC(num ,type) (type *)alloc( (num) * sizeof(type) )
extern void *alloc( size_t size );
//alloc.c
//不易发生错误的内存分配器的实现
#include <stdio.h>
#include "alloc.h"
#undef malloc
void * alloc( size_t size) {
//请求所需的内存,并检查确实分配成功
new_mem = malloc( size );
if( new_mem == NULL ){
printf("Out of memory!\n";
exit(1);
}
return new_mem;
}
//a_client.c
//使用
#include "alloc.c";
void function() {
int *new_memory;
//获得一串整型数的空间
new_memory = MALLOC( 25, int );
}

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

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

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

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

内存泄漏

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

1.6 内存分配的实例

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

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

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

存货系统的声明:

//inventor.c
//存货记录的声明
//包含零件专用信息的结构
typedef struct {
int cost;
int supplier;
//其他信息...
} Partinfo; //存储装配件专用信息的结构
typedef struct {
int n_parts;
struct SUBASSYPART {
char partno[10];
short quan;
} *part;
} Subassyinfo; //存货记录结构,它是一个变体记录
typedef struct {
char partno[10];
int quan;
enum {PART, SUBASSY } type;
union {
Partinfo *part;
Subassyinfo *subassy;
} info;
} Invrec;

动态创建变体记录

//invertor.c
//用于创建 SUBASSEMBLY( 装配件) 存货记录的函数
#include <stdlib.h>
#include <stdio.h>
#include "invertor.h"
Invrec *create_subassy_record( int n_parts){
Inverc *new_rec;
//试图为Invrec 部分分配内存
new_rec = malloc( sizeof ( Invrec ) );
if( new_rec != NULL) {
//内存分配成功,现在存储SUBASSYINFO 部分
new_rec->info.subassy = malloc( sizeof( Subassinfo ) );
if (new_rec->info.subassy != NULL){
//为零件获取一个足够大的数组
new_rec->info.subassy->part = malloc( n_parts * sizeof( struct SUBASSYPART ) );
if( new_rec->info.subassy->part != NULL ){
//获取内存,天聪我们知道的字段,然后返回
new_rec->type = SUBASSY;
new_rec->info.subassy->n_parts = n_parts;
return new_rec;
}
//内存已用完,释放我们原先分配的内存
free( new_rec -> info.subassy );
}
free( new_rec );
}
return NULL;
}

变体记录的销毁

//释放存货记录的函数
#include <stdlib.h>
#include "inventor.h"
void discard_inventory_record( Invrec *record ) {
//删除记录中的变体部分
switch(record->type){
case SUBASSY:
free( record->info.subassy->part );
free( record->info.subassy );
break;
case PART:
free( record->info.part );
break;
}
// 删除记录的主题部分
free( record );
}

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

if(record->type == SUBASSY )
free( record->info.subassy->part );
free( record->info.part );
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. 转【微信小程序 四】二维码生成/扫描二维码

    原文:https://blog.csdn.net/xbw12138/article/details/75213274 前端 二维码生成 二维码要求:每分钟刷新一次,模拟了个鸡肋,添加了个按分钟显示的时 ...

  2. MVC5 IIS7 403错误

    问题背景 MVC5部署到IIS7中显示403错误. 解决方案 <system.webServer> → <modules> 节点下的 ApplicationInsightsWe ...

  3. ASP.NET CORE 中用单元测试测试控制器

    之前用ASP.NET CORE做的项目 加了一个新功能,数据库加了个字段balabala.... 更新到服务器上,新功能测试正常,然后就没管了..... 今天客户说网站有BUG,某个页面打开后出错了, ...

  4. 科普知识普及 - 桥接VS中继

    首先要说明一个很多人理解的误区,中继比桥接好用,真的是这么回事么? 答案是否定的. 我们在说桥接和中继的时候我们要先了解,桥接和中继的工作原理.还有一个问题,估计很多人都想不明白:为什么中继搜到的信号 ...

  5. .net Core 生产环境报错 MIME

    because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled. 主要没 ...

  6. 优化实现Mobile Diffuse动态直接光照shader

    项目中美术使用了Unity自带的Mobile/Diffuse这个shader制作了一部分场景素材,这个shader会依赖场景中的动态实时光源,比较耗费. 于是自己手动重写一份,简化shader的消耗, ...

  7. application.properties详解 --springBoot配置文件【转载】

    # spring boot application.properties配置的各个属性详解 # 该示例文件作为标准提供.(官方文档 翻译过来的) # 还是花了些功夫翻译,各位如果转发,请留下本文地址, ...

  8. java面试中被问到的问题

     技术面 1) Spring 的注解有哪些? 2) 你如何理解 spring IOC 技术,如果不使用spring,你有哪些方式来加载 bean? 3) spring aop 的原理是什么?它是怎样一 ...

  9. idea创建maven SSM项目

    maven配置 ➜ ~ cd /Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/conf ➜ conf vim set ...

  10. A new session could not be created. (Original error: Requested a new session but one was in progress) (WARNING: The server did not provide any stacktrace information)

    出现这个问题,是因为关毕了appium,或者是关毕了appium再次打开,那么session就不是一样的了所以报错. 一般是因为测试的时候报错了,appium自动关毕了,再次打出现的报错 解决方法:重 ...