提问人:Yeatsflee 提问时间:2/12/2023 最后编辑:Vlad from MoscowYeatsflee 更新时间:2/13/2023 访问量:108
如何理解辅助指针?
How to understand secondary pointer?
问:
我想问一个关于指针的问题:
void fun(const char** a) {...}
1.
const char* b = nullptr;
fun(&b);
2.
const char** b = nullptr;
fun(b);
为什么使用 1 而不是 2?
1 好,2 不行
答:
此代码片段
void fun(const char** a) {...}
1.
const char* b = nullptr;
fun(&b);
表示在 C 含义中通过引用传递指向函数的指针。因此,取消引用指向函数中指针的指针,可以通过指向函数的指针间接更改传递给函数的原始指针。b
fun
a
b
这是一个演示程序。
#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含义中通过引用)。因此,取消对指针的指针的引用b
a
*a = "Hello World!";
您可以更改 Main 中定义的原始指针。b
在第二种情况下
2.
const char** b = nullptr;
fun(b);
指针按值传递,如果函数将取消引用空指针,则将调用未定义的行为。b
fun
const char* b = nullptr;
fun(&b);
这会通过引用传递指针,或者模拟通过引用语义传递,即传递变量存储位置的内存地址,而不是其值。这允许您访问最初在调用函数中声明的指针,并且对被调用函数中该指针所做的任何更改都将在调用函数中可见。b
const char** b = nullptr;
fun(b);
相反,这是按值传递的。如果将指针更改为指向被调用函数中的其他内存位置,则该更改将不会反映回调用函数中。根据 C11 的以下条款,任何在它指向时取消引用它的尝试都会导致未定义的行为:b
NULL
如果已为指针分配了无效值,则 一元 * 运算符未定义。
[...]
在一元取消引用指针的无效值中 * 运算符是空指针,[...]
[6.5.3.2 地址和间接运算符,C11]
为什么使用 1 而不是 2?
为什么要使用它们中的任何一个?
1 好,2 不行
“没有什么好坏之分,但思考会让它如此。”— 它们都有不同的目的。不过,第二个片段的目的在这里相当模棱两可。
C 对函数调用参数使用传递 by 值。这意味着函数接收传递值的副本,并且函数无法直接更改调用方中的变量。
例如,如果您将
void set_to_5(int x)
{
x = 5;
}
然后写
int i = 3;
set_to_5(i);
printf("%d\n", i);
它打印,而不是 .或者,如果你写3
5
void set_string(char *str)
{
str = "world";
}
然后写
char *p = "hello";
set_string(p);
printf("%s\n", p);
它打印,而不是 .hello
world
在这两种情况下,函数都会成功更改其传递值的副本 — 或 — 但这对调用方中的变量 ( 或 ) 没有任何影响。x
str
i
p
当您希望某个函数在其调用方中更改变量时,一种方法是让该函数接受指针。它看起来像这样:
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);
现在它确实打印了 和 ,根据需要。5
world
请注意,在每种情况下,我都进行了三项更改:
- 我在函数预期的参数类型中添加了一个 ( to , to
*
int x
int *x
char *str
char **str
) - 我在函数中每次使用参数之前都添加了一个,也就是说,我实际设置了新值(和
*
5
"world"
) - 我在变量之前添加了一个,将它们传递给函数 ( 和 )。
&
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 的参数,所以它是匹配的。但是,如果您声明并调用x
char **
set_string_via_pointer
char **
char *y;
set_string_via_pointer(&y);
这也是传递 type 参数的一个非常好的方法,在这种情况下,这肯定是他们所期望的。char **
评论
fun
b
&b
fun
b
int