一、指针是什么:从内存地址说起
大家好,我是你们这门课的老朋友。今天咱们聊一个C语言里绕不开的话题——指针。
说实话,我当年学指针的时候,也被绕得晕头转向。后来在嵌入式项目里栽了几个跟头,才真正搞明白它到底是个什么东西。今天我就把那些经验掰开了揉碎了讲给你听。
1.1 内存地址的概念
先问一个问题:你电脑里的内存,到底长什么样?
说白了,内存就是一大排连续的“小格子”。每个格子有8个比特位,也就是1个字节。每个格子都有一个唯一的编号,这个编号就是内存地址。
举个例子:
内存地址: 0x1000 0x1001 0x1002 0x1003 ...
内容: [0x12] [0x34] [0x56] [0x78] ...
地址从0开始,一直往上排。32位系统能访问0x00000000到0xFFFFFFFF,总共4GB空间。64位系统就更大了,理论上能到16EB。
核心理解:内存地址就是内存单元的“门牌号”。你知道了门牌号,就能找到对应的数据。
我在做嵌入式开发时,经常要直接操作寄存器。那些寄存器就映射在固定的内存地址上。比如某个GPIO控制寄存器的地址是0x40021000,你往这个地址写数据,就能控制LED亮灭。这就是地址的威力。
1.2 指针变量的定义与初始化
指针是什么?指针就是一个存放内存地址的变量。
普通变量存的是数据,比如int a = 10; 变量a里存的是10。指针变量存的是地址,比如int *p = &a; 变量p里存的是a的地址。
定义指针的语法很简单:
类型名 *指针变量名;
比如:
int *p; // 指向int类型数据的指针
char *pc; // 指向char类型数据的指针
float *pf; // 指向float类型数据的指针
初始化指针,一般用取地址符&:
int a = 100;
int *p = &a; // p保存了a的地址
也可以初始化为空指针:
int *p = NULL; // 指向空,表示不指向任何有效地址
我的习惯:定义指针时一定初始化。要么给有效地址,要么给NULL。野指针是C语言里最坑的东西之一,我早期项目里因为这个吃过不少苦头。
1.3 取地址符&与解引用符*
这两个符号是操作指针的左右手。
&:取地址符
放在变量前面,得到该变量的内存地址。
int x = 42;
printf("x的地址是:%p\n", &x); // 输出类似 0x7ffd5a3b4c08
*:解引用符
放在指针变量前面,得到该指针指向的内存单元里的数据。
int x = 42;
int *p = &x;
printf("p指向的值是:%d\n", *p); // 输出42
说白了,&是“找到你的门牌号”,*是“按门牌号进去拿东西”。
我曾经踩过的坑:解引用一个未初始化的指针,程序直接崩溃。比如:
int *p; // 没初始化,p里是随机地址
*p = 10; // 往随机地址写数据,大概率段错误
所以记住:指针不初始化,千万别解引用。
1.4 指针的大小与类型
指针本身也是变量,它也要占内存。那么一个指针变量占几个字节?
答案是:取决于系统位数。
| 系统位数 | 指针大小 | 可寻址范围 |
|---|---|---|
| 16位系统 | 2字节 | 64KB |
| 32位系统 | 4字节 | 4GB |
| 64位系统 | 8字节 | 16EB |
你可以用sizeof验证一下:
printf("int*的大小:%zu字节\n", sizeof(int*));
printf("char*的大小:%zu字节\n", sizeof(char*));
printf("void*的大小:%zu字节\n", sizeof(void*));
你会发现,不管指向什么类型,同一系统下所有指针的大小都一样。为什么?因为指针存的是地址,地址的长度是固定的。
那为什么还要区分类型呢?
嗯,这里有个关键点:指针的类型决定了它如何解释内存中的数据。
举个例子:
int a = 0x12345678;
int *p_int = &a;
char *p_char = (char*)&a;
printf("*p_int = 0x%x\n", *p_int); // 输出 0x12345678
printf("*p_char = 0x%x\n", *p_char); // 输出 0x78(小端模式)
同样的地址,int*指针一次读4个字节,char*指针一次读1个字节。类型告诉编译器:从这个地址开始,取多少字节,怎么解释。
一句话总结:指针的大小由系统位数决定,指针的类型由它指向的数据类型决定。大小管“能访问多大范围”,类型管“怎么访问数据”。
知识体系总览
下面这张图帮你理清本章的核心逻辑:
小结
这一章我们讲了四个核心点:
- 内存地址:内存单元的编号,是访问数据的“门牌号”
- 指针变量:存放地址的变量,定义时要初始化
- &和*:取地址和解引用,一个找门牌,一个进门拿东西
- 大小与类型:大小由系统决定,类型决定如何解释数据
指针这东西,刚开始觉得绕,但一旦理解了“地址”这个概念,后面就顺了。我当年在调试一个串口驱动时,就是因为没搞清指针类型,读出来的数据全是乱的。从那以后,我对指针的类型格外敏感。
下一章咱们聊聊指针的运算,那才是真正开始玩指针的地方。