如何理解辅助指针?

How to understand secondary pointer?

提问人:Yeatsflee 提问时间:2/12/2023 最后编辑:Vlad from MoscowYeatsflee 更新时间:2/13/2023 访问量:108

问:

我想问一个关于指针的问题:

void fun(const char** a) {...}

1. 
const char* b = nullptr;
fun(&b);

2. 
const char** b = nullptr;
fun(b);

为什么使用 1 而不是 2?

1 好,2 不行

C 引用传递 按值传递 到指针

评论

0赞 Some programmer dude 2/12/2023
这要么是你正在使用的非常现代的C编译器,要么是你正在用C++编程。
0赞 Some programmer dude 2/12/2023
至于你的问题,你说的“好”和“不行”是什么意思?请花一些时间阅读帮助页面,参加 SO 导览,阅读如何提问,以及这个问题清单
0赞 Steve Summit 2/12/2023
这取决于你想做什么。但是,如果目标是让函数在其调用方中对变量进行更改,那么您总是希望传递给 。无论 的类型如何,也就是说,无论它是 、 指针还是其他类型,都是如此。funb&bfunbint

答:

0赞 Vlad from Moscow 2/12/2023 #1

此代码片段

void fun(const char** a) {...}

1. 
const char* b = nullptr;
fun(&b);

表示在 C 含义中通过引用传递指向函数的指针。因此,取消引用指向函数中指针的指针,可以通过指向函数的指针间接更改传递给函数的原始指针。bfunab

这是一个演示程序。

#include <stdio.h>

void fun( const char **a )
{
    *a = "Hello World!";
}

int main( void )
{
    const char *b = NULL;

    fun( &b );

    puts( b );
}

程序输出为

Hello World!

正如你所看到的,指针是通过指向函数的指针间接传递给函数的(在C含义中通过引用)。因此,取消对指针的指针的引用ba

*a = "Hello World!";

您可以更改 Main 中定义的原始指针。b

在第二种情况下

2. 
const char** b = nullptr;
fun(b);

指针按值传递,如果函数将取消引用空指针,则将调用未定义的行为。bfun

1赞 Harith 2/12/2023 #2
const char* b = nullptr;
fun(&b);

这会通过引用传递指针,或者模拟通过引用语义传递,即传递变量存储位置的内存地址,而不是其值。这允许您访问最初在调用函数中声明的指针,并且对被调用函数中该指针所做的任何更改都将在调用函数中可见。b


const char** b = nullptr;
fun(b);

相反,这是按值传递的。如果将指针更改为指向被调用函数中的其他内存位置,则该更改将不会反映回调用函数中。根据 C11 的以下条款,任何在它指向时取消引用它的尝试都会导致未定义的行为:bNULL

如果已为指针分配了无效值,则 一元 * 运算符未定义。

[...]

在一元取消引用指针的无效值中 * 运算符是空指针,[...]

[6.5.3.2 地址和间接运算符,C11]


为什么使用 1 而不是 2?

为什么要使用它们中的任何一个?


1 好,2 不行

“没有什么好坏之分,但思考会让它如此。”— 它们都有不同的目的。不过,第二个片段的目的在这里相当模棱两可。

1赞 Steve Summit 2/12/2023 #3

C 对函数调用参数使用传递 by 值。这意味着函数接收传递值的副本,并且函数无法直接更改调用方中的变量。

例如,如果您将

void set_to_5(int x)
{
    x = 5;
}

然后写

int i = 3;
set_to_5(i);
printf("%d\n", i);

它打印,而不是 .或者,如果你写35

void set_string(char *str)
{
    str = "world";
}

然后写

char *p = "hello";
set_string(p);
printf("%s\n", p);

它打印,而不是 .helloworld

在这两种情况下,函数都会成功更改其传递值的副本 — 或 — 但这对调用方中的变量 ( 或 ) 没有任何影响。xstrip

当您希望某个函数在其调用方中更改变量时,一种方法是让该函数接受指针。它看起来像这样:

void set_to_5_via_pointer(int *x)
{
    *x = 5;
}

void set_string_via_pointer(char **str)
{
    *str = "world";
}

然后你可以写:

int i = 3;
set_to_5_via_pointer(&i);
printf("%d\n", i);

char *p = "hello";
set_string_via_pointer(&p);
printf("%s\n", p);

现在它确实打印了 和 ,根据需要。5world

请注意,在每种情况下,我都进行了三项更改:

  1. 我在函数预期的参数类型中添加了一个 ( to , to*int xint *xchar *strchar **str)
  2. 我在函数中每次使用参数之前都添加了一个,也就是说,我实际设置了新值(和*5"world")
  3. 我在变量之前添加了一个,将它们传递给函数 ( 和 )。&set_to_5_via_pointer(&i)set_string_via_pointer(&p)

还有一件事。当您看到类似

void set_string_via_pointer(char **str);

这并不一定意味着调用函数的正确方法是

char **x;
set_string_via_pointer(x);

是的,这里是 类型 ,函数需要一个 type 的参数,所以它是匹配的。但是,如果您声明并调用xchar **set_string_via_pointerchar **

char *y;
set_string_via_pointer(&y);

这也是传递 type 参数的一个非常好的方法,在这种情况下,这肯定是他们所期望的。char **