6、C 语言指针初阶知识点总结
指针的基础概念、特殊指针类型、指针运算等方面,指针初阶的核心知识点,补充实用注意事项
C 语言指针初阶核心知识点总结
一、指针入门
1. 基础概念:内存与地址
内存地址与字节
- 字节(byte):内存的基本单位,1 字节 = 8 位(bit),可表示 0~255 的数值(二进制00000000~11111111)。
- 地址:系统为每个字节分配的唯一编号(如0x00000000~0xFFFFFFFF),用于区分不同内存位置。
- 地址特性:地址 + 1 表示偏移 1 个字节(即下一个字节的编号),与数据类型无关。
基地址
- 单字节数据:地址就是其自身字节的编号(如char ch = 'a',&ch即为该字节的地址)。
- 多字节数据:由多个连续字节组成,其地址为所有字节中最小的编号(称为基地址)。
示例:int num = 100(4 字节),基地址为第一个字节的编号(如0x0A33FF04),后续字节地址依次为0x0A33FF05、0x0A33FF06、0x0A33FF07。
取址符(&)
- 功能:获取变量所占用内存的基地址。
示例:int num = 100; printf("%p", &num); 输出num的基地址。
- 特性:
- 无论变量类型(int、char 等),地址的尺寸固定(32 位系统 4 字节,64 位系统 8 字节)。
- 地址的逻辑含义由指向的数据类型决定(如int*与char*地址数值可能相同,但指向的内存尺寸不同)。
2. 指针的定义与本质
指针的双重含义
- 地址:变量的内存地址(如&num是指向num的指针)。
- 指针变量:存储地址的变量(简称为 “指针”),定义语法为类型* 指针名(如int* p表示p是存储 int 型变量地址的指针)。
指针的初始化
- 核心原则:指针必须指向合法内存,否则会成为野指针(危险)。
- 初始化方式:
- 指向已定义的变量:int num = 100; int* p = #(p存储num的地址)。
- 指向空地址(安全的初始值):int* p = NULL;(NULL是定义为(void*)0的宏,代表零地址的保留区)。
指针的解引用(*)
- 功能:通过指针访问其指向的内存内容(“降级操作”,从地址获取值)。
示例:int num = 100; int* p = # *p = 200;(通过p修改num的值为 200)。
- 注意:*在定义时表示 “指针类型”(如int* p),在使用时表示 “解引用”(如*p = 200)。
二、特殊指针
1. 野指针
- 定义:指向未知或非法内存的指针(未初始化、指向已回收内存或越界)。
- 危害:
- 访问野指针可能导致段错误(Segmentation fault)。
- 可能修改系统关键数据,导致程序崩溃或系统异常。
- 产生原因:
- 指针未初始化:int* p; *p = 100;(p乱指)。
- 指向已释放的内存:char* p = malloc(10); free(p); *p = 'a';(p指向的内存已被回收)。
- 指针越界:char buf[5] = "abc"; char* p = buf; *(p+10) = 'd';(访问超出buf范围的内存)。
- 预防措施:
- 定义时立即初始化(如int* p = NULL;)。
- 释放内存后将指针置为NULL(如free(p); p = NULL;)。
- 操作数组时检查边界(如用宏计算长度#define LEN sizeof(arr)/sizeof(arr[0]))。
2. 空指针(NULL)
- 定义:指向零地址(0x00000000)的指针,该地址是系统保留区(不可访问)。
- 作用:作为指针的安全初始值,避免野指针(如int* p = NULL;表示p暂时无有效指向)。
- 注意:访问NULL会触发段错误(提示指针未正确初始化),便于调试。
三、指针运算
1. 指针的大小
- 特性:指针的尺寸仅由系统位数决定,与指向的数据类型无关。
- 32 位系统:所有指针均为 4 字节。
- 64 位系统:所有指针均为 8 字节。
- 示例:
char* p1; int* p2; double* p3; printf("%lu, %lu, %lu", sizeof(p1), sizeof(p2), sizeof(p3)); // 64位系统输出:8, 8, 8 |
2. 指针的加减运算
- 本质:指针的偏移量由指向的数据类型决定(“按类型尺寸移动”)。
- 规则:
- 指针 + 1:地址增加 “指向类型的字节数”(如int* p,p+1表示地址 + 4 字节)。
- 指针 - 1:地址减少 “指向类型的字节数”(如char* p,p-1表示地址 - 1 字节)。
- 示例:
int num = 100; int* p = # printf("p: %p, p+1: %p", p, p+1); // 输出地址相差4字节(如0x7ffd12345678 和 0x7ffd1234567c) |
3. 指针运算的应用
- 遍历数组:通过指针加减访问数组元素(数组名本质是首元素的指针)。
int arr[3] = {1,2,3}; int* p = arr; for (int i=0; i<3; i++) { printf("%d ", *(p+i)); // 等价于 arr[i],输出1 2 3 } |
四、核心注意事项
- 指针与数组的关系:数组名是首元素的指针(常量指针,不可修改),如int arr[5]; int* p = arr;(p与arr均指向arr[0])。
- 避免指针越界:访问数组或动态内存时,严格限制指针范围在申请的内存内(如for (int i=0; i<LEN; i++))。
- 指针初始化检查:使用指针前判断是否为NULL(如if (p != NULL) { *p = 100; })。
- 动态内存管理:malloc申请的内存需用free释放,释放后立即置为NULL(避免野指针)。
总结
指针是 C 语言的核心,其本质是 “存储地址的变量”,通过地址间接访问内存。掌握指针需理解:地址与指针变量的区别、初始化的重要性、解引用的作用,以及野指针的危害与预防。指针运算的关键是 “按类型偏移”,这是数组遍历、动态内存操作的基础。正确使用指针能提升程序效率,但需严格遵循安全规则,避免内存错误。