1. 指针基础与内存地址可视化

任务目标

编写一个C程序,定义不同类型的普通变量,通过&取地址运算符打印其内存地址;定义指针变量存储该地址,打印指针自身的值(即所指向变量的地址)以及指针自身的地址。通过对比输出,直观理解“指针保存的是变量的地址”这一核心概念,并观察栈上变量在内存中的布局特征。

核心实现

#include <stdio.h>

int main(void) {
    int    a = 42;
    char   b = 'A';
    double c = 3.14;

    // 定义指针并指向对应变量
    int    *ptr_a = &a;
    char   *ptr_b = &b;
    double *ptr_c = &c;

    // 打印变量值、变量地址、指针值、指针自身地址
    printf("=== 变量与指针地址对照 ===\n");
    printf("变量 a: 值=%d, 地址=%p\n", a, (void*)&a);
    printf("指针 ptr_a: 指向值=%d, 指针值(存地址)=%p, 指针自身地址=%p\n",
           *ptr_a, (void*)ptr_a, (void*)&ptr_a);

    printf("变量 b: 值=%c, 地址=%p\n", b, (void*)&b);
    printf("指针 ptr_b: 指向值=%c, 指针值(存地址)=%p, 指针自身地址=%p\n",
           *ptr_b, (void*)ptr_b, (void*)&ptr_b);

    printf("变量 c: 值=%.2f, 地址=%p\n", c, (void*)&c);
    printf("指针 ptr_c: 指向值=%.2f, 指针值(存地址)=%p, 指针自身地址=%p\n",
           *ptr_c, (void*)ptr_c, (void*)&ptr_c);

    // 验证解引用与直接访问结果一致
    printf("\n=== 验证 ===\n");
    printf("a == *ptr_a ? %s\n", (a == *ptr_a) ? "是" : "否");
    printf("b == *ptr_b ? %s\n", (b == *ptr_b) ? "是" : "否");
    printf("c == *ptr_c ? %s\n", (c == *ptr_c) ? "是" : "否");

    return 0;
}

编译与运行

# 使用gcc编译(需支持C99或更高版本)
gcc -o pointer_demo pointer_demo.c

# 运行
./pointer_demo

验证方法与判定标准

  • 地址格式:程序输出的地址与指针值两列应为完全相同的十六进制值(如0x7ffc12345678),证明指针确实存储了变量的地址。
  • 指针自身地址:指针自身地址与变量地址不同,说明指针变量本身也占用独立的内存空间。
  • 解引用验证:程序末尾输出三个是,表明通过*ptr访问的值与直接使用变量名访问的值一致,验证指针解引用的正确性。
  • 内存布局观察:观察三个变量地址的间隔。在典型x86-64栈上,a(4字节)、b(1字节)、c(8字节)的地址间隔可能大于其类型大小(因编译器对齐),且地址通常从高到低排列(栈向下生长)。此现象可直观感受栈内存的分配方式。
说明:地址的具体数值每次运行可能不同(受ASLR影响),但上述逻辑关系(指针值等于变量地址、指针自身地址不同、解引用结果一致)在每次运行中必然成立。

关键点

  • &是取地址运算符,作用于变量时返回该变量在内存中的首字节地址。
  • 指针变量存储的是地址值,其大小在64位系统上通常为8字节,与所指向类型无关。
  • %p是打印指针地址的专用格式符,配合(void*)转换以保证可移植性。