一、先搞懂:指针的本质 —— 内存地址的 “代言人”
在学指针之前,必须先理解计算机内存的底层逻辑,这是搞懂指针的核心:
-
内存的基本单位:计算机内存以「字节(Byte)」为最小存储单元,1Byte=8bit(位),每个 bit 可存储 0 或 1;内存容量换算:1TB=1024GB,1GB=1024MB,1MB=1024KB,1KB=1024Byte
-
内存地址的分配规则:
- X86(32 位)计算机有 32 根地址线,每根地址线的电荷状态(0/1)组合成唯一的 32 位二进制编号,对应内存中一个字节的地址,总寻址空间为 232Byte=4GB;
- X64(64 位)计算机有 64 根地址线,总寻址空间为 264Byte(理论值);
- 内存中每一个字节都有唯一的地址编号,指针的本质就是 “保存这个地址编号的变量”
-
核心结论:
- 任何一片内存空间(比如
int a)的地址,都是它第一个字节的地址; - 指针变量的作用:存储某块内存的首字节地址,通过这个地址可以访问对应内存的值
- 任何一片内存空间(比如
二、指针的基础语法:定义、取地址、解引用
1. 指针的定义格式
指针的定义必须指定「类型」,格式:Type* 指针变量名;
#include <stdio.h>
int main() {
int a = 10; // 定义int类型变量,占用4个字节
int* p = &a; // 定义int*类型指针,存储a的首字节地址
char c = 'A'; // 定义char类型变量,占用1个字节
char* pc = &c; // 定义char*类型指针,存储c的首字节地址
// 指针变量本身的大小:X86下所有指针都是4字节,X64下都是8字节
printf("int*指针大小:%zd\n", sizeof(p)); // X86下输出4
printf("char*指针大小:%zd\n", sizeof(pc)); // X86下输出4
return 0;
}
2. 两个核心运算符:&(取地址)和 *(解引用)
| 运算符 | 作用 | 示例 |
|---|---|---|
& | 提取某块内存的首字节地址,返回Type*类型 | int* p = &a; // 取 a 的地址赋值给 p |
* | 解引用:通过指针保存的地址,访问对应内存的值(访问长度 = sizeof (Type)) | *p = 20; // 修改 a 的值为 20 |
关键示例(对应笔记核心):
#include <stdio.h>
int main() {
int a = 10; // 内存布局(小端存储):
// 地址0X00000001:00001010(10的二进制低8位)
// 地址0X00000002:00000000
// 地址0X00000003:00000000
// 地址0X00000004:00000000
int* p = &a; // p存储a的首地址0X00000001
printf("a的值:%d\n", a); // 输出10
printf("a的地址:%p\n", &a); // 输出0X00000001(示例地址)
printf("p存储的地址:%p\n", p); // 输出0X00000001
printf("解引用p:%d\n", *p); // 输出10(访问p指向的4个字节)
// 解引用修改值
*p = 20;
printf("修改后a的值:%d\n", a); // 输出20
return 0;
}
三、指针的 “能力”:由类型决定(核心重点)
指针的类型(如char*/int*/double*)不影响指针变量本身的大小,但决定了两个关键能力:
-
解引用能力:
*p能访问的字节数 =sizeof(Type)char* p:解引用仅访问 1 个字节;int* p:解引用访问 4 个字节;double* p:解引用访问 8 个字节。
-
加减能力:
p±n实际移动的字节数 =sizeof(Type) * n
#include <stdio.h>
int main() {
int a = 10;
char* p1 = &a;
short* p2 = &a;
int* p3 = &a;
double* p4 = &a;
printf("p1+1 = %p\n", p1 + 1); // 移动1字节
printf("p2+1 = %p\n", p2 + 1); // 移动2字节
printf("p3+1 = %p\n", p3 + 1); // 移动4字节
printf("p4+1 = %p\n", p4 + 1); // 移动8字节
return 0;
}
四、指针与数组:核心关联(笔记重点梳理)
数组和指针是 C 语言中密不可分的部分,核心结论先记住:
数组名
arr在绝大多数情况下会 “退化” 为数组首元素的地址(Type*),仅两种情况例外:sizeof(arr)、&arr。
1. 数组名的两种特殊情况
c
运行
#include <stdio.h>
int main() {
int arr[5] = {1,2,3,4,5};
// 情况1:sizeof(arr) —— arr代表整个数组,计算数组总大小
printf("数组总大小:%zd\n", sizeof(arr)); // 输出20(5*4)
// 情况2:&arr —— arr代表整个数组,返回数组类型的指针(int(*)[5])
int(*parr)[5] = &arr;
printf("&arr+1 = %p\n", &arr + 1); // 移动20字节(整个数组大小)
// 普通情况:arr退化为首元素地址(int*)
printf("arr+1 = %p\n", arr + 1); // 移动4字节(int大小)
return 0;
}
2. 数组访问的本质:arr[i] == *(arr+i)
数组的[]运算符是语法,底层本质是指针运算,这也是指针能遍历数组的核心:
#include <stdio.h>
int main() {
int arr[5] = {1,2,3,4,5};
int* p = arr; // p指向数组首元素
// 三种等价的数组访问方式
for (int i = 0; i < 5; i++) {
printf("arr[%d] = %d | *(arr+%d) = %d | p[%d] = %d\n",
i, arr[i], i, *(arr+i), i, p[i]);
}
// 甚至可以反着写(仅演示,不推荐)
printf("i[arr] = %d\n", 2[arr]); // 等价于*(2+arr),输出3
return 0;
}
3. 二维数组与指针(进阶重点)
二维数组可理解为 “数组的数组”,比如int arr[3][4]:
arr退化后类型为int(*)[4](指向包含 4 个 int 的一维数组);arr[i]退化后类型为int*(指向第 i 行的首元素);arr[i][j] == *(*(arr+i)+j)。
#include <stdio.h>
int main() {
int arr[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
// 访问第2行第3列的元素(6)
printf("arr[1][2] = %d\n", arr[1][2]); // 直接访问
printf("*(*(arr+1)+2) = %d\n", *(*(arr+1)+2)); // 指针运算访问
return 0;
}
五、指针的避坑指南:野指针、指针越界
- 野指针:指向未申请 / 非法内存的指针,解引用会导致程序崩溃
// 错误示例:野指针解引用
int* p = 0; // p指向地址0(非法内存)
printf("%d", *p); // 程序崩溃
- 指针越界:访问超出数组 / 内存范围的地址
// 错误示例:数组越界
int arr[5] = {1,2,3,4,5};
int* p = arr + 10; // 超出数组范围
printf("%d", *p); // 访问非法内存,结果未知
六、实战案例:指针的经典应用
1. 指针实现交换函数(笔记核心案例)
#include <stdio.h>
// 指针交换两个变量的值(无临时变量也可用位运算,见之前的补充)
void swap(int* a, int* b) {
if (a == b) return; // 避免地址相同导致错误
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 1, y = 2;
printf("交换前:x=%d, y=%d\n", x, y);
swap(&x, &y); // 传入地址
printf("交换后:x=%d, y=%d\n", x, y); // 输出x=2, y=1
return 0;
}
2. 指针遍历数组(高效写法)
#include <stdio.h>
void printArray(int* arr, int len) {
int* p = arr;
while (p < arr + len) {
printf("%d ", *p++); // *p++ 先解引用,再移动指针
}
printf("\n");
}
int main() {
int arr[10] = {0,1,2,3,4,5,6,7,8,9};
printArray(arr, 10); // 输出0 1 2 3 4 5 6 7 8 9
return 0;
}
&spm=1001.2101.3001.5002&articleId=158932271&d=1&t=3&u=b2e2d9cbc26a46ee8f39dbfd6a04bb72)
986

被折叠的 条评论
为什么被折叠?



