第十一章、指针

这一章我们来学习 C 语言中最难理解的知识点指针

指针是 C 语言的灵魂。C 语言有了指针才使得它变得更加强大、灵活和高效。如果你不懂 C 语言的指针就等于你没有掌握 C 语言。如果你不打算搞懂指针。那建议你学习其它的编程语言,比如 python

指针 是 C 语言的重点难点,它理解起来比较令人困惑。要理解指针我们先要了解内存以及变量在内存中的存放方式。

下面我先来讲解一下内存和变量地址。

1. 内存和变量地址

先来说一下内存。内存是用来存放计算机运行时数据的重要存储单元。 我们声明的变量在运行时都占用一段连续的内存空间。

在计算机内,内存是以字节为最小单位的存储单元组成的。这些字节在逻辑上是顺序排列的,每个字节都有自己的地址。第一个字节的地址为 0,第二个字节的地址为1,以此类推。一般要存储比较小的数字,比如年岭我们用一个字节就够了。但我们要存储你的豪车的里程数,显然我们需要两个或四个字节才能存得下。因此我们存储不同的数据就有了不同的数据类型。也就是说,数据类型是内存结构中的数据对外(编译器)的具体表现形式。

在现实中,你可以把内存理解成依山傍海的海边的一条狭长的平原空地。你打算在这里建造一座城市。于是作为城市规划师的你自西向东每隔 10 米划分成一个地段,如下图所示:

^^ ^^ ^^ 山 ^^ ^^ ^^ 山 ^^ ^^ ^^ https://weimingze.com ^^ ^^ ^^ 山 ^^ ^^
+-------+-------+-------+-------+-------+-------+-------+-------+-------+
| house | house | house |            school             | house |   ...
+-------+-------+-------+-------+-------+-------+-------+-------+-------+
    1       2       3       4       5       6       7       8       9
                          道             路
~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~
 ~~ ~~ ~~ 海 ~~ ~~ ~~ ~~ ~~ ~~ 海 ~~ ~~ ~~ ~~ ~~ ~~ 海 ~~ ~~ ~~ ~~ ~~ ~~
~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~

你为每个地块都定义的一个数字 1、2、3 这就是地址。其中 居民房子(house)比较小占一个地块,学校(school)比较大占四个地块。房子和学校就是不同的类型(对应C 中的数据类型)。但有个问题出现了,学校占用了4、5、6、7 四个地块,为了邮寄和管理方便,我们如何确定学校的地址呢?聪明的你想到了用最小的数值表示跨越多个地块的建筑的地址(C 语言中也是这样规定地址的),即学校的地址是 4 而不是 5、6、7。

在 C 语言中,我们声明的变量,在运行时都会存在于内存中。我们用 &标识符 的语法就可以获取一个变量的地址。下面我们用程序打印这些变量的地址。

// filename: memory_addr.c
#include <stdio.h>

int main(int argc, char *argv[]) {
    char ch1 = 'a', ch2 = 'b';
    short s = 10;
    int i = 1000;

    printf("&ch1:%p\n", &ch1);
    printf("&ch2:%p\n", &ch2);
    printf("&s  :%p\n", &s);
    printf("&i  :%p\n", &i);

    return 0;
}

说明:

运行结果:

weimingze@mzstudio:~$  gcc -o memory_addr memory_addr.c
weimingze@mzstudio:~$  ./memory_addr
&ch1:0x7ffdd8ac3ae0
&ch2:0x7ffdd8ac3ae1
&s  :0x7ffdd8ac3ae2
&i  :0x7ffdd8ac3ae4

根据上述输出结果,可以推断其内存分布结构如下:

          ch1     ch2          s                       i
   ----+-------+-------+-------+-------+-------+-------+-------+-------+----
   ... |  'a'  |  'b'  |      10       |              1000             | ...
   ----+-------+-------+-------+-------+-------+-------+-------+-------+----
       ^       ^       ^               ^                               ^
       | &ch   | &ch2  | &s            | &i                            |
0x7ffdd8ac3ae0 |   0x7ffdd8ac3ae2      |                      0x7ffdd8ac3ae8
         0x7ffdd8ac3ae1           0x7ffdd8ac3ae4

计算内的内存地址的范围

具体内存的取值范围由操作系统的位宽和编译器共同决定。

现在我们了解了地址的概念。那么我们如何来用变量来保存这些地址呢?这就需要用到我们下一节课讲到的指针

实验:

写一段程序,定义几个变量,打印这些变量的地址,分析这些变量在内存中的分布情况。