C语言教程-详解C语言中的双指针

正如我们所知,指针用于在C语言中存储变量的地址。指针可以减少访问变量的时间。然而,在C语言中,我们也可以定义一个指针来存储另一个指针的地址。这样的指针被称为双指针(指向指针)。第一个指针用于存储变量的地址,而第二个指针用于存储第一个指针的地址。让我们通过下面的图示来理解。
声明双指针的语法如下:
int **p; // 指向整数的指针的指针
考虑以下示例:
cCopy code
#include <stdio.h>
void main()
{
int a = 10;
int *p;
int **pp;
p = &a; // 指针p指向变量a的地址
pp = &p; // 双指针pp指向指针p的地址
printf("a的地址:%x\n", &a); // 输出a的地址
printf("p的地址:%x\n", p); // 输出p的地址
printf("p指针存储的值:%d\n", *p); // 输出p指针存储的值,即a的值
printf("pp的地址:%x\n", pp); // 输出pp的地址
printf("pp指针存储的值:%d\n", **pp); // 输出pp指针存储的值,即a的值
}
输出结果
cssCopy code
a的地址:d26a8734
p的地址:d26a8738
p指针存储的值:10
pp的地址:d26a8738
pp指针存储的值:10
C双指针示例
让我们看一个示例,其中一个指针指向另一个指针的地址。
p2包含p的地址(fff2),p包含number变量的地址(fff4)。
#include<stdio.h>
int main() {
int number = 50;
int *p; // 指向整型的指针
int **p2; // 指向指针的指针
p = &number; // 存储number变量的地址
p2 = &p;
printf("number变量的地址为 %x \n", &number);
printf("p变量的地址为 %x \n", p);
printf("p变量的值为 %d \n", *p);
printf("p2变量的地址为 %x \n", p2);
printf("p2指针所指向的值为 %d \n", **p2);
return 0;
}
以下是给您重新翻译后的C语言教程,代码已经使用代码块展示:
cCopy code
#include<stdio.h>
int main() {
int number = 50;
int *p; // 指向整型的指针
int **p2; // 指向指针的指针
p = &number; // 存储number变量的地址
p2 = &p;
printf("number变量的地址为 %x \n", &number);
printf("p变量的地址为 %x \n", p);
printf("p变量的值为 %d \n", *p);
printf("p2变量的地址为 %x \n", p2);
printf("p2指针所指向的值为 %d \n", **p2);
return 0;
}
输出结果
cssCopy code
number变量的地址为 fff4
p变量的地址为 fff4
p变量的值为 50
p2变量的地址为 fff2
p2指针所指向的值为 50
问:下面这个程序的输出是什么?
#include <stdio.h>
int main()
{
int a[10] = {100, 206, 300, 409, 509, 601}; // 第1行
int *p[] = {a, a+1, a+2, a+3, a+4, a+5}; // 第2行
int **pp = p; // 第3行
pp++; // 第4行
printf("%d %d %d\n", pp - p, *pp - a, **pp); // 第5行
*pp++; // 第6行
printf("%d %d %d\n", pp - p, *pp - a, **pp); // 第7行
++*pp; // 第8行
printf("%d %d %d\n", pp - p, *pp - a, **pp); // 第9行
++**pp; // 第10行
printf("%d %d %d\n", pp - p, *pp - a, **pp); // 第11行
return 0;
}
解释
在上述问题中,使用了双指针进行指针运算。定义了一个包含6个元素的数组a,由指针数组p指向。指针数组p由双指针pp指向。然而,上面的图片给出了关于如何分配内存给数组a和指针数组p的简要概念。p的元素是指向数组a的每个元素的指针。由于我们知道数组名包含数组的基地址,因此可以通过使用(a)、(a+1)等方式来遍历数组的值。如图所示,a[0]可以通过以下方式访问:
- a[0]:这是访问数组第一个元素的最简单方式。
- *(a):由于a存储了数组的第一个元素的地址,可以通过对其进行间接指针访问来获取其值。
- p[0]:如果要通过指针p来访问a[0],可以在指针数组p的第一个元素上使用间接操作符(),即*p[0]。
- (pp):由于pp存储了指针数组的基地址,*pp将给出指针数组的第一个元素的值,即整数数组的第一个元素的地址。pp将给出整数数组的第一个元素的实际值。
接下来讨论程序,第1行和第2行分别声明了整数数组和指针数组。第3行将双指针初始化为指针数组p。如图所示,如果数组的地址从200开始,整数的大小为2,则指针数组的值将为200、202、204、206、208、210。假设指针数组的基地址为300,双指针pp包含指针数组的地址,即300。第4行将pp的值增加1,即pp现在指向地址302。
第5行包含一个表达式,打印了三个值,即pp - p、pp - a、*pp。让我们计算每个值。
- pp = 302,p = 300 => pp - p = (302-300)/2 => pp - p = 1,即打印1。
- pp = 302,pp = 202,a = 200 => pp - a = 202 - 200 = 2/2 = 1,即打印1。
- pp = 302,pp = 202,(*pp) = 206,即打印206。
因此,作为第5行的结果,将在控制台上打印输出1、1、206。第6行写入pp++。在这里,我们必须注意,两个一元运算符和++具有相同的优先级。因此,按照结合律规则,它将从右到左进行评估。因此,表达式pp++可以重写为((pp++))。由于pp = 302,现在它变成了304。*pp将给出204。
第7行再次写入表达式,打印了三个值,即pp - p、pp - a、pp。让我们计算每个值。
- pp = 304,p = 300 => pp - p = (304 - 300)/2 => pp - p = 2,即打印2。
- pp = 304,pp = 204,a = 200 => pp - a = (204 - 200)/2 = 2,即打印2。
- pp = 304,pp = 204,(*pp) = 300,即打印300。
因此,作为第7行的结果,将在控制台上打印输出2、2、300。第8行写入++pp。根据结合律规则,这可以重写为(++((pp)))。由于pp = 304,pp = 204,pp = *(p[2]) = 206,现在它将指向a[3]。
第9行再次写入表达式,打印了三个值,即pp - p、pp - a、pp。让我们计算每个值。
- pp = 304,p = 300 => pp - p = (304 - 300)/2 => pp - p = 2,即打印2。
- pp = 304,pp = 206,a = 200 => pp - a = (206 - 200)/2 = 3,即打印3。
- pp = 304,pp = 206,(*pp) = 409,即打印409。
因此,作为第9行的结果,将在控制台上打印输出2、3、409。第10行写入++pp。根据结合律规则,这可以重写为(++(((pp))))。pp = 304,pp = 206,pp = 409,++pp => pp = *pp + 1 = 410。换句话说,a[3] = 410。
第11行再次写入表达式,打印了三个值,即pp - p、pp - a、pp。让我们计算每个值。
- pp = 304,p = 300 => pp - p = (304 - 300)/2 => pp - p = 2,即打印2。
- pp = 304,pp = 206,a = 200 => pp - a = (206 - 200)/2 = 3,即打印3。
- 在第8行中,**pp = 410。
因此,作为第11行的结果,将在控制台上打印输出2、3、410。
- 输出
1 1 206
2 2 300
2 3 409
2 3 410