【C语言-指针-指针变量】1. 指针是什么?——从“你家住哪儿”开始讲起

开发技术 作者: 精灵星空 SKY315.COM 更新于: 浏览: 4
码字发文不易,转载请注明出处【精灵星空 SKY315.COM】

同学们,前面咱们把函数章节全部啃完了。今天开始,咱们要进入C语言里最刺激、最让人又爱又恨的部分——指针。

为啥说它让人又爱又恨?因为指针是C语言的灵魂,你学不会指针,C语言就等于白学。学明白了指针,你才能真正理解什么叫“直接操作内存”,才能在数据结构、操作系统、嵌入式这些领域站住脚。

但很多同学一听到“指针”两个字,心里就咯噔一下——“完了,这玩意儿听说特别难,各种*和&,绕来绕去的,脑子都晕了。”

别怕。今天咱们就把指针彻底拆开,用最朴素的大白话,从“你家住哪儿”这个问题开始讲起。

先想一个问题:变量到底住在哪儿?

咱们前面学了变量。你写int a = 10;,意思是在内存里找了一块地方,把10放进去。

那问题来了:这块地方在哪儿?内存那么大,它总得有个位置吧?

你可以把内存想象成一栋超级大的酒店,里面密密麻麻全是房间,每个房间都有一个独一无二的门牌号。这个门牌号,在计算机里就叫地址。

你定义了一个变量int a = 10;,就相当于酒店开了一个房间,门牌号是比如0x7ffd5e8a3c(这是十六进制的地址,你不用记这个具体的数字,知道它长这样就行),房间里住着一个客人,名字叫10。

所以,变量名是什么?是我们给一个内存地址起的好记的别名。 你写a,计算机就帮你找到那个地址,把里面的东西取出来。就像你说“前台”,酒店的人就知道是哪个房间。

那问题又来了:如果我想直接知道那个门牌号呢?比如我想把“a住在哪个房间”这个信息存下来,怎么办?

这时候,就轮到指针出场了。

指针是什么?

指针其实特别简单,它就是一个专门用来存地址的变量。

你想想:

  • 你有一个int变量,里面存的是整数。

  • 你有一个float变量,里面存的是小数。

  • 那你要存地址,就得有一个专门存地址的变量,这就是指针变量。

打个比方你就明白了。你去酒店找朋友,你手里拿着一张纸条,上面写着“张三住1808”。这张纸条本身不是张三,也不是1808这个数字,它只是告诉你“怎么找到张三”。

指针变量就是这张纸条。它存的是“变量住在哪个地址”。

int a = 10;       // 定义一个普通变量a,存的是10
int *p = &a;      // 定义一个指针变量p,存的是a的地址

这里出现了两个新符号:*和&。别慌,咱们一个一个说。

符号一:& —— “帮我拿一下地址”

&叫取地址运算符。它的作用特别直接:放在一个变量前面,就能得到这个变量在内存里的地址。

int a = 10;
printf("%p\n", &a);   // 输出a的地址,比如 0x7ffd5e8a3c

%p是专门用来打印地址的格式控制符。输出的那一串0x...就是a住在内存里的门牌号。

你记住:&a读作“a的地址”。

符号二:* —— “帮我进去看看”

*有两种用法,咱们现在说的是第一种——在定义指针变量的时候用它。

int *p;   // 定义一个指针变量p,它可以指向一个int类型的变量

这里的*不是在“做乘法”,它是在声明:p不是一个普通变量,它是一个指针,指向的是int类型的数据。

你可能会问:那这个p能指向float吗?不行。你声明的是int *p,它就专门用来指向int。就像你的酒店房卡上写着“标准间”,就不能拿去开总统套房的门。指针的类型必须跟它指向的变量类型一致。

那如果现在我已经有一个指针p,里面存了a的地址,我怎么通过p拿到a的值呢?

这时候就用到了*的第二种用法——解引用运算符。

int a = 10;
int *p = &a;     // p存了a的地址

printf("%d\n", *p);   // 输出10

*p的意思就是:“顺着p里存的地址,找到那个房间,把里面的东西拿出来。” 因为p指向的是a,所以*p拿出来的就是a的值,也就是10。

你还可以通过*p修改a的值:

*p = 20;      // 把p指向的那个房间里的东西改成20
printf("%d\n", a);   // 输出20,a的值被改了

你看,我们没碰a,但通过指针p直接改了a的值。这就是指针的威力——它能直接操作内存里的数据。

咱们画个图,把这件事彻底搞明白

你写这三行代码:

int a = 10;
int *p = &a;
printf("%d\n", *p);

内存里发生了什么?咱们画出来:

第一步:int a = 10;

内存里分配了一个房间,门牌号比如是0x1000,里面住着10。

地址: 0x1000
变量:  a
值:   10

第二步:int *p = &a;

内存里又分配了一个房间给p,门牌号比如是0x2000。&a取出a的地址0x1000,存进p的房间。

地址: 0x1000          地址: 0x2000
变量:  a               变量:  p
值:   10               值:   0x1000  ──指向──> 0x1000

第三步:*p

*p的意思是“顺着p里存的地址(0x1000)找过去,把那个房间里的值取出来”。找过去一看,里面是10,所以*p就是10。

你看,指针就这么点事儿。

指针变量的定义格式

咱们把指针变量的定义格式总结一下:

类型 *指针变量名;

比如:

int *p1;       // 指向int的指针
float *p2;     // 指向float的指针
char *p3;      // 指向char的指针
double *p4;    // 指向double的指针

你可以写int *p,也可以写int* p,甚至int * p,效果都一样。但我建议你写成int *p——因为*是跟着变量名走的,int *p读起来就是“p是一个指向int的指针”。

注意:指针变量本身也占内存。在64位系统上,指针占8个字节。因为它要存地址,地址就是8个字节的。不管它指向的是int还是char,指针本身的大小都是一样的。

几个常见的初学者误解

误解一:int *p;里的*p是在做乘法?

不是。这行代码只是在声明一个指针变量。这里的*只是告诉你“p是个指针”。只有当你写*p = 20;的时候,*才是解引用。

误解二:定义指针的时候忘了初始化,然后直接*p。

int *p;         // p里存的是垃圾地址,不知道指向哪里
*p = 100;       // 危险!往一个未知的内存地址写数据,程序可能崩溃

指针定义之后,一定要让它指向一个合法的地址,再通过它去操作数据。

int a;
int *p = &a;    // 正确做法:定义的时候就让它指向a
*p = 100;       // 安全

误解三:*p和p搞混。

  • p是指针本身,存的是地址。

  • *p是解引用,得到的是它指向的那个变量的值。

用咱们的酒店比方:

  • p就是那张写着“1808”的纸条。

  • *p就是“拿着纸条去1808房间,看里面是谁”。

一个完整的例子,把今天学的串起来

#include <stdio.h>

int main() {
    int a = 10;        // 普通变量
    int b = 20;

    int *p;            // 声明一个指针变量

    p = &a;            // p指向a
    printf("p指向a,*p = %d\n", *p);    // 输出10

    p = &b;            // p指向b(指针可以重新指向)
    printf("p指向b,*p = %d\n", *p);    // 输出20

    *p = 100;          // 通过指针修改b的值
    printf("修改后,b = %d\n", b);       // 输出100

    return 0;
}

你看到没有?指针p一开始指向a,后来改成指向b了。指针变量可以重新指向别的变量,它不是死的。而且通过*p可以直接改它指向的那个变量的值。

为什么要有指针?

说了这么多,你可能会想:我直接用a改值不就行了吗?为什么还要绕个弯用指针?

好问题。现在你学的还只是最基础的指针概念,还没到真正体现指针价值的时候。但我提前给你透露一下——指针真正的威力,体现在这三个地方:

第一,函数可以通过指针修改外面变量的值。 还记得咱们前面讲参数传递吗?你写个交换函数,直接传变量进去是没用的,但传地址进去就能真的交换。这个“传地址”就是用指针。

第二,动态内存分配必须用指针。 你想在程序运行时动态申请内存,只能通过指针来管理。这个后面会详细讲。

第三,链表、树这些数据结构必须用指针。 没有指针,你根本没法把一个个节点串起来。

所以,今天只是一个开始。指针的基础概念——指针就是地址,指针变量就是存地址的变量,&取地址,*解引用——你把这几个核心点吃透了,后面学起来就不慌了。

最后强调三句话

第一,指针就是地址。 它没你想的那么神秘。

第二,&a是取a的地址,*p是顺着p的地址去取里面的值。 一取一解,配对着用。

第三,定义指针的时候类型要匹配。 指向int的指针写int *,指向float的写float *,别搞混。

好,这节课就到这里。回去之后自己动手,写几个变量,定义指针指向它们,用&和*打印地址和值,观察输出结果。再把指针从一个变量改指向另一个变量,看看会发生什么。指针这东西,看是看不会的,必须自己动手跑代码,跑着跑着就豁然开朗了。下节课咱们讲指针和数组的关系,那是指针里最好玩的部分之一。