动态内存分配

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. 使用 Nginx 为 Linux 实例绑定多个域名

    KB: 41467 · 更新时间:2018-11-16 20:26:51     Nginx 是一款广泛应用的 Web 服务器,常用于反向代理.负载均衡器以及 HTTP 缓存等.本文以 CentOS ...

  2. 发现2017年最好的CSS框架

    如今,无数的框架出现在定期而少数人喜欢自助,Foundation和angular.js主宰了整个世界的发展.CSS代表用于描述HTML(或XML)文档表示的样式表语言.一个框架被定义为一个包,它由一组 ...

  3. numpy中的方差、协方差、相关系数

    一.np.var 数学上学过方差:$$ D(X)=\sum_{i\in [0,n)} ({x-\bar{x}})^2 $$ np.var()实际上是均方差,均方差的意义就是将方差进行了平均化,从而使得 ...

  4. C#7.2——编写安全高效的C#代码 c# 中模拟一个模式匹配及匹配值抽取 走进 LINQ 的世界 移除Excel工作表密码保护小工具含C#源代码 腾讯QQ会员中心g_tk32算法【C#版】

    C#7.2——编写安全高效的C#代码 2018-11-07 18:59 by 沉睡的木木夕, 123 阅读, 0 评论, 收藏, 编辑 原文地址:https://docs.microsoft.com/ ...

  5. Easyui中 alert 带回调函数的 消息框

    带回调函数的 消息框: $.messager.alert({ title:'消息', msg:'电话号码 只能是数字!', icon: 'info', width: 300, top:200 , // ...

  6. Elasticsearch 性能监控基础【转】

    https://blog.csdn.net/yangwenbo214/article/details/74000458

  7. 现代php编程

    自动加载__autolaod和spl_autoload_register() 自动加载就是指如果找不到某个类如何处理的方式,具体可参见此文,可以说spl_autoload_register是更加高级, ...

  8. PCL点云分割(3)

    (1)Euclidean分割 欧几里德分割法是最简单的.检查两点之间的距离.如果小于阈值,则两者被认为属于同一簇.它的工作原理就像一个洪水填充算法:在点云中的一个点被“标记”则表示为选择在一个的集群中 ...

  9. 手动安装mysql

    需要在本机(mac)上装个mysql,看到压缩版的体积最小,结果进入了手工安装的坑(正常有界面可以安装,但安装目录将是/usr/local/mysql),既然入了坑就填上吧. 1.解压到自己选择的目录 ...

  10. 消息中间件系列五:RabbitMQ的使用场景(异步处理、应用解耦)

    一.异步处理 场景: 用户注册,写入数据库成功以后,发送邮件和短信. 准备工作: 1)安装RabbitMQ,参考前面的文章 2)新建一个名为RabbitMQAsyncProc的maven web工程, ...