【问题标题】:Double pointers as arguments in C双指针作为C中的参数
【发布时间】:2020-03-11 19:05:37
【问题描述】:

我在将双指针作为 C 中的参数时遇到了一些问题。

据我所知: 当我有一个将指针作为参数的函数时,比如说一个名为 functionX(int *y) 的函数 然后当我调用functionX时,假设:functionX(&randomvar),其中randomvar是一个包含某个值(比如说5)的整数,然后C将创建一个也称为“randomvar”的指针,其中包含@的地址987654325@。所以*randomvar 的输出将是 5。

我的理解正确吗?

如果是这样,那么当函数具有双指针时:functionX(int **y) 并通过执行 functionX(&randomvar) 创建 2 个指针,一个包含 randomvar 的地址,另一个包含第一个指针的地址。

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

【问题讨论】:

  • 双指针是指向指针的指针。就是这样。他们并不特别。要做到functionX(&randomvar)randomvar 必须是一个指针。你不能这样做 &&randomvar 因为 & 与变量一起使用,而 &randomvar 不是变量。
  • 今天早些时候的问题提供了一般性讨论,可能会有所帮助Pointer to pointer of structs indexing out of bounds

标签: c pointers double-pointer


【解决方案1】:

据我目前所知:当我有一个需要指针的函数时 作为参数,说一个名为functionX(int *y)的函数然后当我 打电话给functionX,比如说:functionX(&randomvar),在哪里 randomvar 是一个包含某个值(比如说 5)的整数, 然后 C 将创建一个也称为“随机变量”的指针 包含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 是单个表达式。评估它会产生 one 值,正如我已经介绍过的,它的类型为int *。这与 int ** 类型的函数参数不匹配。 int ** 是指向 int * 的指针。要获得一个,您可以获取int * 类型的对象(不是值)的地址:

void functionY(int **z) {
    // ...
}

int main(void) {
    int randomvar = 5;
    int *varptr = &randomvar;
    functionY(&varptr);
}

这样做时,确实有两个指针,一个是int *,另一个是int **,但前者需要显式声明。

【讨论】:

  • 好的,现在更清楚了,非常感谢!顺便说一句,在您的示例中,由于 functionY 接受仅接受一个值的“int *”类型的参数,为什么“&varptr”被视为一个值而不是“int *”类型的对象的地址?
  • 我很抱歉,@RyanTee,这是functionY() 声明中的一个错字——事实上,这让我很尴尬。现已修复。
【解决方案2】:

以这种方式可视化它:

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'

【讨论】:

  • 哇,这太棒了!视觉效果真的很有帮助!
【解决方案3】:

当我调用 functionX 时,假设是:functionX(&randomvar),where 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

【讨论】:

    【解决方案4】:

    如果你愿意,你可以永远保持链接指针。这里有一些代码可以证明这一点:

    #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 = &amp;D

    0x7ffc81b06210 A
    0x7ffc81b06218 *A
    0x7ffc81b06224 **A                                                                  
    8 ***A                                                                              
    
    0x7ffc81b06218 B
    0x7ffc81b06224 *B
    8 **B
    
    0x7ffc81b06224 C
    8 *C                                                                                
    
    0x7ffc81b06224 &D (in main)
    

    最后请注意,调用foo (&amp;B, &amp;C, &amp;D) 将产生相同的结果。

    【讨论】:

      【解决方案5】:

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


      当函数具有双指针时:functionX(int **y) 并通过执行 functionX(&amp;randomvar) 创建 2 个指针,一个包含 randomvar 的地址,另一个包含第一个指针的地址。

      当函数需要 int** 类型的对象时,您不能传递 int 类型的对象的地址,该对象的类型为 int*

      一般来说,传递双指针的概念与上面所说的相同。

      【讨论】:

        【解决方案6】:

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

        例如,假设您的程序声明了一个类似int randomvar; 的变量——在执行时,RAM 的一部分——通常为int 的4 个字节——被保留用于保存randomvar 的变量值。在程序在内存中执行之前,该地址是未知的,但为了示例,让我们假设地址0xCAFEBABEDEADBEEF(8 个字节)是指向 4 个字节以保存整数值的地址。在为变量赋值之前,地址处的值是不确定的——变量的声明只保留保存值的空间,它不会在地址处写入任何内容,因此在为变量赋值之前,你甚至根本不应该使用这个值(大多数 C 编译器都会警告你)。

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

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

        【讨论】:

        • 我喜欢你选择的地址。 :)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-06-07
        • 2012-12-29
        • 1970-01-01
        • 2013-09-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多