正确理解 C 语言中的指针

2019年09月16日 » ComputerScience
注意! 离本文创建时间已经过去了 57 天, 请注意时效性

本文是当初学习 C 语言时写过的一篇博客, 从老的 WordPress 博客中恢复过来的.

前言

新手在学习 C 语言的时候常常对指针有些困惑, 下面就说说我对指针的理解.

先弄清楚几个问题

变量的本质

变量的本质是一个指向某个内存地址的名称表示. 编译后的目标代码中并没有变量名字, 编译器在编译阶段会将变量名字和和其表示的内存地址建立一个映射表, 记录变量类型/名称/地址.

当声明一个变量的时候,实际上就是在向操作系统申请一段内存地址; 赋值的时候, 就是在向这个地址填入相应的数据.

变量赋值的本质

声明一个变量 x, 地址是 0x0001, 该地址存储着 1. 将 2 赋值给 x, 则执行以下步骤:

  1. 根据映射表, 查找变量 x 的地址, 得到 0x0001
  2. 将值 2 存入该地址, 赋值完成

指针赋值同理.

* 的三种含义

  1. 声明一个函数原型的时候, 如:
     void fn(int *a)
    

    其中的 *a 表示 fn 函数接受一个指针变量(即内存地址)作为参数, 该参数是 a.

  2. 在函数体中, *a 出现在等号左边的时候, 表示往指针 a 所表示的内存地址存入等号右边的值, 如:
     void fn(int *a) {
         *a = 3;
     }
    
  3. 在函数体中, *a 出现在等号右边的时候, 表示取出指针 a 所表示的内存地址中的值
     void fn(int *a) {
         int tmp = *a;
     }
    

举例: 一个交换两个变量值的函数

void swap(int *a, int *b);

int main (void) {
    int x = 1;
    int y = 2;
    swap(&x, &y);
}

void swap(int *a, int *b) {
    int tmp = *a; // 也可以写作 int * tmp = *a; 只是说明 tmp 是一个指针变量, 即存储着指针的变量;
    *a = *b;
    *b = tmp;
}

示例解释:

  1. main 中声明了两个变量 x, y. 地址是 &x 和 &y, 该地址分别存存储着值 1 和 2.
  2. 遇到 swap 函数, 该函数:
    1. 声明两个指针作为参数, int *a 或 int *b 作为形参, 表示可以被定位到值为 int 类型的指针, 即该函数期望两个指向 int 类型值的地址作为参数. 此时, * 表示接受指针变量.
  3. 执行 swap 函数, 则:
    1. 形参 a 被赋值为 &x, 即 x 的内存地址; 则本地变量 a 的内存地址证存储着的值是 x 的内存地址. 同理, 形参 b 被赋值为 &y, 即 y 的内存地址.(形参加上 * 号只是表明该参数是指针类型的)
    2. 声明一个本地整型变量 tmp, 则 *a 表示:
      1. 第一步: 找到 a 在内存中的地址.
      2. 第二步: 读取 a 在内存中的地址所表示的值, 该值也是一个内存地址, 即变量 x 的内存地址.
      3. 第三步: 返回 a 的值的值, 即 a 的值是个内存地址, 获取该内存地址上的值, 也就是 x 的值, 即 1.
    3. *b 表示将变量 b 的值(一个内存地址)的值(该内存地址存储的值)通过 * 号取出来, 即内存地址 &y 的对应的值, 即 2, 之后赋值给 *a 即内存地址 &x 所存储的值(因为 a = &x), 即 x 的值, 为 1. 注意, 在 swap 函数中修改了 main 函数中变量 x 的内存地址所存储的值,
    4. 最后一句中, tmp 即为值 1, 将其赋值给 *b, 也即是赋值给 b 的内存地址所存储的值, 也就是 2, 将该值覆盖成 1. 这里 swap 函数亦通过指针修改了 main 函数中 y 的内存地址上所存储的值.

上个图加深理解:

交换值函数

交换值函数

The End. 转载请注明出处