C语言指针详解
1. 什么是指针?
指针是一个变量,其值是另一个变量的内存地址。简单来说,指针就是存储地址的变量。
指针的核心概念:
- 每个变量都有内存地址
- 指针存储这些地址
- 通过指针可以间接访问和修改变量的值
2. 指针的基本概念
C语言指针 基本指针 指针运算 指针与数组 多级指针 函数指针 声明和初始化 解引用 算术运算 关系运算 数组名即指针 指针访问数组 指向指针的指针 回调函数
3. 基本指针操作
指针的声明和使用
c
#include <stdio.h>
int main() {
int number = 42;
int *ptr; // 声明一个整型指针
printf("=== 基本指针操作 ===\n");
// 获取变量的地址
ptr = &number; // & 是取地址运算符
printf("变量number的值: %d\n", number);
printf("变量number的地址: %p\n", &number);
printf("指针ptr的值: %p\n", ptr);
printf("指针ptr指向的值: %d\n", *ptr); // * 是解引用运算符
// 通过指针修改变量的值
*ptr = 100;
printf("通过指针修改后number的值: %d\n", number);
// 指针的大小
printf("指针ptr的大小: %zu 字节\n", sizeof(ptr));
printf("int的大小: %zu 字节\n", sizeof(int));
printf("double指针的大小: %zu 字节\n", sizeof(double*));
return 0;
}
不同类型的指针
c
#include <stdio.h>
int main() {
printf("=== 不同类型的指针 ===\n");
int integer = 10;
float floating = 3.14;
char character = 'A';
double double_value = 2.71828;
// 声明不同类型的指针
int *int_ptr = &integer;
float *float_ptr = &floating;
char *char_ptr = &character;
double *double_ptr = &double_value;
printf("整型指针:\n");
printf(" 值: %d, 地址: %p, 指针值: %p\n", integer, &integer, int_ptr);
printf("浮点型指针:\n");
printf(" 值: %.2f, 地址: %p, 指针值: %p\n", floating, &floating, float_ptr);
printf("字符型指针:\n");
printf(" 值: %c, 地址: %p, 指针值: %p\n", character, &character, char_ptr);
printf("双精度指针:\n");
printf(" 值: %.5f, 地址: %p, 指针值: %p\n", double_value, &double_value, double_ptr);
// void指针(通用指针)
void *void_ptr = &integer;
printf("void指针: %p\n", void_ptr);
// printf("*void_ptr = %d\n", *void_ptr); // ❌ 错误:void指针不能直接解引用
return 0;
}
指针的初始化和NULL指针
c
#include <stdio.h>
int main() {
printf("=== 指针初始化和NULL指针 ===\n");
int x = 50;
// 正确的指针初始化方式
int *ptr1 = &x; // 初始化时赋值
int *ptr2 = NULL; // 初始化为NULL
int *ptr3; // 未初始化(危险!)
printf("ptr1: %p, 指向的值: %d\n", ptr1, *ptr1);
printf("ptr2: %p\n", ptr2);
printf("ptr3: %p (未初始化)\n", ptr3);
// NULL指针检查
if (ptr2 == NULL) {
printf("ptr2是NULL指针,不能解引用\n");
}
// 危险的未初始化指针
// printf("*ptr3 = %d\n", *ptr3); // ❌ 未定义行为!
// 安全的指针使用
ptr3 = &x; // 现在ptr3是安全的
printf("ptr3现在指向: %p, 值: %d\n", ptr3, *ptr3);
return 0;
}
4. 指针运算
指针算术运算
c
#include <stdio.h>
int main() {
printf("=== 指针算术运算 ===\n");
int numbers[] = {10, 20, 30, 40, 50};
int *ptr = numbers; // 指向数组的第一个元素
int size = sizeof(numbers) / sizeof(numbers[0]);
printf("数组: ");
for (int i = 0; i < size; i++) {
printf("%d ", numbers[i]);
}
printf("\n");
printf("指针初始位置: %p, 值: %d\n", ptr, *ptr);
// 指针加法
ptr = ptr + 1;
printf("ptr + 1: %p, 值: %d\n", ptr, *ptr);
ptr = ptr + 2;
printf("ptr + 2: %p, 值: %d\n", ptr, *ptr);
// 指针减法
ptr = ptr - 1;
printf("ptr - 1: %p, 值: %d\n", ptr, *ptr);
// 指针递增递减
ptr++; // 移动到下一个元素
printf("ptr++: %p, 值: %d\n", ptr, *ptr);
ptr--; // 移回前一个元素
printf("ptr--: %p, 值: %d\n", ptr, *ptr);
// 指针差值
int *first = &numbers[0];
int *last = &numbers[4];
printf("第一个和最后一个元素的差值: %ld\n", last - first);
return 0;
}
指针关系运算
c
#include <stdio.h>
int main() {
printf("=== 指针关系运算 ===\n");
int arr[] = {1, 2, 3, 4, 5};
int *ptr1 = &arr[1]; // 指向第二个元素
int *ptr2 = &arr[3]; // 指向第四个元素
printf("arr[1] = %d, 地址: %p\n", arr[1], ptr1);
printf("arr[3] = %d, 地址: %p\n", arr[3], ptr2);
// 指针比较
if (ptr1 < ptr2) {
printf("ptr1在ptr2之前\n");
}
if (ptr1 > ptr2) {
printf("ptr1在ptr2之后\n");
}
if (ptr1 == &arr[1]) {
printf("ptr1指向arr[1]\n");
}
if (ptr1 != ptr2) {
printf("ptr1和ptr2指向不同的位置\n");
}
// 遍历数组使用指针
printf("使用指针遍历数组: ");
int *current = arr;
for (int i = 0; i < 5; i++) {
printf("%d ", *current);
current++; // 移动到下一个元素
}
printf("\n");
return 0;
}
5. 指针与数组
数组名即指针
c
#include <stdio.h>
int main() {
printf("=== 指针与数组 ===\n");
int numbers[] = {10, 20, 30, 40, 50};
int size = sizeof(numbers) / sizeof(numbers[0]);
printf("数组名numbers: %p\n", numbers);
printf("&numbers[0]: %p\n", &numbers[0]);
printf("数组名等于第一个元素的地址: %s\n",
numbers == &numbers[0] ? "是" : "否");
// 不同的访问方式
printf("\n=== 数组元素访问方式 ===\n");
for (int i = 0; i < size; i++) {
printf("numbers[%d] = %d\n", i, numbers[i]);
printf("*(numbers + %d) = %d\n", i, *(numbers + i));
}
// 指针可以像数组一样使用
int *ptr = numbers;
printf("\n指针像数组一样使用:\n");
for (int i = 0; i < size; i++) {
printf("ptr[%d] = %d\n", i, ptr[i]);
}
return 0;
}
指针与多维数组
c
#include <stdio.h>
#define ROWS 3
#define COLS 4
int main() {
printf("=== 指针与多维数组 ===\n");
int matrix[ROWS][COLS] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// 使用指针访问二维数组
printf("使用指针遍历二维数组:\n");
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
printf("%d\t", *(*(matrix + i) + j));
}
printf("\n");
}
// 数组指针(指向整个一维数组的指针)
int (*row_ptr)[COLS] = matrix;
printf("\n使用数组指针:\n");
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
printf("%d\t", row_ptr[i][j]);
}
printf("\n");
}
return 0;
}
6. 指针与函数
传值调用 vs 传址调用
c
#include <stdio.h>
// 传值调用 - 接收参数的副本
void swapByValue(int a, int b) {
int temp = a;
a = b;
b = temp;
printf("函数内 - a = %d, b = %d\n", a, b);
}
// 传址调用 - 接收参数的地址
void swapByReference(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
printf("函数内 - *a = %d, *b = %d\n", *a, *b);
}
// 返回多个值通过指针参数
void calculate(int a, int b, int *sum, int *product, float *average) {
*sum = a + b;
*product = a * b;
*average = (a + b) / 2.0;
}
int main() {
printf("=== 指针与函数 ===\n");
int x = 5, y = 10;
printf("交换前: x = %d, y = %d\n", x, y);
// 传值调用
swapByValue(x, y);
printf("传值调用后: x = %d, y = %d\n", x, y);
// 传址调用
swapByReference(&x, &y);
printf("传址调用后: x = %d, y = %d\n", x, y);
// 通过指针返回多个值
int sum, product;
float average;
calculate(x, y, &sum, &product, &average);
printf("\n计算结果:\n");
printf("和: %d\n", sum);
printf("积: %d\n", product);
printf("平均值: %.2f\n", average);
return 0;
}
数组作为函数参数
c
#include <stdio.h>
// 函数声明
void printArray(int arr[], int size);
void modifyArray(int *arr, int size);
int* findMax(int arr[], int size);
int main() {
int numbers[] = {23, 45, 12, 67, 34, 89, 56};
int size = sizeof(numbers) / sizeof(numbers[0]);
printf("=== 数组作为函数参数 ===\n");
printf("原始数组: ");
printArray(numbers, size);
// 修改数组
modifyArray(numbers, size);
printf("修改后数组: ");
printArray(numbers, size);
// 返回指针的函数
int *max_ptr = findMax(numbers, size);
printf("最大值: %d (地址: %p)\n", *max_ptr, max_ptr);
return 0;
}
// 使用数组语法
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
// 使用指针语法(两种语法是等价的)
void modifyArray(int *arr, int size) {
for (int i = 0; i < size; i++) {
arr[i] *= 2; // 等价于 *(arr + i) *= 2
}
}
// 返回指针的函数
int* findMax(int arr[], int size) {
int *max_ptr = &arr[0];
for (int i = 1; i < size; i++) {
if (arr[i] > *max_ptr) {
max_ptr = &arr[i];
}
}
return max_ptr;
}
7. 多级指针
二级指针
c
#include <stdio.h>
int main() {
printf("=== 多级指针 ===\n");
int value = 100;
int *ptr = &value; // 一级指针
int **pptr = &ptr; // 二级指针
int ***ppptr = &pptr; // 三级指针
printf("变量value: %d\n", value);
printf("变量value的地址: %p\n", &value);
printf("一级指针ptr: %p\n", ptr);
printf("ptr指向的值: %d\n", *ptr);
printf("ptr的地址: %p\n", &ptr);
printf("二级指针pptr: %p\n", pptr);
printf("pptr指向的值(ptr的地址): %p\n", *pptr);
printf("pptr指向的指针指向的值: %d\n", **pptr);
printf("三级指针ppptr: %p\n", ppptr);
printf("ppptr指向的指针指向的指针指向的值: %d\n", ***ppptr);
// 通过多级指针修改变量值
**pptr = 200;
printf("通过二级指针修改后value的值: %d\n", value);
***ppptr = 300;
printf("通过三级指针修改后value的值: %d\n", value);
return 0;
}
指针数组
c
#include <stdio.h>
int main() {
printf("=== 指针数组 ===\n");
int a = 10, b = 20, c = 30, d = 40;
// 指针数组 - 存储指针的数组
int *ptr_array[4] = {&a, &b, &c, &d};
printf("指针数组内容:\n");
for (int i = 0; i < 4; i++) {
printf("ptr_array[%d] = %p, *ptr_array[%d] = %d\n",
i, ptr_array[i], i, *ptr_array[i]);
}
// 修改通过指针数组
*ptr_array[0] = 100;
*ptr_array[1] = 200;
printf("\n修改后:\n");
printf("a = %d, b = %d\n", a, b);
return 0;
}
8. 动态内存分配
malloc, calloc, realloc, free
c
#include <stdio.h>
#include <stdlib.h>
int main() {
printf("=== 动态内存分配 ===\n");
int *dynamic_array;
int size;
printf("请输入数组大小: ");
scanf("%d", &size);
if (size <= 0) {
printf("错误:大小必须为正数!\n");
return 1;
}
// 使用malloc分配内存(不初始化)
dynamic_array = (int*)malloc(size * sizeof(int));
if (dynamic_array == NULL) {
printf("内存分配失败!\n");
return 1;
}
printf("使用malloc分配的内存(未初始化):\n");
for (int i = 0; i < size; i++) {
printf("%d ", dynamic_array[i]); // 可能包含垃圾值
}
printf("\n");
// 初始化数组
for (int i = 0; i < size; i++) {
dynamic_array[i] = i * 10;
}
printf("初始化后的数组:\n");
for (int i = 0; i < size; i++) {
printf("%d ", dynamic_array[i]);
}
printf("\n");
// 使用calloc分配内存(初始化为0)
int *another_array = (int*)calloc(size, sizeof(int));
printf("使用calloc分配的内存(初始化为0):\n");
for (int i = 0; i < size; i++) {
printf("%d ", another_array[i]);
}
printf("\n");
// 使用realloc调整内存大小
int new_size = size * 2;
dynamic_array = (int*)realloc(dynamic_array, new_size * sizeof(int));
// 初始化新分配的部分
for (int i = size; i < new_size; i++) {
dynamic_array[i] = i * 10;
}
printf("realloc后的数组:\n");
for (int i = 0; i < new_size; i++) {
printf("%d ", dynamic_array[i]);
}
printf("\n");
// 释放内存
free(dynamic_array);
free(another_array);
printf("内存已释放\n");
return 0;
}
动态二维数组
c
#include <stdio.h>
#include <stdlib.h>
int main() {
printf("=== 动态二维数组 ===\n");
int rows, cols;
printf("请输入行数: ");
scanf("%d", &rows);
printf("请输入列数: ");
scanf("%d", &cols);
// 分配行指针数组
int **matrix = (int**)malloc(rows * sizeof(int*));
if (matrix == NULL) {
printf("内存分配失败!\n");
return 1;
}
// 为每一行分配内存
for (int i = 0; i < rows; i++) {
matrix[i] = (int*)malloc(cols * sizeof(int));
if (matrix[i] == NULL) {
printf("内存分配失败!\n");
return 1;
}
}
// 初始化矩阵
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i][j] = i * cols + j + 1;
}
}
// 打印矩阵
printf("动态二维数组:\n");
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%d\t", matrix[i][j]);
}
printf("\n");
}
// 释放内存(按分配的顺序反向释放)
for (int i = 0; i < rows; i++) {
free(matrix[i]);
}
free(matrix);
printf("内存已释放\n");
return 0;
}
9. 函数指针
基本函数指针
c
#include <stdio.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 divide(int a, int b) {
if (b != 0) return a / b;
return 0;
}
// 使用函数指针作为参数
void calculate(int a, int b, int (*operation)(int, int)) {
int result = operation(a, b);
printf("计算结果: %d\n", result);
}
int main() {
printf("=== 函数指针 ===\n");
// 声明函数指针
int (*func_ptr)(int, int);
int x = 10, y = 5;
// 使用函数指针调用不同的函数
func_ptr = add;
printf("加法: ");
calculate(x, y, func_ptr);
func_ptr = subtract;
printf("减法: ");
calculate(x, y, func_ptr);
func_ptr = multiply;
printf("乘法: ");
calculate(x, y, func_ptr);
func_ptr = divide;
printf("除法: ");
calculate(x, y, func_ptr);
// 函数指针数组
int (*operations[])(int, int) = {add, subtract, multiply, divide};
const char* operation_names[] = {"加法", "减法", "乘法", "除法"};
printf("\n使用函数指针数组:\n");
for (int i = 0; i < 4; i++) {
printf("%s: %d\n", operation_names[i], operations[i](x, y));
}
return 0;
}
回调函数
c
#include <stdio.h>
// 回调函数类型定义
typedef int (*CompareFunc)(int, int);
// 比较函数
int ascending(int a, int b) { return a - b; }
int descending(int a, int b) { return b - a; }
// 使用回调函数的排序函数
void bubbleSort(int arr[], int size, CompareFunc compare) {
for (int i = 0; i < size - 1; i++) {
for (int j = 0; j < size - i - 1; j++) {
if (compare(arr[j], arr[j + 1]) > 0) {
// 交换
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
printf("=== 回调函数示例 ===\n");
int numbers[] = {64, 34, 25, 12, 22, 11, 90};
int size = sizeof(numbers) / sizeof(numbers[0]);
printf("原始数组: ");
printArray(numbers, size);
// 升序排序
bubbleSort(numbers, size, ascending);
printf("升序排序: ");
printArray(numbers, size);
// 降序排序
bubbleSort(numbers, size, descending);
printf("降序排序: ");
printArray(numbers, size);
return 0;
}
10. 实际应用示例
示例1:字符串处理工具
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 自定义字符串函数
char* myStrcpy(char *dest, const char *src);
char* myStrcat(char *dest, const char *src);
int myStrcmp(const char *str1, const char *str2);
char* myStrrev(char *str);
int main() {
printf("=== 字符串处理工具 ===\n");
// 动态分配字符串
char *str1 = (char*)malloc(100 * sizeof(char));
char *str2 = (char*)malloc(100 * sizeof(char));
if (str1 == NULL || str2 == NULL) {
printf("内存分配失败!\n");
return 1;
}
// 测试自定义字符串函数
myStrcpy(str1, "Hello");
myStrcpy(str2, "World");
printf("str1: %s\n", str1);
printf("str2: %s\n", str2);
printf("比较结果: %d\n", myStrcmp(str1, str2));
myStrcat(str1, " ");
myStrcat(str1, str2);
printf("连接后: %s\n", str1);
myStrrev(str1);
printf("反转后: %s\n", str1);
// 释放内存
free(str1);
free(str2);
return 0;
}
// 自定义字符串复制
char* myStrcpy(char *dest, const char *src) {
char *start = dest;
while (*src != '\0') {
*dest = *src;
dest++;
src++;
}
*dest = '\0';
return start;
}
// 自定义字符串连接
char* myStrcat(char *dest, const char *src) {
char *start = dest;
// 找到dest的结尾
while (*dest != '\0') {
dest++;
}
// 复制src到dest的结尾
while (*src != '\0') {
*dest = *src;
dest++;
src++;
}
*dest = '\0';
return start;
}
// 自定义字符串比较
int myStrcmp(const char *str1, const char *str2) {
while (*str1 && *str2 && *str1 == *str2) {
str1++;
str2++;
}
return *str1 - *str2;
}
// 自定义字符串反转
char* myStrrev(char *str) {
if (str == NULL) return NULL;
char *start = str;
char *end = str;
char temp;
// 找到字符串结尾
while (*end != '\0') {
end++;
}
end--; // 指向最后一个字符
// 反转字符串
while (start < end) {
temp = *start;
*start = *end;
*end = temp;
start++;
end--;
}
return str;
}
示例2:学生管理系统
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_NAME_LENGTH 50
typedef struct {
char name[MAX_NAME_LENGTH];
int age;
float score;
} Student;
// 函数声明
Student* createStudent(const char *name, int age, float score);
void displayStudent(const Student *student);
void updateStudentScore(Student *student, float new_score);
void freeStudent(Student *student);
int main() {
printf("=== 学生管理系统 ===\n");
// 动态创建学生
Student *student1 = createStudent("张三", 20, 85.5);
Student *student2 = createStudent("李四", 22, 92.0);
Student *student3 = createStudent("王五", 21, 78.5);
if (student1 == NULL || student2 == NULL || student3 == NULL) {
printf("学生创建失败!\n");
return 1;
}
// 显示学生信息
printf("=== 学生信息 ===\n");
displayStudent(student1);
displayStudent(student2);
displayStudent(student3);
// 更新成绩
printf("\n=== 更新成绩 ===\n");
updateStudentScore(student3, 88.0);
displayStudent(student3);
// 学生数组
Student *students[] = {student1, student2, student3};
int student_count = 3;
// 计算平均分
float total_score = 0;
for (int i = 0; i < student_count; i++) {
total_score += students[i]->score;
}
printf("\n平均分: %.2f\n", total_score / student_count);
// 释放内存
for (int i = 0; i < student_count; i++) {
freeStudent(students[i]);
}
printf("内存已释放\n");
return 0;
}
// 创建学生
Student* createStudent(const char *name, int age, float score) {
Student *student = (Student*)malloc(sizeof(Student));
if (student == NULL) {
return NULL;
}
strncpy(student->name, name, MAX_NAME_LENGTH - 1);
student->name[MAX_NAME_LENGTH - 1] = '\0';
student->age = age;
student->score = score;
return student;
}
// 显示学生信息
void displayStudent(const Student *student) {
printf("姓名: %s, 年龄: %d, 分数: %.1f\n",
student->name, student->age, student->score);
}
// 更新学生成绩
void updateStudentScore(Student *student, float new_score) {
student->score = new_score;
printf("%s的成绩已更新为: %.1f\n", student->name, new_score);
}
// 释放学生内存
void freeStudent(Student *student) {
free(student);
}
11. 常见指针错误
c
#include <stdio.h>
#include <stdlib.h>
int main() {
printf("=== 常见指针错误 ===\n");
// ❌ 错误1:使用未初始化的指针
/*
int *ptr;
printf("%d\n", *ptr); // 未定义行为
*/
// ✅ 正确:总是初始化指针
int x = 10;
int *ptr = &x;
printf("正确使用: %d\n", *ptr);
// ❌ 错误2:访问已释放的内存
/*
int *dynamic_ptr = (int*)malloc(sizeof(int));
*dynamic_ptr = 100;
free(dynamic_ptr);
printf("%d\n", *dynamic_ptr); // 错误:访问已释放的内存
*/
// ✅ 正确:释放后设为NULL
int *dynamic_ptr = (int*)malloc(sizeof(int));
*dynamic_ptr = 100;
free(dynamic_ptr);
dynamic_ptr = NULL; // 防止悬空指针
// ❌ 错误3:内存泄漏
/*
void leakMemory() {
int *ptr = (int*)malloc(100 * sizeof(int));
// 忘记free(ptr)
}
*/
// ✅ 正确:配对使用malloc/free
int *safe_ptr = (int*)malloc(sizeof(int));
if (safe_ptr != NULL) {
*safe_ptr = 50;
printf("安全使用: %d\n", *safe_ptr);
free(safe_ptr);
}
// ❌ 错误4:返回局部变量的地址
/*
int* dangerousFunction() {
int local_var = 100;
return &local_var; // 错误:返回局部变量的地址
}
*/
// ✅ 正确:返回动态分配的内存或静态变量
int* safeFunction() {
static int static_var = 200; // 静态变量
return &static_var;
}
int *result = safeFunction();
printf("安全返回: %d\n", *result);
return 0;
}