C语言动态内存分配(上)函数介绍

前言

数组中的元素存储在连续的单元中,在C语言中声明一个数组,那么它的内存在编译的时候就已经分配完成。

int array1[5] = {1, 2, 3, 4, 5};
for (auto& elem : array1)
    LOG(INFO) << "Element " <<  elem << ": "<< &elem;

每个元素的内存地址相差4个字节,正好是整形的宽度

Element 1: 0x16b8c3700
Element 2: 0x16b8c3704
Element 3: 0x16b8c3708
Element 4: 0x16b8c370c
Element 5: 0x16b8c3710

你也可以在运行的时候动态分配内存,本篇教程来学习它们的差异,以及如何使用动态分配内存。

为什么使用动态内存分配

当申请数组时,数组的大小必须是一个给定大小的常量。但是经常碰到的一种情况是数组的大小取决于输入的数据,它直到运行的时候才能够确定。比如一个统计学生年级程序,需要一个数据结构存储班级里的所有学生信息,但是不同的班级有不同数量的学生,这种场景下常规申请数组的方式,申请的内存会比我们需要的大。

比如有5个班级,班级人数分别为43, 45, 50, 39, 42

int class1[5][50];

为了存储所有班级的学生信息,数组中所有班级都是以最大的人数存储学生数据。这种做法对于简单的程序来说是可行的,但是对于复杂的问题它会有明显的缺点

  1. 无法处理超过数组声明时大小的数据;
  2. 所有的数据都很小,但是声明的数组却很大,造成不必要的浪费
  3. 当程序的输入超过数组的大小时,本来是需要抛出异常的,但是它输出了看似正确的数据

malloc()和free()函数

C语言库提供malloc()和free()两个函数,它们用来管理动态内存的分配和释放,这些函数维持一个内存池,当程序需要额外的内存时,调用malloc()函数进行分配,返回指定大小的内存地址指针。你需要对数组进行初始化,或者直接调用calloc()函数(下个小节会讲到),当之前分配的内存不需要时,需要调用free()函数将其释放。

malloc()函数

这两个函数的声明

void* malloc(size_t size );
void free( void* ptr );

malloc需要传递需要内存的字节数,如果指定的内存数量可以获取,就返回该片内存的首地址。malloc()函数分配的内存是连续的,比如需要100字节,那么就会分配100字节连续内存,不会多也不会少。

当内存池为空,或者没有足够的内存满足分配时,malloc()函数会调用操作系统获取更多的内存,如果操作系统也不能满足malloc()函数的需求,那么就会返回nullptr指针,因此对于malloc()函数返回值判断是否为nullptr很重要。

free()函数的参数是nullptr或者是之前从malloc(), calloc(), realloc()函数返回的指针,传递nullptr对free()函数没有影响。

malloc()函数怎么知道用户是想存整形变量还是结构体呢?其实malloc()函数是不知道的,它返回void*的指针,可以被转换成为任何的指针类型。

在需要地址对齐的机器中,malloc()函数返回会从地址对齐的地方开始。

calloc()和realloc()函数

有两个额外的内存分配函数calloc()和realloc(),它们的声明如下

void* calloc(size_t num, size_t size );
void* realloc( void* ptr, size_t new_size )

calloc()函数也分配内存,和malloc()函数最大的不同在于calloc()函数会将内存初始化为0。这种初始化是很方便的,但是如果你的程序后续会直接向内存赋值,那么此时的初始化就是一种浪费。

calloc()函数和malloc()函数一个小区别就是传递的参数不同,calloc()函数需要元素的个数以及每个元素的字节数,从这两个参数中计算需要的内存大小。

realloc()函数会改变之前分配的内存大小,可以使用这个函数将内存变大或者变小,如果内存变大,那么会保留原来的数据,额外的内存会添加到原内存结尾,新分配的内存没有被初始化。如果内存变小,后面的内存就会被去掉,前面的数据仍然保持不变。

如果原来的内存没法变大,realloc函数会重新分配一块完整的内存,并且将原来的数据复制到新内存上。因此调用realloc()函数后,不能使用原来的指针,需要使用realloc()函数新返回的指针。最后,如果realloc()函数的参数是nullptr,那么它的表现和malloc()函数一致。

使用动态内存分配

int* pi;
pi = static_cast(malloc(100));
if (pi == nullptr) {
    LOG(INFO) << "Out of memory";
    return EXIT_FAILURE;
}

C语言中使用NULL表示空,相当于0值,使用nullptr看起来是进行指针类型对比,而不是整形对比.

如果分配成功,会获取指向100字节的指针,在4个字节表示 整形(int)的机器中,这些内存相当于有25个整形的数组。

如果你是想获得25个整形空间,还有另外一种更好的获取方法

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

这种方法是更好的,因为它可移植,在整形字节数不是4的机器上运行。

现在你有一个pi指针,如何使用内存呢?你可以使用指针运算去访问数组不同的位置,使用循环将每个元素设置为0

int* pi2 = pi;
for (int i = 0; i < 25; i++)
    *pi2++ = 0;

你也可以使用下标进行访问,和之前的功能相同

for (int i = 0; i < 25; i++)
    pi[i] = 0;
发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章