一、动态内存分配

1. malloc函数

malloc(memory allocation,内存分配)函数是C语言标准库中的一个函数,用于在程序运行时动态分配一块指定大小的内存区域。这块内存是在(heap)上分配的,与栈上的内存不同,它的生命周期由程序员控制,可以手动分配和释放。malloc 函数的声明在 stdlib.h 头文件中。

malloc 的基本用法

1
2
3
#include <stdlib.h>

void* malloc(size_t size);

参数:

  • size: 要分配的内存大小,以字节为单位。

返回值:

  • 成功时,返回一个指向分配内存块的指针,该指针的类型为 void*(可以强制转换为任何类型的指针)。
  • 如果分配失败(比如系统内存不足),则返回 NULL

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <stdlib.h>

int main() {
int *arr;
int n = 5;

// 使用 malloc 动态分配一个大小为 n 的整型数组
arr = (int*) malloc(n * sizeof(int));

if (arr == NULL) {
// 如果 malloc 失败,arr 会返回 NULL
printf("Memory allocation failed\n");
return 1;
}

// 使用分配的内存
for (int i = 0; i < n; i++) {
arr[i] = i + 1;
printf("%d ", arr[i]);
}
printf("\n");

// 使用完毕后释放内存
free(arr);

return 0;
}

在这个例子中,malloc 分配了一个能容纳 5 个 int 类型元素的内存空间。注意,分配内存后应立即检查是否分配成功(返回值是否为 NULL),并且在使用完分配的内存后,应该使用 free 函数释放内存,以避免内存泄漏。

malloc 的优点

  1. 灵活的内存管理malloc 允许在运行时动态分配内存,尤其适用于数组大小在编译时不确定的情况。
  2. 堆上的大内存:与栈不同,堆可以存储比栈更大的数据。使用 malloc 可以避免栈溢出,适合处理大数据或需要长期保留的数据。
  3. 可移植性malloc 是标准库函数,跨平台可用。

malloc 的缺点

  1. 手动管理内存:程序员必须手动释放内存(使用 free 函数)。未及时释放内存会导致内存泄漏,程序运行时间长了可能耗尽系统内存。
  2. 分配失败风险malloc 在分配失败时返回 NULL,程序员需要处理这种情况。忽略分配失败的检查可能导致程序崩溃。
  3. 性能开销:动态内存分配在系统调用中有一定的性能开销,因此如果频繁分配和释放内存,性能可能受到影响。

常见的错误使用

  1. 忘记释放内存:导致内存泄漏。
  2. 二次释放内存:释放已经释放过的内存会导致程序运行错误。
  3. 使用未初始化的内存malloc 只分配内存但不初始化,需要手动初始化或使用 calloc

总之,malloc 是C语言中用于动态内存管理的基础工具,但在使用时需要格外小心,以避免内存管理方面的错误。

2. 内存泄漏

程序作用:

  • 用malloc获取100MB内存空间,返回地址赋值给p
  • while循环可以分辨获取了几次空间

3. malloc申请的内存是否连续?

是的,malloc 函数分配的内存是连续的。

在 C 语言中,malloc 函数用于动态分配一块指定大小的内存区域,并返回一个指向该内存块的指针。malloc 保证返回的内存块是连续的,即在物理内存中是相邻的一块区域。这对于许多需要内存连续性的操作非常重要,例如在处理数组、矩阵等数据结构时。

4. 0 (Null)不是一个有效的地址

free(Null)

free(0)均是不会报错的。

因此,可以在定义指针时,赋值为零。

5. 常见问题

  • malloc没有free, 残留内存垃圾,申请内存不释放,会造成内存泄漏,比如大型程序(服务器程序)

思考

1
2
3
4
5
6
7
8
9


int a[] ={1,2,3,4,5,}

int *p= a

int *q = &a[5]

q-p=?

在 C 语言中,指针相减操作会返回两个指针之间的距离,单位是数组元素的个数。

假设你有如下的代码:

1
2
3
int a[] = {1, 2, 3, 4, 5};
int *p = a;
int *q = &a[5];
  • p 指向数组 a 的第一个元素,即 a[0],地址为 a
  • q 指向 a[5],也就是超出数组末尾的第一个地址(这个元素不存在,因为数组的有效下标是 0 到 4)。

接下来执行 q - p

  1. q 指向 &a[5],即 a 数组末尾后面的地址。
  2. p 指向数组的第一个元素 a[0]

计算 q - p 时,它会计算两个指针之间相隔的元素个数。因此:

1
q - p = 5

这是因为 q 指向的地址比 p 向后移动了 5 个整型元素的距离。

最终答案是:q - p = 5