指针
1. 定义
不同概念
本篇讲义所说的指针,和 体系结构 中的指针不是同一个概念:前者指语言特性,是 C 语言中一类类型的统称;后者指特定的整数字节地址(偏移量)。
- 指针是一种对象类型,它引用函数或另一种类型的对象
- 指针可以不引用任何内容,此时他是一个空指针
2. 声明
指针声明符的格式:
* 声明符(一个星加上另一个声明符)函数声明符:
声明符 ( 形参列表 )
数组声明符:声明符 [ 表达式 ]
当上述两行的声明符是指针声明符时,得加括号其中
声明符可以是标识符,也可以是其他的声明符(包括指针声明符)如果指针声明符是其他声明符的
声明符部分,那么声明一个“指向”其他声明符声明的类型“的指针”cint **p; // '**p' 是 指针声明符 // 他的“声明符”部分为 '*p',是个指针声明符 // 声明一个指向“指向 int 的指针”的指针1
2
3
4
3. 取地址与解引用
指针最基础的两个操作是取地址 & 和解引用 *。前者从对象得到地址,后者通过地址访问对象。
int n = 10;
int *p = &n;
*p = 20;2
3
4
运行结果:该代码块主要用于语法或结构说明,单独运行通常无终端输出。
这里 p 保存 n 的地址,对 *p 赋值会直接修改 n。空指针或无效地址解引用会导致未定义行为,因此解引用前应保证地址有效。
4. 空指针
空指针表示“不指向任何对象”,通常写作 NULL。空指针可以比较、赋值、传参,但不能解引用。
#include <stddef.h>
int *p = NULL;
if (p == NULL) {
/* 尚未绑定对象 */
}2
3
4
5
6
运行结果:该代码块主要用于语法或结构说明,单独运行通常无终端输出。
5. 指针与数组
数组表达式在大多数上下文会退化为首元素指针,因此数组与指针在访问语法上经常看起来相似,但两者并不是同一种类型。
int arr[4] = {1, 2, 3, 4};
int *p = arr; /* 等价于 &arr[0] */
int a = arr[2];
int b = p[2];2
3
4
5
运行结果:该代码块主要用于语法或结构说明,单独运行通常无终端输出。
arr 的类型是“4 个 int 的数组”,p 的类型是“指向 int 的指针”;sizeof arr 与 sizeof p 一般不同。
6. 指针算术
指针加减是按“所指对象类型大小”移动,而不是按字节裸移动。p + 1 表示跳到下一个同类型对象地址。
int arr[3] = {10, 20, 30};
int *p = arr;
int x = *(p + 1); /* 20 */2
3
4
运行结果:该代码块主要用于语法或结构说明,单独运行通常无终端输出。
越过同一数组对象边界(除“尾后位置”外)进行访问是未定义行为。
7. const 与指针
const 位置不同,含义不同:
int a = 1;
int b = 2;
const int *p1 = &a; /* 不能通过 p1 修改对象值 */
int *const p2 = &a; /* p2 的地址绑定不可变 */2
3
4
5
运行结果:该代码块主要用于语法或结构说明,单独运行通常无终端输出。
这两种形式分别约束“所指对象可改性”和“指针自身可改性”,阅读声明时要分开判断。
8. 函数指针
函数也有地址,可通过函数指针实现回调与策略分发。
int add(int x, int y) {
return x + y;
}
int main(void) {
int (*fp)(int, int) = add;
return fp(3, 4) == 7 ? 0 : 1;
}2
3
4
5
6
7
8
运行结果:该代码块主要用于语法或结构说明,单独运行通常无终端输出。
函数指针是 C 接口抽象的关键能力,标准库中的 qsort、bsearch 都依赖它。