0.关于动态分配内存的必要性:空间开辟的大小是固定的,而数组在声明时,需要指明数组的大小,意味着数组在编译时必须确定数组的大小,而当我们面对在运行时才能确定开辟内存大小的需求,明显无法满足,所以存在需要动态开辟内存空间的需求。
1.三个动态内存的函数
1.01(malloc)
描述
C 库函数 void *malloc(size_t size) 分配所需的内存空间,并返回一个指向它的指针。
声明
下面是 malloc() 函数的声明。
void *malloc(size_t size)
参数
返回值
该函数返回一个指针 ,指向已分配大小的内存。如果请求失败,则返回 NULL。
实例
下面的实例演示了 malloc() 函数的用法。
实例
#include #include #include
int main()
{ char *str; /* 最初的内存分配 */
str = (char *) malloc(15);
strcpy(str, "runoob");
printf("String = %s, Address = %u\n", str, str); /* 重新分配内存 */
str = (char *) realloc(str, 25);
strcat(str, ".com");
printf("String = %s, Address = %u\n", str, str);
free(str);
return(0);
}
让我们编译并运行上面的程序,这将产生以下结果:
String = runoob, Address = 3662685808
String = runoob.com, Address = 3662685808
1.02(calloc)
描述
C 库函数 void *calloc(size_t nitems, size_t size) 分配所需的内存空间,并返回一个指向它的指针。malloc 和 calloc 之间的不同点是,malloc 不会设置内存为零,而 calloc 会设置分配的内存为零。
声明
下面是 calloc() 函数的声明。
void *calloc(size_t nitems, size_t size)
参数
- nitems -- 要被分配的元素个数。
- size -- 元素的大小。
返回值
该函数返回一个指针,指向已分配的内存。如果请求失败,则返回 NULL。
实例
下面的实例演示了 calloc() 函数的用法。
实例
#include #include
int main()
{
int i, n; int *a; printf("要输入的元素个数:");
scanf("%d",&n);
a = (int*)calloc(n, sizeof(int));
printf("输入 %d 个数字:\n",n);
for( i=0 ; i < n ; i++ )
{
scanf("%d",&a[i]);
}
printf("输入的数字为:");
for( i=0 ; i < n ; i++ )
{ printf("%d ",a[i]); }
free (a); // 释放内存
return(0);
}
让我们编译并运行上面的程序,这将产生以下结果:
要输入的元素个数:3
输入 3 个数字:
22
55
14
输入的数字为:22 55 14
(相对于malloc而言calloc会将所开辟空间的每个字节都初始化为0)
1.03(relloc)
描述
C 库函数 void *realloc(void *ptr, size_t size) 尝试重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小。
声明
下面是 realloc() 函数的声明。
void *realloc(void *ptr, size_t size)
参数
- ptr -- 指针指向一个要重新分配内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的。如果为空指针,则会分配一个新的内存块,且函数返回一个指向它的指针。
- size -- 内存块的新的大小,以字节为单位。如果大小为 0,且 ptr 指向一个已存在的内存块,则 ptr 所指向的内存块会被释放,并返回一个空指针。
返回值
该函数返回一个指针 ,指向重新分配大小的内存。如果请求失败,则返回 NULL。
实例
下面的实例演示了 realloc() 函数的用法。
实例
#include #include #include
int main()
{
char *str; /* 最初的内存分配 */
str = (char *) malloc(15);
strcpy(str, "runoob");
printf("String = %s, Address = %p\n", str, str); /* 重新分配内存 */
str = (char *) realloc(str, 25);
strcat(str, ".com");
printf("String = %s, Address = %p\n", str, str);
free(str);
return(0);
}
让我们编译并运行上面的程序,这将产生以下结果:
String = runoob, Address = 0x7fa2f8c02b10
String = runoob.com, Address = 0x7fa2f8c02b10
关于realloc情况的几种说明:
情况 1 当是情况 1 的时候,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化。
情况 2 当 是情况2 的时候,原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来 使用。这样函数返回的是一个新的内存地址。 由于上述的两种情况realloc 函数的使用就要注意一些。
1.04(free函数释放内存)
void free ( void* ptr );
free 函数用来释放动态开辟的内存。
如果参数 ptr 指向的空间不是动态开辟的,那 free 函数的行为是未定义的。
如果参数 ptr 是 NULL 指针,则函数什么事都不做。
2.关于动态内存开辟的相关错误
2.01 对NULL指针进行解引用操作
2.02 对动态开辟空间的越界访问
2.03 对非动态开辟的空间进行free操作
2.04使用free释放内存空间的一部分
2.05 对同一块内存进行多次释放或者忘记释放
3 .关于c和c++程序的内存分配
C/C++ 程序内存分配的几个区域:
1. 栈区( stack ):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些
存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有
限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
2. 堆区( heap ):一般由程序员分配释放, 若程序员不释放,程序结束时可能由 OS 回收 。分配方式类似
于链表。
3. 数据段(静态区)( static )存放全局变量、静态数据。程序结束后由系统释放。
4. 代码段:存放函数体(类成员函数和全局函数)的二进制代码。
4.柔性数组
也许你从来没有听说过 柔性数组( flflexible array ) 这个概念,但是它确实是存在的。 C99 中,结构中的最
后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员。 比特科技
例如:
有些编译器会报错无法编译可以改成:
柔性数组的特点:
结构中的柔性数组成员前面必须至少一个其他成员。
sizeof 返回的这种结构大小不包括柔性数组的内存。
包含柔性数组成员的结构用 malloc () 函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应
柔性数组的预期大小。
例如:
柔性数组的使用
这样柔性数组成员 a ,相当于获得了 100 个整型元素的连续空间。
柔性数组的优势
上述的 type_a 结构也可以设计为:
typedef struct st_type
{
int i ;
int a [ 0 ]; // 柔性数组成员
} type_a ;
typedef struct st_type
{
int i ;
int a []; // 柔性数组成员
} type_a ;
//code1
typedef struct st_type
{
int i ;
int a [ 0 ]; // 柔性数组成员
} type_a ;
printf ( "%d\n" , sizeof ( type_a )); // 输出的是 4
// 代码 1
int i = 0 ;
type_a * p = ( type_a * ) malloc ( sizeof ( type_a ) + 100 * sizeof ( int ));
// 业务处理
p -> i = 100 ;
for ( i = 0 ; i < 100 ; i ++ )
{
p -> a [ i ] = i ;
}
free ( p );
上述 代码 1 和 代码 2 可以完成同样的功能,但是 方法 1 的实现有两个好处: 第一个好处是: 方便内存释放
如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给用户。用
户调用 free 可以释放结构体,但是用户并不知道这个结构体内的成员也需要 free ,所以你不能指望用户来发
现这个事。所以,如果我们把结构体的内存以及其成员要的内存一次性分配好了,并返回给用户一个结构体
指针,用户做一次 free 就可以把所有的内存也给释放掉。
第二个好处是: 这样有利于访问速度 .
连续的内存有益于提高访问速度,也有益于减少内存碎片。