C语言动态内存管理

8/26/2023 C语言

# C 语言动态内存管理

char arr[10] = {0}; // 在栈空间上连续开辟 10 个字节的连续空间,指定数组长度后,就不能变动。

有时候我们需要的空间大小在程序运行的时候才能知道,那数组在编译时开辟空间的方式就不能满足,这时候只能试试动态内存开辟了。

# malloc

void* malloc (size_t size);

Allocate memory block Allocates a block of size bytes of memory, returning a pointer to the beginning of the block.

The content of the newly allocated block of memory is not initialized, remaining with indeterminate values.

If size is zero, the return value depends on the particular library implementation (it may or may not be a null pointer), but the returned pointer shall not be dereferenced.

分配内存块 分配 size 字节的内存块,返回指向块开头的指针。

新分配的内存块的内容未初始化,保留不确定的值。

如果大小为零,则返回值取决于特定的库实现(它可能是也可能不是空指针),但返回的指针不应被取消引用。

# free

void free (void* ptr);

Deallocate memory block A block of memory previously allocated by a call to malloc, calloc or realloc is deallocated, making it available again for further allocations.

If ptr does not point to a block of memory allocated with the above functions, it causes undefined behavior.

If ptr is a null pointer, the function does nothing.

Notice that this function does not change the value of ptr itself, hence it still points to the same (now invalid) location.

释放内存块 先前通过调用 malloc、calloc 或 realloc 分配的内存块将被释放,使其再次可用于进一步分配。

如果 ptr 不指向用上述函数分配的内存块,则会导致未定义的行为。

如果 ptr 是空指针,则该函数不执行任何操作。

请注意,此函数不会更改 ptr 本身的值,因此它仍然指向相同的(现在无效)位置。

当不释放动态申请的内存的时候,如果程序结束,动态申请的内存由操作系统自动回收,如果程序不结束,动态内存时不会自动回收的,就会形成内存泄漏的问题。

# calloc

void* calloc (size_t num, size_t size);

Allocate and zero-initialize array Allocates a block of memory for an array of num elements, each of them size bytes long, and initializes all its bits to zero.

The effective result is the allocation of a zero-initialized memory block of (num*size) bytes.

If size is zero, the return value depends on the particular library implementation (it may or may not be a null pointer), but the returned pointer shall not be dereferenced.

分配数组并将其清零初始化 为 num 个元素的数组分配一块内存,每个元素的长度为 size 字节,并将其所有位初始化为零。

有效结果是分配 (num*size) 字节的零初始化内存块。

如果大小为零,则返回值取决于特定的库实现(它可能是也可能不是空指针),但返回的指针不应被取消引用。

# realloc

void* realloc (void* ptr, size_t size);

Reallocate memory block Changes the size of the memory block pointed to by ptr.

The function may move the memory block to a new location (whose address is returned by the function).

The content of the memory block is preserved up to the lesser of the new and old sizes, even if the block is moved to a new location. If the new size is larger, the value of the newly allocated portion is indeterminate.

In case that ptr is a null pointer, the function behaves like malloc, assigning a new block of size bytes and returning a pointer to its beginning.

重新分配内存块 更改 ptr 指向的内存块的大小。

该函数可以将内存块移动到新位置(其地址由函数返回)。

即使该块被移动到新位置,内存块的内容也会保留到新大小和旧大小中较小的一个。 如果新的大小更大,则新分配的部分的值是不确定的。

如果 ptr 是空指针,则该函数的行为类似于 malloc,分配大小为字节的新块并返回指向其开头的指针。

使用如下:

#include <stdlib.h>
#include <stdio.h>

int main() {
    // 申请 10 个整型空间 
    // int * ptr = (int*)malloc(40);
    int * ptr = (int*)calloc(10, sizeof(int));
    
    if(ptr == NULL) {
        perror("calloc");
        return 1;
    }
  
    // 使用
  	for(int i = 0; i < 10; i++) {
      *(ptr + i) = i;
    }
    
  	// 空间不够,再扩容 10 个整型空间
  	int * p = (int*)realloc(ptr, 40);
  
  	 if(ptr != NULL) {
       ptr = p;
     }
  
    // 用完
    free(ptr);
    ptr = NULL;
    
    return 0;
}

# 常见错误

  • 使用 free 来释放非动态开辟的内存。
  • 使用 free 来释放一块动态开辟的内存的一部分。
  • 对同一块动态内存多次释放。
  • 动态开辟内存忘记释放。

# C/C++中程序的内存区域划分

![image-20230825164135515](/Users/mac/Library/Application Support/typora-user-images/image-20230825164135515.png)

1、栈又叫堆栈,用于存储非静态局部变量/函数参数/返回值等等,栈是向下增长的。

2、内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享内存,做进程间通信。

3、堆用于存储运行时动态内存分配,堆是向上增长的。

4、数据段又叫静态区,用于存储全局数据和静态数据。

5、代码段又叫常量区,用于存放可执行的代码和只读常量。

# 柔性数组

C99中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组成员。

struct S3 {
    int num;
    int arr[]; // 柔性数组成员
};


int main() {
    
    printf("%d\n", sizeof(struct S3)); // 4
    return 0;
}

柔性数组的特点:

  • 结构中的柔性数组成员前必须至少一个其他成员
  • sizeof返回的这种结构大小不包括柔性数组的内存
  • 包含柔性数组成员结构用malloc()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小
struct S3 {
    int num;
    int arr[]; // 柔性数组成员
};


int main() {

    printf("%d\n", sizeof(struct S3));

   // 这样只是给num开辟4个字节的空间
   // struct S3* ps = (struct S3*)malloc(sizeof(struct S3)); 
   // 这样才是给num和柔性数组成员开辟内存
    struct S3* ps = (struct S3*)malloc(sizeof(struct S3) + 40);

    // 扩容
    struct S3* ptr = (struct S3*)realloc(ps, sizeof(struct S3) + 40);
    if(ptr != NULL) {
        ps = ptr;
    }

    return 0;
}

柔性数组的优势:

  • 方便内存释放
  • 利于访问速度
Last Updated: 3/20/2024, 8:15:43 AM
강남역 4번 출구
Plastic / Fallin` Dild