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指向mainout)。
  • &a(值为0x1000)被赋值给*out,即修改了mainout的值
    内存变化
地址       变量       值
0x1000     a          10
0x2000     out        0x1000 (指向a)

4. func内部:a = 20;

  • 直接修改全局变量a的值为20
    内存变化
地址       变量       值
0x1000     a          20
0x2000     out        0x1000 (仍指向a)

5. 返回mainprintf("%u", *out);

  • out的值为0x1000(指向a)。
  • *out解引用得到a的值20,输出结果为20

三、关键误区解答

Q1:*out = &a 到底修改了什么?

  • *out是对二级指针out的解引用,等价于main中的out变量。
  • *out = &amain中的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。核心在于理解二级指针如何间接操作外部指针,以及参数传递是值拷贝但可通过地址修改原变量

这段代码展示了通过二级指针修改外部指针变量的过程。下面详细解析其执行流程和内存变化:

五、关键点总结

  1. 二级指针的作用

    • func通过二级指针uint32_t **out修改了main中一级指针out的值(使其指向a)。
    • 若直接传递一级指针(uint32_t *),只能修改指针指向的内容(如*out = 20),无法修改指针本身。
  2. 全局变量的可见性

    • func内部对a的修改直接影响全局变量,因此main中通过out访问到的是修改后的值20
  3. 执行顺序的重要性

    • 即使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。核心在于理解二级指针如何间接操作一级指针,以及全局变量的作用域