Appearance
指针
指针是 C 语言最强大也最复杂的特性,是理解 C 语言的核心。
指针基础
什么是指针
指针是一个变量,其值为另一个变量的地址。
c
#include <stdio.h>
int main() {
int a = 10;
int *p; // 声明指针变量
p = &a; // 将 a 的地址赋给 p
printf("a 的值: %d\n", a);
printf("a 的地址: %p\n", (void*)&a);
printf("p 的值: %p\n", (void*)p);
printf("*p 的值: %d\n", *p); // 解引用
return 0;
}指针声明与初始化
c
#include <stdio.h>
int main() {
// 声明指针
int *p1; // 推荐:* 靠近变量名
int* p2; // 也可以
int * p3; // 不推荐
// 初始化
int a = 10;
int *p = &a; // 初始化为 a 的地址
int *null_p = NULL; // 空指针
// 未初始化的指针是危险的
int *wild_p; // 野指针,包含随机地址
// 指针的大小
printf("指针大小: %zu bytes\n", sizeof(p));
// 32位系统: 4 bytes, 64位系统: 8 bytes
return 0;
}指针运算符
c
#include <stdio.h>
int main() {
int a = 10;
int *p = &a;
// & 取地址运算符
printf("a 的地址: %p\n", (void*)&a);
printf("p 的值: %p\n", (void*)p);
// * 解引用运算符
printf("*p = %d\n", *p);
// 通过指针修改值
*p = 20;
printf("修改后 a = %d\n", a);
// 指针的指针
int **pp = &p;
printf("**pp = %d\n", **pp);
return 0;
}指针与数组
数组名与指针
c
#include <stdio.h>
int main() {
int arr[] = {10, 20, 30, 40, 50};
int *p = arr; // 数组名就是首元素地址
printf("arr = %p\n", (void*)arr);
printf("&arr[0] = %p\n", (void*)&arr[0]);
printf("p = %p\n", (void*)p);
// 访问数组元素
for (int i = 0; i < 5; i++) {
printf("arr[%d] = %d, *(p+%d) = %d\n",
i, arr[i], i, *(p + i));
}
// 指针下标访问
for (int i = 0; i < 5; i++) {
printf("p[%d] = %d\n", i, p[i]);
}
return 0;
}指针算术运算
c
#include <stdio.h>
int main() {
int arr[] = {10, 20, 30, 40, 50};
int *p = arr;
printf("*p = %d\n", *p); // 10
p++; // 移动到下一个元素
printf("*p = %d\n", *p); // 20
p += 2; // 移动2个元素
printf("*p = %d\n", *p); // 40
p--; // 回退一个元素
printf("*p = %d\n", *p); // 30
// 指针相减
int *p1 = arr;
int *p2 = arr + 3;
printf("p2 - p1 = %ld\n", p2 - p1); // 3
// 指针比较
printf("p1 < p2: %d\n", p1 < p2); // 1
return 0;
}指针与多维数组
c
#include <stdio.h>
int main() {
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// 数组名是首行地址
printf("matrix = %p\n", (void*)matrix);
printf("matrix[0] = %p\n", (void*)matrix[0]);
printf("&matrix[0][0] = %p\n", (void*)&matrix[0][0]);
// 访问元素
printf("matrix[1][2] = %d\n", matrix[1][2]);
printf("*(*(matrix + 1) + 2) = %d\n", *(*(matrix + 1) + 2));
// 使用指针遍历
int *p = &matrix[0][0];
for (int i = 0; i < 12; i++) {
printf("%d ", *(p + i));
}
printf("\n");
// 行指针
int (*row)[4] = matrix; // 指向包含4个int的数组
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", row[i][j]);
}
printf("\n");
}
return 0;
}指针与函数
指针作为参数
c
#include <stdio.h>
// 交换两个变量的值
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
// 通过指针返回多个值
void minmax(int arr[], int size, int *min, int *max) {
*min = arr[0];
*max = arr[0];
for (int i = 1; i < size; i++) {
if (arr[i] < *min) *min = arr[i];
if (arr[i] > *max) *max = arr[i];
}
}
int main() {
int x = 10, y = 20;
printf("交换前: x = %d, y = %d\n", x, y);
swap(&x, &y);
printf("交换后: x = %d, y = %d\n", x, y);
int arr[] = {5, 2, 8, 1, 9, 3};
int min, max;
minmax(arr, 6, &min, &max);
printf("最小值: %d, 最大值: %d\n", min, max);
return 0;
}指针作为返回值
c
#include <stdio.h>
// 返回数组中的最大元素地址
int* find_max(int arr[], int size) {
int *max = arr;
for (int i = 1; i < size; i++) {
if (arr[i] > *max) {
max = &arr[i];
}
}
return max;
}
// 返回静态数组的指针
int* get_static_array() {
static int arr[5] = {1, 2, 3, 4, 5};
return arr;
}
int main() {
int arr[] = {5, 2, 8, 1, 9, 3};
int *max = find_max(arr, 6);
printf("最大值: %d\n", *max);
printf("最大值位置: %ld\n", max - arr);
int *static_arr = get_static_array();
printf("静态数组: ");
for (int i = 0; i < 5; i++) {
printf("%d ", static_arr[i]);
}
printf("\n");
return 0;
}函数指针
c
#include <stdio.h>
#include <stdlib.h>
// 普通函数
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }
// 使用函数指针作为参数
int calculate(int a, int b, int (*op)(int, int)) {
return op(a, b);
}
// 回调函数示例
void process_array(int arr[], int size, void (*process)(int*)) {
for (int i = 0; i < size; i++) {
process(&arr[i]);
}
}
void double_value(int *n) { *n *= 2; }
void print_value(int *n) { printf("%d ", *n); }
int main() {
// 声明函数指针
int (*fp)(int, int);
// 赋值
fp = add;
printf("add(5, 3) = %d\n", fp(5, 3));
fp = subtract;
printf("subtract(5, 3) = %d\n", fp(5, 3));
// 函数指针数组
int (*operations[])(int, int) = {add, subtract, multiply};
char *names[] = {"加法", "减法", "乘法"};
for (int i = 0; i < 3; i++) {
printf("%s: %d\n", names[i], operations[i](10, 5));
}
// 使用函数指针作为参数
printf("calculate(10, 5, add) = %d\n", calculate(10, 5, add));
// 回调函数
int arr[] = {1, 2, 3, 4, 5};
printf("原数组: ");
process_array(arr, 5, print_value);
printf("\n");
process_array(arr, 5, double_value);
printf("翻倍后: ");
process_array(arr, 5, print_value);
printf("\n");
// qsort 使用函数指针
int nums[] = {5, 2, 8, 1, 9, 3};
int compare(const void *a, const void *b) {
return (*(int*)a - *(int*)b);
}
qsort(nums, 6, sizeof(int), compare);
printf("排序后: ");
for (int i = 0; i < 6; i++) {
printf("%d ", nums[i]);
}
printf("\n");
return 0;
}指针与字符串
字符串指针
c
#include <stdio.h>
#include <string.h>
int main() {
// 字符数组
char str1[] = "Hello";
str1[0] = 'h'; // 可以修改
printf("str1: %s\n", str1);
// 字符串常量指针
char *str2 = "World";
// str2[0] = 'w'; // 错误:修改字符串常量是未定义行为
printf("str2: %s\n", str2);
// 遍历字符串
char *p = str1;
while (*p != '\0') {
printf("%c ", *p);
p++;
}
printf("\n");
// 字符串函数使用指针
char src[] = "Hello";
char dest[20];
strcpy(dest, src);
printf("复制后: %s\n", dest);
// 自定义字符串长度函数
size_t my_strlen(const char *s) {
const char *p = s;
while (*p) p++;
return p - s;
}
printf("字符串长度: %zu\n", my_strlen("Hello"));
return 0;
}字符串数组
c
#include <stdio.h>
int main() {
// 二维字符数组
char fruits[][10] = {
"Apple",
"Banana",
"Cherry"
};
for (int i = 0; i < 3; i++) {
printf("%s\n", fruits[i]);
}
// 指针数组(更灵活)
char *colors[] = {
"Red",
"Green",
"Blue"
};
for (int i = 0; i < 3; i++) {
printf("%s\n", colors[i]);
}
// 命令行参数
// int main(int argc, char *argv[])
// argv 就是指针数组
return 0;
}指针高级用法
void 指针
c
#include <stdio.h>
#include <string.h>
// 通用交换函数
void generic_swap(void *a, void *b, size_t size) {
char temp[size];
memcpy(temp, a, size);
memcpy(a, b, size);
memcpy(b, temp, size);
}
// 通用打印函数
void print_value(void *ptr, char type) {
switch (type) {
case 'i':
printf("int: %d\n", *(int*)ptr);
break;
case 'f':
printf("float: %f\n", *(float*)ptr);
break;
case 'd':
printf("double: %lf\n", *(double*)ptr);
break;
case 'c':
printf("char: %c\n", *(char*)ptr);
break;
}
}
int main() {
// void 指针可以指向任何类型
int a = 10;
float b = 3.14f;
char c = 'A';
void *vp;
vp = &a;
printf("int: %d\n", *(int*)vp);
vp = &b;
printf("float: %f\n", *(float*)vp);
vp = &c;
printf("char: %c\n", *(char*)vp);
// 通用交换
int x = 10, y = 20;
printf("交换前: x=%d, y=%d\n", x, y);
generic_swap(&x, &y, sizeof(int));
printf("交换后: x=%d, y=%d\n", x, y);
float f1 = 1.5f, f2 = 2.5f;
printf("交换前: f1=%.2f, f2=%.2f\n", f1, f2);
generic_swap(&f1, &f2, sizeof(float));
printf("交换后: f1=%.2f, f2=%.2f\n", f1, f2);
return 0;
}const 指针
c
#include <stdio.h>
int main() {
int a = 10;
int b = 20;
// 指向常量的指针(不能通过指针修改值)
const int *p1 = &a;
// *p1 = 100; // 错误
p1 = &b; // 正确:可以改变指向
// 常量指针(指针本身不能改变)
int * const p2 = &a;
*p2 = 100; // 正确:可以修改值
// p2 = &b; // 错误:不能改变指向
// 指向常量的常量指针
const int * const p3 = &a;
// *p3 = 200; // 错误
// p3 = &b; // 错误
printf("a = %d\n", a);
return 0;
}指针与结构体
c
#include <stdio.h>
#include <string.h>
struct Person {
char name[50];
int age;
};
void print_person(struct Person *p) {
printf("姓名: %s, 年龄: %d\n", p->name, p->age);
}
void update_age(struct Person *p, int new_age) {
p->age = new_age;
}
int main() {
struct Person person = {"张三", 25};
struct Person *p = &person;
// 访问成员
printf("姓名: %s\n", (*p).name);
printf("年龄: %d\n", p->age); // 使用 -> 操作符
// 修改成员
strcpy(p->name, "李四");
p->age = 30;
print_person(p);
update_age(p, 35);
print_person(&person);
// 结构体指针数组
struct Person people[] = {
{"张三", 25},
{"李四", 30},
{"王五", 28}
};
struct Person *pp = people;
for (int i = 0; i < 3; i++) {
printf("%s: %d岁\n", pp[i].name, pp[i].age);
}
return 0;
}指针常见问题
空指针
c
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p = NULL;
// 使用前检查
if (p != NULL) {
printf("%d\n", *p);
} else {
printf("指针为空\n");
}
// malloc 可能返回 NULL
int *arr = (int*)malloc(1000000000000);
if (arr == NULL) {
printf("内存分配失败\n");
}
// 使用后置空
free(arr);
arr = NULL;
return 0;
}野指针
c
#include <stdio.h>
#include <stdlib.h>
int main() {
// 未初始化的指针(野指针)
int *p1; // 危险!
// 正确做法
int *p2 = NULL;
// 悬空指针
int *p3 = (int*)malloc(sizeof(int));
*p3 = 10;
free(p3);
// p3 现在是悬空指针
// *p3 = 20; // 危险!
// 正确做法
p3 = NULL;
// 返回局部变量地址
int* bad_function() {
int local = 10;
return &local; // 危险!返回局部变量地址
}
// 正确做法
int* good_function() {
static int local = 10;
return &local; // 正确:静态变量
}
return 0;
}指针越界
c
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr;
// 正确访问
for (int i = 0; i < 5; i++) {
printf("%d ", p[i]);
}
printf("\n");
// 越界访问(危险!)
// printf("%d\n", p[5]); // 越界
// printf("%d\n", p[-1]); // 越界
// 指针越界后的运算
int *end = arr + 5; // 指向数组末尾的下一个位置
// 可以比较,但不能解引用
while (p < end) {
printf("%d ", *p++);
}
printf("\n");
return 0;
}实践示例
动态数组
c
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int *data;
int size;
int capacity;
} DynamicArray;
DynamicArray* create_array(int capacity) {
DynamicArray *arr = (DynamicArray*)malloc(sizeof(DynamicArray));
arr->data = (int*)malloc(capacity * sizeof(int));
arr->size = 0;
arr->capacity = capacity;
return arr;
}
void push_back(DynamicArray *arr, int value) {
if (arr->size == arr->capacity) {
arr->capacity *= 2;
arr->data = (int*)realloc(arr->data, arr->capacity * sizeof(int));
}
arr->data[arr->size++] = value;
}
int get(DynamicArray *arr, int index) {
if (index < 0 || index >= arr->size) {
printf("索引越界\n");
return -1;
}
return arr->data[index];
}
void free_array(DynamicArray *arr) {
free(arr->data);
free(arr);
}
int main() {
DynamicArray *arr = create_array(5);
for (int i = 0; i < 10; i++) {
push_back(arr, i * 10);
}
for (int i = 0; i < arr->size; i++) {
printf("%d ", get(arr, i));
}
printf("\n");
free_array(arr);
return 0;
}链表实现
c
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int data;
struct Node *next;
} Node;
Node* create_node(int data) {
Node *node = (Node*)malloc(sizeof(Node));
node->data = data;
node->next = NULL;
return node;
}
void append(Node **head, int data) {
Node *new_node = create_node(data);
if (*head == NULL) {
*head = new_node;
return;
}
Node *current = *head;
while (current->next != NULL) {
current = current->next;
}
current->next = new_node;
}
void print_list(Node *head) {
Node *current = head;
while (current != NULL) {
printf("%d -> ", current->data);
current = current->next;
}
printf("NULL\n");
}
void free_list(Node *head) {
Node *current = head;
while (current != NULL) {
Node *temp = current;
current = current->next;
free(temp);
}
}
int main() {
Node *head = NULL;
append(&head, 1);
append(&head, 2);
append(&head, 3);
append(&head, 4);
append(&head, 5);
print_list(head);
free_list(head);
return 0;
}