指针的概念
指针是 C 语言最核心的概念,也是最具挑战性的部分。指针本质上是一个存储内存地址的变量——它指向内存中的某个位置。通过指针,你可以直接操作内存、高效传递大型数据结构、实现动态数据结构(如链表和树)。理解指针,是掌握 C 语言的关键门槛。
什么是指针
内存可以看作一个巨大的字节数组,每个字节都有唯一的地址编号。指针就是存储这些地址的变量:
int x = 42; /* x 存储在内存地址 0x1000 */
int *p = &x; /* p 存储 0x1000,即 x 的地址 */
printf("x = %d\n", x); /* 42 */
printf("*p = %d\n", *p); /* 42:通过指针访问 x 的值 */
&x 获取变量 x 的地址(取地址),*p 访问指针 p 指向的内存(解引用)。
指针的大小
指针的大小取决于平台架构,与指向的类型无关:
printf("sizeof(char*) = %zu\n", sizeof(char*)); /* 4(32位)或 8(64位) */
printf("sizeof(int*) = %zu\n", sizeof(int*)); /* 同上 */
printf("sizeof(double*) = %zu\n", sizeof(double*)); /* 同上 */
32 位系统中指针占 4 字节,64 位系统中占 8 字节。
为什么使用指针
高效传递大型数据:
struct BigStruct { char data[1000]; };
/* 传值:复制 1000 字节 */
void process_value(struct BigStruct s);
/* 传指针:只复制 4/8 字节(地址) */
void process_pointer(const struct BigStruct *p);
修改函数外部的变量:
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
int x = 3, y = 5;
swap(&x, &y); /* x = 5, y = 3 */
动态内存分配:
int *arr = malloc(100 * sizeof(int)); /* 运行时分配内存 */
/* ... */
free(arr);
实现复杂数据结构:
struct Node {
int data;
struct Node *next; /* 链表节点 */
};
指针的类型
指针有类型,类型决定了指针运算的步长和解释内存的方式:
int x = 0x12345678;
int *ip = &x;
char *cp = (char *)&x;
printf("%X\n", *ip); /* 12345678 */
printf("%02X\n", *cp); /* 78(小端)或 12(大端) */
int* 每次解引用读取 4 字节(通常),char* 每次读取 1 字节。
空指针
NULL 表示指针不指向任何有效地址:
int *p = NULL; /* 空指针 */
if (p == NULL)
printf("Pointer is null\n");
/* 解引用空指针是未定义行为 */
/* *p = 10; */ /* 崩溃! */
NULL 在 C99 中定义为 (void*)0 或 0,具体取决于实现。
常见错误
未初始化的指针:
int *p; /* 未初始化,指向随机地址 */
*p = 10; /* 未定义行为!可能覆盖任意内存 */
/* 正确 */
int *p = NULL; /* 初始化为空 */
解引用空指针:
int *p = NULL;
printf("%d\n", *p); /* 崩溃:段错误 */
混淆指针和值:
int x = 10;
int *p = x; /* 警告:将 int 赋给指针 */
/* p 指向地址 10,不是 x 的地址 */
/* 正确 */
int *p = &x; /* p 指向 x */
最佳实践
- 指针声明时初始化(
NULL或有效地址) - 解引用前检查指针是否为
NULL - 理解指针类型决定解引用时读取多少字节
- 用
const修饰不修改的指针目标 - 释放后将指针置
NULL,避免悬垂指针