C语言指针和地址,理解*p和**p

本文讲解了计算机内存地址的概念,指针如何存储和访问地址,以及int*p与int**p的区别。通过打针的比喻,阐述了单指针和双指针的操作过程,强调了在C语言中不同类型指针的使用规则。

在计算机中所有数据都存放在存储器中。

把主存储器中的一个字节称为一个内存单元 ,通过内存单元的编号能正确地访问内存单 元,内存单元的编号也称为地址。

&:取地址运算符。

*: 指针运算符。

int a;  //定义一个整型变量a

&a则可以获取到变量a的地址(编号)

可以想象它是:6422020,或者任意数字,总之它是个编号,是个常量

如果要把a的地址,放到一个变量p存起来,则把a的地址赋给p,即:p = &a

但,有个重要的前提是:定义p是一个指针变量,还得是整型指针变量,因为变量a是整型的

这个是C语言的法则。

如何让p成为一个整型指针变量呢?

可以这么做: int *p;    //在定义p的时候,前面加个*号,这个*号就告诉了编译器任命变量p为一个指针变量,专门用于存放地址的,地址一定是一个常量。而写在更前面的类型说明符 int 则是要求变量p存的这个地址它对应的变量是整型变量

那么如何取到a的值呢?

两种方法:可以用*p,也可以直接用a 来取。*p和a是等价的。听起来有点拗口?用程序+图来说明:

 

6466020是变量a的地址,此时a的“肚子”里有个值是100,变量p的“肚子”里有个值是6422020(&a取出来的a的地址)( 6666666是p的地址,这里没必要画出来,但是觉得画出来比较完整吧)*p这个针是扎进了这个地址对应的变量a的肚子里,取出来的是a的值100。对于*p,教科书上的解释是:p指向了变量a。 我觉得指来指去很难理解,于是我把指针理解成打针的针,一旦某个变量前面加了个*,这个变量就变成了个针筒,比如*p,这个针*p戳到哪,吸出来什么,是由它的变量肚子装的哪个地址决定的,就是往哪戳总要有个目标吧。比如这里的针 *p,  变量p肚子里的地址是6466020,于是它戳进了地址6466020对应变量a的肚子,吸出来了个值:100

简单来讲:*p 就是为了变量p拿到的地址对应内存里的值   (从硬件方向去理解)

简单来讲:*p 就是为了变量p拿到的地址对应变量里的值   (从软件方向去理解)

上面两句话是一个意思,总之硬件上就是一个“肚子”,一个“房间”,一个“空间”,软件上它总得有个名,变量a,b,c 什么的都行 

如果这样定义p : int p,没有*号,那么p肚子里是装不了a的地址的,float *p也不行,float *p 定义后,p只能装float类型的变量的地址,比如以下这样定义才是合法的:

float *p;

float a;

a = 3.14159;

p=&a;

那么如何改变a的值呢?

同理也是两种办法:

a = 5.9999; 

或者 *p = 5.9999

——————————————————————————————————————————

*p总结完了,**p呢?

我为什么把指针的针想象成打针的针,也是因为理解完**p后决定的。

如果定义了**p,那么给p赋值的时候,仍然要求是个地址,而且还得是指针变量的地址不能是其他的值,比如值是整型的100之类的变量的地址是行不通的,不符合法则的

先上一段不符合法则的代码:

代码段①:

int **p;

int a;

a = 100;

p=&a;

第四行这里,编译器是不会通过的。

再上一段符合法则的代码:

代码段②:

int **p,*t;

int a;

a = 100;

t = &a;

p=&t;

**p  我认为可以按套娃的模式来理解了:

如果是定义的是int *p,只有一个*号,那就只有一针,一针戳到的肯定是整型变量的地址,不可能戳到的是其他类型的,因为*p要吸出来的值肯定得是个整型。

如果定义的是int **p,有两个*号,那用**p取值的时候就得是两针,一针下去戳到的得是整型指针变量的肚子,图上的变量t就是个整型指针变量,如果是别的类型的地址,则不合法,所以代码段①这里把&a直接给到p的话编译器不会通过,因为第二针都还没打,戳一针就到头了。代码段②里把整型指针变量的地址给到p才是正确的p=&t; 这个语句,使得*p一针戳到整型指针变量t的肚子里,吸出了个变量a的地址4444444, 使得**p这第二针下去戳到了变量a的肚子,吸出来值100,  还有,*t也是可以吸出100的

**p *(*p)是等价的,毕竟是从右到左的运算规则,不管定义了*p  和  **p,在给p赋值的时候都得给的是地址,只不过定义*p的话一针就要到头了,而定义**p是需要两针才到头 ,多少*就戳多少针,最后一针吸出来的值类型要和定义时的类型说明符一致。

定义int **p ,*p一针取出来的是地址,**p两针后取出来的就得是int类型的值。

定义int *p,那么*p一针取出来的就得是int类型的值,没有**p,因为没有两针,预约的是一针。

 

 

<think> 嗯,用户询问的是C语言中是否存在指针指针。这是个好问题啊。用户对指针概念的理解似乎在进阶阶段。 从引用资料看,引用[2]明确提到"二级指针:指向一级指针指针",还举例说明了三级指针、四级指针的声明方式。这直接证明了指针指针的存在。引用[3]提到"常量指针指针常量"的区别,虽然不直接相关,但说明指针本身也是变量,自然可以有指向它的指针。 用户可能正在学习多级指针的概念,或者遇到需要修改指针地址的场景。考虑到指针指针在内存管理数据结构中的重要性,特别是处理动态分配的多维数组或函数参数传递时。 需要特别注意解释的清晰性:一级指针存放普通变量的地址,二级指针存放一级指针地址。用内存示意图可能更直观,但文字描述要强调"指针本身也有地址"这个关键点。 用户可能隐含想知道使用场景,所以准备补充函数参数传递动态二维数组的例子。另外要提醒const在多级指针中的位置会影响修饰对象,不过当前问题可能不需要展开这点。 决定采用"存在+原理+示例+场景"的结构,用代码块展示声明语法,最后通过相关问题引导深入学习。特别注意区分声明时的*取值时的*号,这是新手常见困惑点。 </think> 是的,C语言**确实存在指针指针**,这是其强大且灵活的特性之一,主要用于更复杂的内存管理数据操作。 ### 核心概念 1. **指针的本质**指针是变量,存储的是另一个变量的**内存地址**。 2. **指针指针(多级指针*** 既然指针本身也是一个变量(占用内存空间),那么它自然也有自己的地址* 一个“指针指针”就是这样一个变量:它存储的是**另一个指针变量的地址*** 最常见的指针指针**二级指针**。 ### 声明与使用 * **一级指针**:指向普通数据(如 `int`, `char`, `struct`) ```c int num = 10; int *p = &num; // p 是一级指针,存储 num 的地址 (&num) ``` * **二级指针**:指向一级指针 ```c int **pp = &p; // pp 是二级指针,存储一级指针 p 的地址 (&p) ``` * 声明:`int **pp;` * 含义:`pp` 指向一个 `int*` 类型的指针* **三级指针**:指向二级指针(依此类推) ```c int ***ppp = &pp; // ppp 是三级指针,存储二级指针 pp 的地址 (&pp) ``` ### 访问数据 通过多级指针访问最终数据需要逐级解引用: ```c int num = 10; int *p = &num; // p 存储 num 的地址 int **pp = &p; // pp 存储 p 的地址 int ***ppp = &pp; // ppp 存储 pp 的地址 printf("num = %d\n", num); // 直接访问 num printf("*p = %d\n", *p); // 解引用一级指针 p 得到 num printf("**pp = %d\n", **pp); // 解引用二级指针 pp 得到 p, 再解引用 p 得到 num printf("***ppp = %d\n", ***ppp); // 解引用三级指针 ppp 得到 pp, 再解引用 pp 得到 p, 再解引用 p 得到 num ``` ### 主要用途 1. **动态分配二维数组**: ```c int rows = 3, cols = 4; int **arr = (int **)malloc(rows * sizeof(int*)); // 分配指针数组 (二级指针) for (int i = 0; i < rows; i++) { arr[i] = (int *)malloc(cols * sizeof(int)); // 为每一行分配一维数组 } // 使用 arr[i][j] 访问元素 ``` 2. **在函数中修改指针本身的值*** 如果想在函数内部改变一个传入的指针变量指向的地址(而不仅仅是改变它指向地址上的值),需要将**指针地址**(即指针指针)传递给函数。 ```c void allocateMemory(int **ptr) { // 参数是二级指针 *ptr = (int *)malloc(sizeof(int)); // 解引用二级指针 ptr 修改传入的指针变量指向的内存块 **ptr = 100; // 通过双重解引用修改分配内存的值 } int main() { int *p = NULL; allocateMemory(&p); // 传入一级指针 p 的地址 (二级指针) printf("%d\n", *p); // 输出 100 free(p); return 0; } ``` 3. **处理字符串数组(`char *argv[]`)*** C 语言的 `main` 函数参数 `char *argv[]` 实际上等价于 `char **argv`,是一个指向字符串指针数组的指针(二级指针)。`argv[0]`, `argv[1]` 等是 `char*`(一级指针,指向字符串)。 4. **复杂数据结构**:在链表、树等结构中,有时需要操作指向指针指针来简化节点插入/删除操作,尤其是在处理头节点时。 ### 总结 C语言不仅支持指针,还支持指向指针指针(二级指针、三级指针等)。它们提供了一种间接访问操作内存的方式,在动态内存分配(尤其是多维数组)、需要在函数内部修改外部指针本身、以及处理指针数组等场景中发挥着重要作用。理解指针指针对于掌握C语言的内存管理高级编程技巧至关重要[^1][^2][^4]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Riven_h

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值