[英]Double pointers as arguments in C
我在用双指针作为 C 中的参数时遇到了一些问题。
据我目前所知:当我有一个将指针作为参数的函数时,说一个名为functionX(int *y)
函数,然后当我调用 functionX 时,让我们说: functionX(&randomvar)
,其中randomvar
是一个整数包含某个值(假设为 5),那么 C 将创建一个指针,该指针也称为 'randomvar',其中包含randomvar
的地址。 所以*randomvar
的输出将是 5。
我的理解正确吗?
如果是这样,那么当函数具有双指针时: functionX(int **y)
并通过执行functionX(&randomvar)
创建 2 个指针,一个包含randomvar
的地址,另一个包含第一个指针的地址。
我在这里有点难住,我不确定它是否正确。
据我目前所知:当我有一个将指针作为参数的函数时,它说一个名为
functionX(int *y)
functionX
,然后当我调用functionX
,让我们说:functionX(&randomvar)
,其中randomvar
是一个包含某个值(比方说 5),然后 C 将创建一个指针,该指针也称为“randomvar”,其中包含randomvar
的地址。 所以*randomvar
的输出将是 5。我的理解正确吗?
不。
你描述的情况是这样的:
void functionX(int *y) {
// ...
}
int main(void) {
int randomvar = 5;
functionX(&randomvar);
}
该代码的main()
函数中的表达式&randomvar
计算结果为该函数的局部变量randomvar
的地址。 该表达式的类型为int *
,与函数functionX()
参数y
相同,因此它非常适合用作该函数的参数。 到目前为止一切都很好。
但是表达式&randomvar
只指定一个值,而不是一个对象。 没有为它保留存储空间,因此它没有地址。 它也没有名字,特别是它没有命名为“randomvar”。 在函数functionX()
,该值(的副本)可以作为y
访问,并且表达式*y
计算结果为 5。所提供的代码中没有任何地方*randomvar
是语义上有效的表达式。
如果是这样的话
不是这样。
,然后当函数具有双指针时: functionX(int **y) 并通过执行 functionX(&randomvar) 创建 2 个指针,一个包含 randomvar 的地址,另一个包含第一个指针的地址。
一点也不。 &randomvar
是单个表达式。 评估它会产生一个值,正如我已经介绍的那样,它的类型为int *
。 这与int **
类型的函数参数不匹配。 int **
是指向int *
的指针。 要获得一个,您可以获取int *
类型的对象(不是值)的地址:
void functionY(int **z) {
// ...
}
int main(void) {
int randomvar = 5;
int *varptr = &randomvar;
functionY(&varptr);
}
这样做时,确实有两个指针,一个是int *
,另一个是int **
,但前者需要明确声明。
以这种方式可视化:
void foo(int a);
void bar(int *b);
void baz(int **c);
// main function
{
int x = 5;
int *p = &x;
foo(x);
bar(p);
baz(&p);
}
// **main** mem space
virtual mem address var name (conceptual) value
=================== ===================== =====
ABCD:4000 x 5
ABCD:4008 p ABCD:4000
// **foo** mem space
virtual mem address var name (conceptual) value
=================== ===================== =====
BCDE:2000 a 5
// a new variable is created.
// any changes made on 'a' will not affect 'x' in 'main'
// **bar** mem space
virtual mem address var name (conceptual) value
=================== ===================== =====
BCDE:4000 b ABCD:4000
// a new pointer is created pointing to 'x'
// 'b' points to 'x' and '*b' means 'the value stored in ABCD:4000'
// any changes made on '*b' will affect 'x' in main
// **baz** mem space
virtual mem address var name (conceptual) value
=================== ===================== =====
BCDE:8000 c ABCD:4008
// a new pointer is created pointing to 'p'
// 'c' points to 'p' and '*c' means 'the value stored in ABCD:4008'
// any changes made on '*c' will change the value of 'p' in main
// if '**c = 7' is executed, x will be assigned '7'
// if '*c = ABCD:8000' is executed, p will no longer point to 'x'
不,你的理解是不正确的。 C 不会“创建”您假设的任何东西——大多数变量只是内存位置的标签(不仅如此,它们可能“别名”,或标签,CPU 寄存器)——您的randomvar
在functionX
所在的范围内调用是分配用于存储整数的内存区域的标签,它被解释为整数。 &randomvar
一元表达式的值(运算符&
带有单个操作数randomvar
)是数据的地址。 该地址是作为y
传递给functionX
,这意味着保留一个内存位置(或 CPU 寄存器)并存储randomvar
的地址(而不是值)。
例如,假设您的程序声明了一个变量,如int randomvar;
-- 执行时,一部分 RAM -- 通常为int
4 个字节 -- 保留用于保存randomvar
的变量值。 该地址直到程序在内存中执行时才知道,但为了示例起见,让我们假设地址0xCAFEBABEDEADBEEF
(8 个字节)是指向 4 个字节以保存整数值的地址。 在给变量赋值之前,地址处的值是不确定的——变量的声明只保留了保存值的空间,它不会在地址上写任何东西,所以在给变量赋值之前,您甚至根本不应该使用该值(并且大多数 C 编译器都会警告您这一点)。
现在,当地址传递给functionX
这意味着对于函数,标签y
是在某个内存位置保留的 8 个字节,用于存储整数变量的地址。 当像functionX(&randomvar)
一样functionX(&randomvar)
, y
存储0xCAFEBABEDEADBEEF
。 然而, y
也[通常]有一个地址——前一个值( randomvar
地址)必须存储在某个地方! 除非 CPU 寄存器存储该值,否则自然没有 [RAM] 地址。
对于指向像int * * y
这样的指针的指针, y
标记了一个保留的内存位置,用于存储指向整数变量地址的地址。
当我调用 functionX 时,让我们说:functionX(&randomvar),其中 randomvar 是一个包含某个值的整数(假设为 5),然后 C 将创建一个也称为“randomvar”的指针,其中包含 randomvar 的地址
函数functionX
指针的名称是y
因为您自己写的是函数参数functionX(int *y)
。
如果是这样,那么当函数具有双指针时: functionX(int **y) 并通过执行 functionX(&randomvar) 创建 2 个指针,一个包含 randomvar 的地址,另一个包含第一个指针的地址。 如果您声明了一个函数,例如
运算符 & 创建一个指针而不是两个指针。
如果您声明了一个函数,例如
void functionX(int **y);
和一个变量声明如下
int randomvar = 5;
那么这样的函数调用就像
functionX( &randomvar );
生成编译错误,因为参数的类型是int *
而根据函数声明的参数类型是int **
。
您可能不会写例如
functionX( &&randomvar );
因为使用第一个运算符&
创建了一个 int * 类型的临时对象。 并且您不能将运算符 & 应用于临时对象。
要调用您可以编写的函数
int *p = &randomvar;
functionX( &p );
在这种情况下,参数的类型将是int **
因为它是参数类型所要求的。
这是一个演示程序。
#include <stdio.h>
void f( int *p )
{
printf( "The value of p is %p\n"
"the pointed value is %d\n",
( void * )p, *p );
}
void g( int **p )
{
printf( "The value of p is %p\n"
"the pointed value is also a pointer %p\n"
"the pointed value by the dereferenced pointer is %d\n",
( void * )p, ( void * )*p, **p );
}
int main(void)
{
int x = 5;
f( &x );
putchar( '\n' );
int *p = &x;
g( &p );
return 0;
}
它的输出可能看起来像
The value of p is 0x7ffced55005c
the pointed value is 5
The value of p is 0x7ffced550060
the pointed value is also a pointer 0x7ffced55005c
the pointed value by the dereferenced pointer is 5
以传递变量的地址作为参数调用函数时,不存在与传递变量同名的指针。 它只是在前面带有&
运算符时传递某个变量的地址。 在被调用的函数中,保存这个地址的指针可以有任何有效的标识符。
当函数有双指针时:
functionX(int **y)
并通过执行functionX(&randomvar)
创建 2 个指针,一个包含randomvar
,另一个包含第一个指针的地址。
当函数需要int**
类型的对象时,您不能传递int*
类型的int
对象的地址。
一般而言,传递双指针的概念与上面所说的相同。
如果你愿意,你可以永远保持链接指针。 下面是一些演示这一点的代码:
#include<stdio.h>
void foo(int ***A, int **B, int *C) {
printf(" %p A\n %p *A\n %p **A\n %d ***A\n", A, *A, **A, ***A);
printf(" %p B\n %p *B\n %d **B\n", B, *B, **B);
printf(" %p C\n %d *C\n ", C, *C);
}
int main(void) {
int D = 8;
int* C = &D;
int** B = &C;
int*** A = &B;
foo (A,B,C);
printf("%p &D (in main)\n", &D);
return 0;
这将为取消引用的指针提供这样的结果,每个 '*' 都会随着它的进行而脱落,所以请注意**A = *B = C = &D
0x7ffc81b06210 A
0x7ffc81b06218 *A
0x7ffc81b06224 **A
8 ***A
0x7ffc81b06218 B
0x7ffc81b06224 *B
8 **B
0x7ffc81b06224 C
8 *C
0x7ffc81b06224 &D (in main)
最后请注意,调用foo (&B, &C, &D)
将产生相同的结果。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.