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*)转换以保证可移植性。