繁体   English   中英

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

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

据我目前所知:当我有一个将指针作为参数的函数时,它说一个名为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 寄存器)——您的randomvarfunctionX所在的范围内调用是分配用于存储整数的内存区域的标签,它被解释为整数。 &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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM