1
What's the output of following code (B)
#include <stdio.h>
#include <stdint.h>
uint32_t a = 10;
void func(uint32_t **out) {
*out = &a;
a = 20;
}
int main(void) {
uint32_t *out = 0;
func(&out);
printf("%u\r\n", *out);
return 0;
}
10
20
无法运行
0
解答:
out 为 uint32_t 类型的指针,func 函数将指针 out 的地址作为形参传入,在函数体内修改指针指向的值为全局变量 a,同时又修改全局变量 a 的值为 20,因此最终输出的是 a 的值 20
相当于先创建一个指针o,在func中:对指针的地址作为形参(指向指针的指针),对这个参数(指针)指向a的地址。然后改动a值。最后引用o,相当于jiet's the output of following code ==(B)==
#include <stdio.h>
#include <stdint.h>
uint32_t a = 10;
void func(uint32_t **out) {
*out = &a;
a = 20;
}
int main(void) {
uint32_t *out = 0;
func(&out);
printf("%u\r\n", *out);
return 0;
}
10
20
无法运行
0
解答:
out 为 uint32_t 类型的指针,func 函数将指针 out 的地址作为形参传入,在函数体内修改指针指向的值为全局变量 a,同时又修改全局变量 a 的值为 20,因此最终输出的是 a 的值 20
相当于先创建一个指针o,在func中:对指针的地址作为形参(指向指针的指针),对这个参数(指针)指向a的地址。然后改动a值。最后引用o,相当于jie
你的疑问涉及指针和二级指针的核心概念。让我通过更直观的方式解释整个过程,特别是参数传递和解引用的细节:
一、关键概念澄清
1. 指针 vs 二级指针
- 指针(
uint32_t*
):存储变量的地址(如&a
)。 - 二级指针(
uint32_t**
):存储指针的地址(如&out
)。
2. 参数传递本质
C语言中,函数参数是值传递(拷贝):
- 传递
&out
(指针的地址)时,形参out
是&out
的拷贝(但二者指向同一内存地址)。 - 修改
*out
(即修改原指针out
的值),但无法修改&out
本身(因为是拷贝)。
二、分步图解执行过程
1. 初始状态(main函数)
uint32_t a = 10; // a的值为10,地址假设为0x1000
uint32_t *out = 0; // out的值为NULL(0),自身地址假设为0x2000
内存布局:
地址 变量 值
0x1000 a 10
0x2000 out NULL (0)
2. 调用func(&out)
&out
(值为0x2000
)被拷贝到func
的形参out
中。- 形参
out
的值为0x2000
(即指向main
中的out
变量)。
3. func
内部:*out = &a;
*out
等价于main
中的out
(因为out
指向main
的out
)。&a
(值为0x1000
)被赋值给*out
,即修改了main
中out
的值。
内存变化:
地址 变量 值
0x1000 a 10
0x2000 out 0x1000 (指向a)
4. func
内部:a = 20;
- 直接修改全局变量
a
的值为20
。
内存变化:
地址 变量 值
0x1000 a 20
0x2000 out 0x1000 (仍指向a)
5. 返回main
:printf("%u", *out);
out
的值为0x1000
(指向a
)。*out
解引用得到a
的值20
,输出结果为20
。
三、关键误区解答
Q1:*out = &a
到底修改了什么?
*out
是对二级指针out
的解引用,等价于main
中的out
变量。*out = &a
将main
中的out
指向a
(即out
的值变为a
的地址0x1000
)。
Q2:为什么需要二级指针?
- 若仅传递一级指针(
uint32_t*
),函数内部只能修改指针指向的内容(如*out = 20
),无法修改指针本身(因为参数是拷贝)。 - 二级指针允许函数通过
*out = &a
间接修改外部指针的值。
Q3:最终*out
得到了什么?
out
指向a
(地址为0x1000
)。*out
通过地址0x1000
访问到a
的值20
。
总结
通过二级指针uint32_t**
,函数func
修改了main
中指针out
的值,使其指向全局变量a
。最终*out
解引用得到的是a
被修改后的值20
。核心在于理解二级指针如何间接操作外部指针,以及参数传递是值拷贝但可通过地址修改原变量。
这段代码展示了通过二级指针修改外部指针变量的过程。下面详细解析其执行流程和内存变化:
五、关键点总结
-
二级指针的作用:
func
通过二级指针uint32_t **out
修改了main
中一级指针out
的值(使其指向a
)。- 若直接传递一级指针(
uint32_t *
),只能修改指针指向的内容(如*out = 20
),无法修改指针本身。
-
全局变量的可见性:
func
内部对a
的修改直接影响全局变量,因此main
中通过out
访问到的是修改后的值20
。
-
执行顺序的重要性:
- 即使
a = 20;
在*out = &a;
之后执行,由于out
已指向a
,修改a
仍会影响后续*out
的结果。
- 即使
六、常见误区对比
错误示例1:传递一级指针(无法修改外部指针)
void func(uint32_t *out) {
out = &a; // 仅修改局部拷贝,main中的out未改变
}
- 结果:
main
中的out
仍为NULL
,*out
会导致空指针解引用错误。
错误示例2:未初始化指针(野指针)
void func(uint32_t **out) {
uint32_t b = 30;
*out = &b; // 返回局部变量地址(函数返回后失效)
}
- 结果:
main
中的out
指向已销毁的局部变量b
,形成野指针。
总结
这段代码通过二级指针实现了函数内部对外部指针的修改,使out
成功指向全局变量a
,最终输出a
被修改后的值20
。核心在于理解二级指针如何间接操作一级指针,以及全局变量的作用域。