Skip to content

C 指针

指针是 C 语言最强大和最独特的特性之一。它们允许直接操作内存、高效处理数据(例如将大型结构体传递给函数)、进行动态内存分配,并且对于构建复杂数据结构至关重要。

从核心上讲,一个 指针 是一个存储另一个变量的 内存地址 的变量。

您声明的每个变量都驻留在计算机内存中的特定位置。这个位置有一个唯一的地址。您可以使用地址运算符(&)获取变量的地址。

为了可移植地打印内存地址,请使用 printf 的 %p 格式说明符,并将地址转换为 (void *)。

#include <stdio.h>
int main(void) {
int count = 10;
double price = 99.99;
char grade = 'A';
// Get and print the memory address of each variable
// 获取并打印每个变量的内存地址
printf("Address of count: %p\n", (void *)&count);
printf("Address of price: %p\n", (void *)&price);
printf("Address of grade: %p\n", (void *)&grade);
return 0;
}

编译并运行此代码时,您将看到表示内存位置的十六进制数字(具体数值在每次运行和不同系统上都会变化):

Address of count: 0x7ffc1a4b9c8c
Address of price: 0x7ffc1a4b9c90
Address of grade: 0x7ffc1a4b9c8b

指针变量被声明用于 存储 这样的地址。声明指定了指针将指向的数据类型。这一点至关重要,因为它告诉编译器在对指针进行解引用时需要读取/写入多少字节,以及如何执行指针算术。

指针声明语法:

type *pointer_variable_name;

这里,type 是指针将指向的变量的数据类型(例如,int、double、char、struct Point)。星号(*)表示 pointer_variable_name 是一个指针。

指针声明示例:

int *p_int; // Pointer to an integer
// 指向整数的指针
double *p_double; // Pointer to a double
// 指向双精度浮点数的指针
char *p_char; // Pointer to a character
// 指向字符的指针
struct Point *p_pt; // Pointer to a struct Point variable
// 指向结构体 Point 变量的指针

注意:任何指针变量中实际存储的 值(无论它指向什么类型)都是一个内存地址(通常表示为一个大的无符号整数)。type 部分对于编译器在使用指针时的解释至关重要。

涉及指针的基本操作包括:

  1. 声明: 声明一个适当类型的指针变量。
  2. 赋值: 将变量的地址(使用 & 获取)赋给指针。
  3. 解引用: 使用解引用(或间接寻址)运算符(*)访问存储在指针中的 地址处 的值。

示例:

#include <stdio.h>
int main(void) {
int score = 95;
int *p_score; // 1. Declare a pointer to an integer
// 1. 声明一个指向整数的指针
p_score = &score; // 2. Assign the address of 'score' to 'p_score'
// 2. 将 'score' 的地址赋给 'p_score'
printf("Address of score variable : %p\n", (void *)&score);
printf("Address stored in p_score : %p\n", (void *)p_score);
printf("Value of score variable : %d\n", score);
// 3. Dereference p_score to get the value stored at that address
// 3. 解引用 p_score,获取该地址处存储的值
printf("Value via p_score (*p_score): %d\n", *p_score);
// Modifying the value *through* the pointer
// 通过指针修改值
*p_score = 100; // Change the value at the address p_score holds
// 改变 p_score 所持有的地址处的值
printf("New value of score : %d\n", score); // Original 'score' is changed!
// 原始变量 'score' 的值改变了!
return 0;
}

输出(地址会变化):

Address of score variable : 0x7ffd6e0f8a5c
Address stored in p_score : 0x7ffd6e0f8a5c
Value of score variable : 95
Value via p_score (*p_score): 95
New value of score : 100

在使用指针之前对其进行初始化至关重要。如果您没有一个有效的地址可以立即赋值,请将指针初始化为 NULL。NULL 是一个特殊的宏(在 <stddef.h>、<stdio.h> 等头文件中定义),表示一个空指针常量(通常是 (void *)0)。

一个 NULL 指针表示该指针有意不指向任何有效的内存位置。

#include <stdio.h>
#include <stddef.h> // For NULL definition
// 用于 NULL 定义
int main(void) {
int *p_data = NULL; // Initialize pointer to NULL
// 将指针初始化为 NULL
printf("Value of p_data: %p\n", (void *)p_data);
// --- Attempting to dereference a NULL pointer causes Undefined Behavior (often a crash!) ---
// --- 尝试解引用 NULL 指针会导致未定义行为(通常是程序崩溃!) ---
// *p_data = 10; // DANGER! DO NOT DO THIS!
// 危险!不要这样做!
// Always check if a pointer is NULL before dereferencing
// 在解引用之前,务必检查指针是否为 NULL
if (p_data != NULL) {
printf("Value pointed to: %d\n", *p_data);
} else {
printf("p_data is a NULL pointer.\n");
}
// Alternative check
// 另一种检查方式
if (!p_data) { // Evaluates to true if p_data is NULL
// 如果 p_data 为 NULL,则此条件为真
printf("p_data is NULL (checked using !p_data).\n");
}
return 0;
}

输出:

Value of p_data: (nil) // Or 0x0, depending on system printf
// 或 0x0,取决于系统的 printf 实现
p_data is a NULL pointer.
p_data is NULL (checked using !p_data).

解引用 NULL 指针是一个常见的编程错误,通常会导致程序崩溃(段错误 Segmentation Fault)。在使用指针之前,尤其是从 malloc 等函数返回或在数据结构中使用的指针,务必进行检查。

指针实现了许多基本的 C 编程技巧:

ConceptDescription
概念描述
指针算术对指针执行算术运算(+、-、++、--)。递增一个指针会使其向前移动 sizeof(pointed-to-type) 字节。对于数组遍历至关重要。
指针与数组在表达式中,数组名通常会“衰退”为一个指向其第一个元素的指针。指针常用于高效地遍历数组。
指针数组创建每个元素都是指针的数组(例如,字符串数组,其中每个元素指向字符串的第一个字符)。
指向指针的指针(二级指针)一个存储另一个指针地址的指针(例如,int **p)。用于修改传递给函数的指针或用于指针数组。
指针与函数将指针作为参数传递给函数,允许函数修改原始数据(模拟传引用)。函数也可以返回指针(例如 malloc)。
动态内存分配使用 malloc、calloc、realloc(来自 <stdlib.h>)等函数在运行时分配内存。这些函数返回指向分配块的指针,程序员必须对其进行管理(使用 free 释放)。
函数指针存储函数地址的指针。允许将函数作为参数传递给其他函数(回调),或存储在数据结构中。

这些概念建立在对指针声明、赋值和解引用的基本理解之上,并将在相关章节中更详细地探讨。