一、指针是什么?
1. 生活中的比喻
-
变量 = 一个房子(里面住着数据)
-
内存地址 = 房子的门牌号(比如"幸福路123号")
-
指针 = 一张写着门牌号的纸条
int number = 10; // 在"内存小区"里建了个房子,里面住着10
int *pointer; // 准备一张空白的门牌号纸条
pointer = &number; // 在纸条上写下number房子的门牌号
2. 为什么要用指针?
场景:你要把一本很厚的书(大数据)给朋友看
方法1(值传递):把整本书复印一份给他
-
优点:安全,原书不会被弄坏
-
缺点:复印很慢,浪费纸张
方法2(地址传递):把藏书地址告诉他
-
优点:快速,不浪费资源
-
缺点:朋友可能把书弄坏
二、指针基础操作
1. 两个重要符号
cpp
#include <stdio.h>
int main() {
int age = 25; // 定义一个整数变量
// & 符号:取地址(问"你住在哪?")
printf("age的值:%d\n", age); // 输出:25
printf("age的地址:%p\n", &age); // 输出:0x7ffd42a1b4bc(类似)
// * 符号:解引用(按地址"敲门")
int *p = &age; // p是指针,存储age的地址
printf("通过指针访问:%d\n", *p); // 输出:25
// 通过指针修改变量
*p = 30; // 通过地址修改age的值
printf("修改后的age:%d\n", age); // 输出:30
return 0;
}
2. 指针的"类型"
指针必须知道它指向的是什么类型的数据:
int number = 100;
int *int_pointer = &number; // 正确:指向整数
char letter = 'A';
char *char_pointer = &letter; // 正确:指向字符
// int_pointer = &letter; // 错误:类型不匹配!
三、值传递 vs 地址传递
1. 值传递示例
cpp
#include <stdio.h>
// 函数:尝试修改数值(但不会成功)
void tryToChange(int x) {
x = 100; // 只修改了副本
printf("函数内部:x = %d\n", x); // 输出:100
}
int main() {
int score = 60;
printf("调用前:score = %d\n", score); // 输出:60
tryToChange(score);
printf("调用后:score = %d\n", score); // 输出:60(没变!)
return 0;
}
2. 地址传递示例
cpp
#include <stdio.h>
// 函数:通过地址真正修改变量
void reallyChange(int *x) {
*x = 100; // 通过地址修改原变量
printf("函数内部:*x = %d\n", *x); // 输出:100
}
int main() {
int score = 60;
printf("调用前:score = %d\n", score); // 输出:60
reallyChange(&score); // 传递地址
printf("调用后:score = %d\n", score); // 输出:100(变了!)
return 0;
}
四、指针与数组
1. 数组名就是地址
cpp
#include <stdio.h>
int main() {
int scores[5] = {85, 90, 78, 92, 88};
printf("=== 数组的多种访问方式 ===\n");
// 方法1:传统数组下标
printf("数组下标:");
for(int i = 0; i < 5; i++) {
printf("%d ", scores[i]);
}
printf("\n");
// 方法2:指针算术
printf("指针算术:");
int *p = scores; // 数组名就是首地址
for(int i = 0; i < 5; i++) {
printf("%d ", *(p + i));
}
printf("\n");
// 方法3:移动指针
printf("移动指针:");
p = scores; // 重新指向开头
for(int i = 0; i < 5; i++) {
printf("%d ", *p);
p++; // 指针移动到下一个元素
}
printf("\n");
return 0;
}
2. 指针运算的"步长"
cpp
#include <stdio.h>
int main() {
int numbers[3] = {10, 20, 30};
int *p = numbers;
printf("指针运算演示:\n");
printf("p指向:%d,地址:%p\n", *p, p);
printf("p+1指向:%d,地址:%p\n", *(p+1), p+1);
printf("地址差:%ld字节\n", (char*)(p+1) - (char*)p);
return 0;
}
输出:
p指向:10,地址:0x7ffd42a1b4bc
p+1指向:20,地址:0x7ffd42a1b4c0
地址差:4字节
五、字符串操作
1. 字符串基础
cpp
#include <stdio.h>
int main() {
// 字符串的两种定义方式
char str1[] = "Hello"; // 字符数组,可以修改
char *str2 = "World"; // 字符串常量,不能修改
printf("str1: %s\n", str1);
printf("str2: %s\n", str2);
str1[0] = 'h'; // 正确:可以修改
// str2[0] = 'w'; // 错误:不能修改常量
return 0;
}
2. 自己实现字符串函数
cpp
#include <stdio.h>
// 计算字符串长度
int myStrlen(const char *str) {
int len = 0;
while(str[len] != '\0') { // 找到结束符
len++;
}
return len;
}
// 字符串复制
void myStrcpy(char *dest, const char *src) {
int i = 0;
while(src[i] != '\0') {
dest[i] = src[i];
i++;
}
dest[i] = '\0'; // 别忘了结束符!
}
int main() {
char source[] = "Programming";
char destination[20];
printf("'%s'的长度:%d\n", source, myStrlen(source));
myStrcpy(destination, source);
printf("复制结果:%s\n", destination);
return 0;
}
六、高级指针概念
1. 指针函数(返回指针的函数)
cpp
#include <stdio.h>
// 返回字符串的指针
char* getGreeting() {
static char greeting[] = "Hello, Friend!"; // static让变量持久存在
return greeting;
}
int main() {
char *msg = getGreeting();
printf("问候语:%s\n", msg); // 输出:Hello, Friend!
return 0;
}
2. 函数指针(指向函数的指针)
cpp
#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 main() {
// 定义函数指针
int (*operation)(int, int);
int x = 10, y = 5;
// 让指针指向不同的函数
operation = add;
printf("加法:%d\n", operation(x, y));
operation = subtract;
printf("减法:%d\n", operation(x, y));
operation = multiply;
printf("乘法:%d\n", operation(x, y));
return 0;
}
3. 二级指针(指向指针的指针)
cpp
#include <stdio.h>
int main() {
int number = 100;
int *p1 = &number; // 一级指针:指向number
int **p2 = &p1; // 二级指针:指向p1
printf("number的值:%d\n", number); // 100
printf("*p1的值:%d\n", *p1); // 100
printf("**p2的值:%d\n", **p2); // 100
printf("number的地址:%p\n", &number);
printf("p1存储的地址:%p\n", p1);
printf("p2存储的地址:%p\n", p2);
return 0;
}
七、const与指针
1. 四种const指针
cpp
#include <stdio.h>
int main() {
char text[] = "example";
// 1. 指向常量的指针
const char *p1 = text;
// *p1 = 'E'; // 错误:不能修改内容
p1 = "new"; // 正确:可以改变指向
// 2. 指针常量
char *const p2 = text;
*p2 = 'E'; // 正确:可以修改内容
// p2 = "new"; // 错误:不能改变指向
// 3. 指向常量的指针常量
const char *const p3 = text;
// *p3 = 'E'; // 错误
// p3 = "new"; // 错误
return 0;
}
八、重要注意事项
1. 避免野指针
cpp
#include <stdio.h>
int main() {
// 危险:野指针(指向随机地址)
// int *dangerous_pointer;
// *dangerous_pointer = 100; // 可能导致程序崩溃
// 安全:初始化为NULL
int *safe_pointer = NULL;
// 使用前检查
if(safe_pointer != NULL) {
*safe_pointer = 100;
} else {
printf("指针还未指向有效内存!\n");
}
return 0;
}
2. 指针使用口诀
定义指针要初始化,野指针很危险
传递大数用地址,效率提升很明显
数组名就是首地址,指针运算很方便
字符串要注意结束符,const保护更安全
总结
指针就像数据的"遥控器":
-
直接访问:直接操作变量
-
间接访问:通过指针操作变量
-
优点:高效、灵活
-
注意事项:类型匹配、避免野指针