指针是C语言的灵魂所在,也是许多初学者感到困惑的概念。本文将带你系统学习指针的基础知识,从指针的本质到指针运算,再到指针与数组的关系,最后介绍二级指针和指针数组的概念。通过本文的学习,你将建立起对指针的全面认识。
一、指针是什么?
指针本质上就是内存地址。当我们说"指针"时,通常指的是指针变量——即存储内存地址的变量。因为指针变量存了地址时也是指针,所口语中说的指针通常指的是指针变量
int a = 10;
int *p = &a; // p是指针变量,存储了变量a的地址
二、指针与指针类型
指针类型不仅决定了指针所指向数据的类型,还决定了:
解引用时的权限:即能访问多少字节
- 整型指针(
int*
)解引用可访问4字节 - 字符指针(
char*
)解引用可访问1字节
- 整型指针(
指针运算的步长:
- 整型指针
+1
,地址实际增加4(跳过1个整型) - 字符指针
+1
,地址实际增加1(跳过1个字符)
- 整型指针
int arr[5] = {0};
int *p = arr;
p = p + 1; // 地址增加4,指向arr[1]
三、野指针及其避免方法
3.1 什么是野指针?
野指针是指向未知内存位置的指针,使用野指针会导致未定义行为。
3.2 野指针产生的原因:
- 指针未初始化
- 指针越界访问
- 指针指向的空间已被释放
- 接收函数返回的局部变量地址(函数结束后变量销毁)
3.3 如何避免野指针?
初始化指针:
- 明确知道地址时初始化为具体地址
- 不知道地址时初始化为
NULL
(需要包含<stdio.h>
)
小心指针越界:C语言不会自动检查数组越界
空间释放后及时置NULL:
free(p); p = NULL;
使用前检查有效性:
if (p != NULL) { // 安全使用指针 }
四、指针运算
指针支持以下几种运算:
指针 ± 整数:移动指针位置
int arr[5]; int *p = arr; p = p + 3; // 指向arr[3]
指针 - 指针:计算两个指针之间的元素个数
- 如&arr[9]-&arr[0],如这里得到的是9,是两个指针之间的元素个数(语法规定),前提是两个指针指向同一块空间(指向同一块空间肯定类型相同)
注意:两个指针必须指向同一块连续内存空间int arr[10]; int n = &arr[9] - &arr[0]; // n = 9
- 指针关系运算:比较指针大小
- 标准规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较
int arr[5]; int *p = arr; while (p < &arr[5]) { // 合法比较 // ... p++; }
4.1 应用:实现strlen函数
int my_strlen(const char *str) {
const char *start = str;
while (*str != '\0') {
str++;
}
return str - start; // 指针相减得到字符个数
}
五、指针与数组
数组名在大多数情况下表示首元素地址,所以p+i是下标为i的元素的地址,用指针可以解引用来访问:
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr; // p指向arr[0]
访问数组元素的多种等价方式:
arr[2] <=> *(arr + 2)
*(p + 2) <=> *(2 + p) <=> *(2 + arr) <=> 2[arr]
原理:arr[2]
会被编译器转换为*(arr + 2)
,因为[]是操作符,2和arr是两个操作数,虽然我们写的是arr[2],但编译器处理时会转化为*(arr+2),所以知道代码底层原理,代码怎么写都行,很灵活
六、二级指针
二级指针是指向指针的指针:
int a = 10;
int *pa = &a; // 一级指针
int **ppa = &pa; // 二级指针
声明方式:
int **ppa; // 靠近类型
int ** ppa; // 两边空格
int** ppa; // 靠近变量名
靠近int,靠近ppa,两边都空格,三种写法都行,语法都支持,只是习惯问题
三级及以上指针虽然语法支持,但实际开发中很少使用。
七、指针数组
指针数组是存放指针的数组:
int a = 1, b = 2, c = 3;
int *arr[3] = {&a, &b, &c}; // 整型指针数组
类比:
- 整型数组存放整型
- 字符数组存放字符
- 所以指针数组存放指针
指针是C语言强大灵活性的重要体现。本文我们介绍了指针的基础概念、常见问题和应用场景。虽然这些只是指针初阶的内容,但是对于我们认识指针,写相关的代码已经足够了。未来我们还会进一步介绍指针进阶相关的内容。
下节预告:我们将介绍结构体初阶方面的内容,敬请期待!
作者其他文章链接:
[C语言初阶]操作符
[C语言初阶]扫雷小游戏
[C语言初阶]三子棋小游戏
Gitee详细使用教程