双指针作为 C 中的参数

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 的地址,另一个包含地址第一个指针。

我在这里有点难过我不确定它是否正确。

不对,你的理解不正确。 C 不会 "create" 任何你假设的意义上的东西——大多数变量只是内存位置的标签(不仅如此,它们可能 "alias",或标签,CPU 寄存器)——在调用 functionX 的范围内,您的 randomvar 是分配用于存储整数并被解释为整数的内存区域的标签。 &randomvar一元表达式(运算符&,单操作数randomvar)的值是数据的地址。该地址是作为 y 传递给 functionX 的地址,这意味着内存位置(或 CPU 寄存器)被保留并存储 randomvar 的地址(不是值) .

举个例子,假设你的程序声明了一个像 int randomvar; 这样的变量——当执行时,RAM 的一部分——int 通常是 4 个字节——被保留来保存变量randomvar 的值。该地址直到程序在内存中执行时才为人所知,但为了举例,让我们假设地址 0xCAFEBABEDEADBEEF(8 个字节)是指向 4 个字节以保存整数值的地址。在为变量赋值之前,地址处的值是不确定的——变量的声明只保留 space 来保存值,它不会在地址处写入任何内容,所以在你赋值之前变量的值,你甚至根本不应该使用这个值(大多数 C 编译器会警告你这一点)。

现在,当地址传递给 functionX 时,这意味着对于函数,标签 y 是在某个内存位置保留的 8 个字节,用于存储整数变量的地址。当像 functionX(&randomvar) 这样调用时,y 存储 0xCAFEBABEDEADBEEF。但是,y 也[通常] 有一个地址——以前的值(randomvar 的地址)必须存储在某处!除非 CPU 寄存器存储值,在这种情况下自然没有 [RAM] 地址。

对于指向像 int * * y 这样的指针的指针,y 标记了一个保留的内存位置,用于存储指向整数变量地址的地址。

hen when I call functionX, let's say by: functionX(&randomvar),where randomvar is an integer containing a certain value (let's say 5), then C will create a pointer that is also called 'randomvar' that contains the addresse of randomvar

函数functionX中指针的名字是y因为你自己写的是函数参数的名字functionX(int *y).

If so, then when the function has double pointers : functionX(int **y) and by doing functionX(&randomvar) creates 2 pointers, one that contains the adresse of randomvar, another that contains the adresse of the first pointer. If you have a function declared for example like

运算符 & 创建一个指针而不是两个指针。

如果你声明了一个函数,例如

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

以传递变量的地址作为参数调用函数时,没有与传递变量同名的指针。当前面有 & 运算符时,它只是传递某个变量的地址。在被调用函数中,保存此地址的指针可以具有任何有效标识符。


when the function has double pointers : functionX(int **y) and by doing functionX(&randomvar) creates 2 pointers, one that contains the adresse of randomvar, another that contains the adresse of the first pointer.

当函数需要 int**.

类型的对象时,您不能传递类型为 int*int 对象的地址

一般来说传递双指针,概念和上面说的是一样的。

From what I know so far: when I have a function that takes a pointer as argument, says a function called functionX(int *y) then when I call functionX, let's say by: functionX(&randomvar),where randomvar is an integer containing a certain value (let's say 5), then C will create a pointer that is also called 'randomvar' that contains the addresse of randomvar. So the ouput of *randomvar will be 5.

Is my understanding correct?

没有

您描述的情况大致如下:

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语义上有效的表达。

If so

不是这样的。

, then when the function has double pointers : functionX(int **y) and by doing functionX(&randomvar) creates 2 pointers, one that contains the adresse of randomvar, another that contains the adresse of the first pointer.

完全没有。 &randomvar 是单个表达式。对其求值会产生 one 值,正如我已经介绍过的,其类型为 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'

如果你愿意,你可以永远保持链接指针。下面是一些演示这一点的代码:

#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) 将产生相同的结果。