C指针基础(一)
- 什么是指针
- 指针的意义:为什么要多此一举?
- 总结
- 定义指针变量
- 基本格式
- 为什么定义指针时需要指定类型名?
- 指针变量的赋值
- &与*
- 辨析:pa、*pa 与 &pa
什么是指针
指针,说白了就是内存的"门牌号"。
想象一下,你住在一栋叫"内存"的大楼里,每个房间都有一个门牌号。你想去朋友家串门,光知道"我朋友住在这栋楼里"可没用——你得知道具体是几号房,才能敲对门。
在 C 语言里,变量就是"房间",变量的值就是"房间里住的人",而指针就是那个门牌号——它告诉你这个变量住在内存的哪个地址。
比如你写:
inta=42;scanf("%d",&a);这里的&a就是"取 a 的门牌号",也就是 a 在内存中的地址。scanf拿到这个门牌号,才能找到 a 的房间,把用户输入的数字"塞"进去。如果你不给它门牌号,只给它a的值(比如 42),那scanf就懵了:“你给我个数字干嘛?我要的是地址啊!”
指针的意义:为什么要多此一举?
你可能会问:“我直接用变量不就行了,干嘛非要搞个指针?”
问得好!指针的意义,主要体现在三个场景:
1. 函数想"改"你的变量
C 语言函数传参默认是"值传递"——你把a的值复制一份给函数,函数改的是副本,原变量纹丝不动。想让函数真正修改外面的变量?就得把&a(门牌号)传进去,函数顺着门牌号找到原房间,直接改。
2. 动态内存管理
程序运行时才知道要多大数组?那就用malloc在堆上申请一块内存,返回一个指针。指针就是这块内存的"钥匙",没有它,你申请的内存就找不回来了。
3. 高效传递大数据
传一个巨大的结构体给函数,如果按值传递,得把整个结构体复制一份,又慢又费内存。传指针就只传一个门牌号,轻装上阵,效率拉满。
总结
指针就是内存地址的化身,它让你能直接"按图索骥"找到数据、修改数据、管理内存。虽然刚学时觉得绕,但一旦掌握,你会发现它其实是 C 语言最锋利的武器。
定义指针变量
指针变量是专门用于存放内存地址的变量。普通变量保存的是数据值,而指针变量保存的是某个数据对象在内存中的地址。
基本格式
类型名*指针变量名;类型名:表示该指针所指向的数据类型,可以是int、float、char等基本类型,也可以是结构体等自定义类型。*:表明该变量是一个指针变量。指针变量名:指针变量的名称,遵循 C 语言标识符命名规则。
例如:
int*p1;// p1 是指向 int 类型数据的指针float*p2;// p2 是指向 float 类型数据的指针char*p3;// p3 是指向 char 类型数据的指针以上都是合法的指针变量定义。需要注意:星号*靠近变量名书写,表示该变量是指针。若一次定义多个指针变量,每个变量名前都必须带星号:
int*pa,*pb;// pa 和 pb 都是指针变量int*pc,d;// pc 是指针变量,d 是普通 int 变量为什么定义指针时需要指定类型名?
指针变量存储的是地址,但仅凭地址信息并不足以正确操作内存中的数据。类型名决定了两个关键方面:
1. 解引用时读取的字节数
*p表示解引用操作,即根据指针中保存的地址读取对应内存中的数据。一个int类型通常占 4 个字节,而char类型占 1 个字节。若编译器不知道指针所指的类型,就无法确定解引用时应读取多少个字节,可能导致读取数据不完整或越界。
2. 指针运算时的步长
指针支持算术运算,例如p + 1。如果p是int *类型,p + 1的地址偏移量为 4 个字节,恰好指向下一个int数据;如果p是char *类型,则偏移量仅为 1 个字节。只有明确指针的类型,编译器才能正确计算指针运算的步长。
指针变量的赋值
指针变量与int、float等普通数据类型一样,声明之后必须赋予一个合法的地址值。若声明后未初始化,指针变量会保存一个不确定的地址值,成为野指针,后续解引用时可能引发未定义行为。
指针变量可以在声明的同时初始化。需要特别注意的是:赋给指针变量的值必须是地址,而不是变量值。
例如:
inta=10;int*pa=&a;// 声明时初始化,pa 保存变量 a 的地址也可以先声明指针变量,再单独赋值:
inta=10;int*pa;// 先声明指针变量pa=&a;// 再赋值,赋的是变量 a 的地址 &a在使用指针时,必须严格区分pa与*pa:
pa是指针变量本身,保存的是地址值,例如&a。*pa是解引用操作,表示pa所指向的那个对象,也就是变量a本身。对*pa的读写等价于对a的读写。
int*pa;pa=&a;// 给指针变量 pa 赋值,赋的是地址*pa=a;// *pa 等价于 a,此句演示 *pa 与 a 表示同一个对象&与*
&与*是和指针紧密相关的两个单目运算符:
&是取地址运算符,用于获取某个变量在内存中的地址。*是解引用运算符,用于通过地址访问该地址所指向的对象。
典型用法如下:
inta=10;int*pa=&a;// &a 取得 a 的地址,pa 保存该地址*pa=20;// *pa 根据 pa 中的地址找到 a,并修改 a 的值上述代码中,*pa = 20;与a = 20;作用相同。
辨析:pa、*pa 与 &pa
在指针表达式中,pa、*pa、&pa三个写法只差一个符号,但含义完全不同。理解它们的区别,是熟练使用指针的关键。
先看一段完整示例:
inta=100;int*pa=&a;// pa 保存变量 a 的地址*pa=200;// 通过 pa 修改 a,此时 a 的值变为 200printf("%d\n",pa);// 输出 pa 保存的地址值,也就是 &aprintf("%d\n",*pa);// 解引用,输出 a 的值,即 200printf("%d\n",&pa);// 输出指针变量 pa 自身的地址运行结果:
从运行结果可以看出,pa、*pa、&pa三者的数据并不相同,可以这样区分:
pa:指针变量本身,它的值是变量a的地址,即&a。*pa:解引用操作,表示pa所指向的对象,在这里就等价于a,因此*pa = 200实际是把a改成了200。&pa:取指针变量pa自己的地址。注意,它和a的地址没有关系,而是pa这个变量在内存中的存放位置。
换句话说:pa是“门牌号”,*pa是“照着门牌号找到的房间”,而&pa是“存放这张门牌号的位置”。