---
title: malloc 的微浅理解
date: 2024-05-19
modified: 2024-05-19
url: https://yuuu.org/post/malloc-understanding
categories: ["学习"]
tags: ["学习"]
---
这段时间在学习数据结构。对数据结构中的 malloc 总是感到困惑，这几天查了资料，算是查清楚了 malloc 的用法，特此记录<!--more -->
在数据结构中，对结构体最开始分配空间的步骤是（以线性表为例）：

```c
typedef struct{
  ElemType data[MaxSize];//`ElemType` 就是用来代替实际数据类型（如 int）的一个占位符。
  int length;
} SqList;

void CreateList(SqList *&L, ElemType a[], int n){
  L = (SqList *)malloc(sizeof(SqList));
  while(i<n){
    L -> data[k]=a[i];
    k++,i++;
  }
  L ->length = k;
}
```

在这段算法中，比较难理解的是这两行代码

```c
SqList *&L;
//和
L = (SqList *)malloc(sizeof(SqList));
```

前者代表的是对指针的引用，允许函数内部修改函数外部传入的指针本身，而不仅仅是修改指针所指向的值。后者是对指针所指向的地址进行动态内存分配。

对于`SqList *&L`，  
`*` 用于指针前面时，它表示解引用操作，即获取指针所指向的值。例如，`int x = *ptr;` 表示将指针 `ptr` 指向的整数值赋给变量 `x`。

`&` 运算符：`&` 运算符用于取地址操作。当 `&` 用于变量前面时，它返回该变量的地址。例如，`int x = 10; int *ptr = &x;` 将变量 `x` 的地址赋给指针 `ptr`。

`*&`：这个组合可以看作是将一个指针的引用。`*` 表示指针，而 `&` 表示引用。所以 `*&` 表示指针的引用。这在 C/C++ 中是合法的语法，允许在函数参数中传递指针的引用，以便在函数内部修改指针指向的地址。

因此，`SqList *&L` 声明了一个指针 `L` 的引用，它指向一个 `SqList` 类型的对象。

我们再来讲讲 malloc。malloc 全称 memory allocation，在 cppreference 中有写到 malloc 的功能。

> Allocates `size` bytes of uninitialized storage.  
> 分配 `size` 字节的未初始化存储。  
>
> If allocation succeeds, returns a pointer that is suitably aligned for any object type with [fundamental alignment](https://en.cppreference.com/w/c/language/object#Alignment 'c/language/object').  
> 如果分配成功，则返回一个指针，该指针适合任何具有基本对齐方式的对象类型。  
>
> If `size` is zero, the behavior of `malloc` is implementation-defined. For example, a null pointer may be returned. Alternatively, a non-null pointer may be returned; but such a pointer should not be [dereferenced](https://en.cppreference.com/w/c/language/operator_member_access 'c/language/operator member access'), and should be passed to [free](https://en.cppreference.com/w/c/memory/free 'c/memory/free') to avoid memory leaks.  
> 如果 `size` 为零，则 `malloc` 的行为是实现定义的。例如，可以返回空指针。或者，可以返回非空指针；但这样的指针不应该被取消引用，并且应该传递给 free 以避免内存泄漏。

以下是 malloc() 函数的声明。

```c
void *malloc(size_t size)
```

其中，`size_t`是一个无符号整数类型，通常用于表示内存块的大小。  
在不同的平台上，`size_t` 的大小可能会有所不同，但它通常被设计为足以容纳系统中最大可能的对象大小。  

> `size_t` can store the maximum size of a theoretically possible object of any type (including array).

`void*` 是一个指针类型，表示指向未知类型的指针。这种类型的指针可以指向任何类型的数据，但不能直接对其进行解引用操作。

所以我们需要将其转化，因此可以有

```c
int* intPtr = (int*)malloc(sizeof(int));
```

或者说，可以使用隐式转化

```c
int* intPtr = malloc(sizeof(int));
```

此刻，一个内存空间就创立好了，那么，发生了什么事情？

当执行 `int* intPtr = malloc(sizeof(int));` 这行代码时，发生了以下几件事情：

1. **`malloc` 分配内存**：`malloc(sizeof(int))` 这部分是一个函数调用，它调用了 `malloc` 函数来分配一块内存空间，大小为 `sizeof(int)` 字节，也就是一个整数大小的内存块。`sizeof()` 是一个运算符，用于计算指定类型的大小。

2. **返回指针**：`malloc` 函数返回一个指向新分配的内存块起始地址的指针。这个指针的类型是 `void*`，即指向未知类型的指针，需要转化。

3. **类型转换**：在赋值语句 `int* intPtr = ...` 中，`void*` 类型的指针被隐式地转换为 `int*` 类型的指针。这是因为 `int*` 类型的指针可以指向整数类型的数据，而 `malloc` 返回的指针指向的是刚分配的内存块，大小为一个整数的大小。

4. **赋值给指针变量**：分配的内存块的起始地址被赋值给了 `intPtr` 指针变量，使得 `intPtr` 指向了这块新分配的内存空间。

因此，`int* intPtr = malloc(sizeof(int));` 这行代码的作用是分配一块大小为一个整数大小的内存空间，并将其起始地址存储在 `intPtr` 指针变量中，以便后续使用。   
当然，这个数字还可以通过 n 来拓展。  
cppreference.com 也写出了关于它的用法和格式。

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

int main(void)
{
    int *p1 = malloc(4*sizeof(int));
    // allocates enough for an array of 4 int(为 4 个 int 的数组分配足够的空间)

    int *p2 = malloc(sizeof(int[4]));//same, naming the type directly(一样，直接命名类型)

    int *p3 = malloc(4*sizeof *p3);//same, without repeating the type name(相同，不重复类型名称)

    if(p1) {
        for(int n=0; n<4; ++n) // populate the array
            p1[n] = n*n;
        for(int n=0; n<4; ++n) // print it back out
            printf("p1[%d] == %d\n", n, p1[n]);
    }

    free(p1);
    free(p2);
    free(p3);
}
```

如有错误，还请指出，愿我们共同成长。

参考资料：

- [cppreference.com](https://en.cppreference.com/w/c/memory/malloc)
