1. 普通指针
定义声明
声明 |
类型 |
含义 |
int a |
int |
整型变量 |
int *a |
int * |
(一级指针)指向整型变量的指针 |
int **a |
int *(*) |
(二级指针)指向一个指向整型变量指针的指针 |
int *(*a) |
int *(*) |
(二级指针)指向一个指向整型变量指针的指针 |
申请与释放空间
// 申请空间
int *p = (int *) malloc(sizeof(int));
// 释放空间
free(p);
// 申请空间
int *p = new int;
// 释放空间
delete p;
取地址(&)和解引用(*)
(已定义int a
和int *p
)
语句/表达式 |
含义 |
a = 2; |
2 赋值给 a |
&a |
取得 a 的地址 |
p = &a; |
a 的地址赋值给指针 p |
*p |
指针 p 所指向的内容 |
*p = 2; |
2 赋值给 p 指向的内容(即 a) |
&p |
取得指针 p 的地址 |
语句/表达式 |
含义 |
p++ |
p 地址自增一个 int 长度(4 字节) |
++p |
p 地址自增一个 int 长度(4 字节) |
*p++ |
先获取 p 所指向的内容,p 地址再自增一个 int 长度(4 字节)(*和++同优先级,结合方向为从右往左) |
*(p++) |
先获取 p 所指向的内容,p 地址再自增一个 int 长度(4 字节) |
++*p |
p 地址先自增一个 int 长度(4 字节),再获取 p 所指向的内容(*和++同优先级,结合方向为从右往左) |
*(++p) |
p 地址先自增一个 int 长度(4 字节),再获取 p 所指向的内容 |
(*p)++ |
p 指向的内容自增 |
++(*p) |
p 指向的内容自增 |
语句/表达式 |
含义 |
(int*)0x1234 |
将 0x1234 强制转换为地址(绝对地址) |
p = (int*)0x1234; |
p 指向地址 0x1234 |
*((int*)0x1234) = 2; |
往地址 0x1234 处写入 2(嵌入式常用) |
*(int*)(0x1234) = 2; |
往地址 0x1234 处写入 2(嵌入式常用) |
*((int* const)0x1234) = 2; |
往地址 0x1234 处写入 2(嵌入式常用) |
*(int* const)(0x1234) = 2; |
往地址 0x1234 处写入 2(嵌入式常用) |
*((int*)p) = 2; |
往指针 p 所指向的内容写入 2 |
*(int*)p = 2; |
往指针 p 所指向的内容写入 2 |
作函数形参
语句/表达式 |
含义 |
void fun (int a, int *b) |
形参 b 为整型指针 |
2. 一维数组
定义声明
声明 |
类型 |
含义 |
int a[10] |
int [10] |
有 10 个整型变量的数组 |
int *a[10] |
int *[10] |
(指针数组)有 10 个整型变量指针的数组 |
int (*a)[10] |
int (*)[10] |
(数组指针)指向有 10 个整型数组的指针 |
提示:如何记忆指针数组和数组指针的区别?
- X 型数组(数组里放的是 X 类型的数据):整型数组、浮点型数组、字符型数组、指针数组
- X 型指针(指针指向 X 类型的数据):整型指针、浮点型指针、字符型指针、数组指针、函数指针
申请与释放空间
// 申请空间
int *p = (int *) malloc(10 * sizeof(int));
// 释放空间
free(p);
// 申请空间
int *p = new int[10];
// 释放空间
delete []p;
取地址(&)和解引用(*)
(已定义int a[10]
和int *p1, *p2
)
语句/表达式 |
含义 |
a |
a[0] 地址 |
&a |
取得 a[0] 地址 |
&a[0] |
取得 a[0] 地址 |
&a[5] |
取得 a[5] 地址 |
a + 5 |
a[5] 地址 |
*(a + 5) |
a[5] 内容 |
*(a + 5) = 2; |
2 赋值给 a[5] |
p1 = a; |
p1 指向 a[0] 地址 |
p1 = &a; |
p1 指向 a[0] 地址 |
p1 = &a[0]; |
p1 指向 a[0] 地址 |
p2 = &a[5]; |
p2 指向 a[5] 地址 |
*p1 = 0; |
0 赋值给 p1 指向的内容即 a[0] |
*p2 = 5; |
5 赋值给 p2 指向的内容即 a[5] |
p1 + p2 |
错误写法,指针不可相加 |
p1 - p2 |
p1 和 p2 之间相差了多少个 int 地址单元,值为 5(1 个 int 单元是 4 字节长度) |
作函数形参
语句/表达式 |
含义 |
void fun (int a, int b[10]) |
形参 b 显式给出数组长度,必须将长度为 10 的整型数组作为实参 |
void fun (int a, int b[]) |
整型指针 b,任意整型变量都可作为实参 |
void fun (int a, int *b) |
整型指针 b,任意整型变量都可作为实参 |
3. 二维数组
定义声明
声明 |
类型 |
含义 |
int a[4][10] |
int [4][10] 或int (*)[10] |
一个 4 行 10 列的二维数组,注意类型不是二级指针 |
int a[][10] = {0,1,2,3,4,5,6,7,8,9,10,11} |
int [][10] |
当没有定义行时,必须至少赋值一个数据,这是一个 2 行 10 列的二维数组 |
提示:将int a[4][10]
视为一个存放了 4 个指针的数组,这些指针都指向有 10 个元素的不同数组。
下标 |
内容 |
(*a)[0] |
指向 10 个元素的数组的指针 |
(*a)[1] |
指向 10 个元素的数组的指针 |
(*a)[2] |
指向 10 个元素的数组的指针 |
(*a)[3] |
指向 10 个元素的数组的指针 |
申请与释放空间
方法一:内存可能不连续
// 申请空间
int **p = (int **) malloc(4 * sizeof(int *)); // 动态申请 4 行二维数组
for (int i = 0; i < 4; i++){ // 每行申请 10 个长度的数组
p[i] = (int *) malloc(10 * sizeof(int));
}
// 释放空间
for (int i = 0; i < 4; i++){
free(p[i]);
}
free(p);
方法二:内存连续
// 申请空间
int **p = (int **) malloc(4 * sizeof(int*)); // 动态申请 4 行二维数组
p[0] = (int *) malloc(4 * 10 * sizeof(int)); // 每行申请 10 个长度的数组
// 释放空间
free(p);
方法一:内存可能不连续
// 申请空间
int **p = new int*[4]; // 动态申请 4 行二维数组
for (int i = 0; i < 4; i++){ // 每行申请 10 个长度的数组
p[i] = new int[10];
}
// 释放空间
for (int i = 0; i < 4; i++){
delete []p[i];
}
delete []p;
方法二:内存连续
// 申请空间
int **p = new int*[4]; // 动态申请 4 行二维数组
p[0] = new int[4 * 10]; // 每行申请 10 个长度的数组
// 释放空间
delete []p;
取地址(&)和解引用(*)
语句/表达式 |
含义 |
a |
a[0][0] 地址,或 0 行起始地址 |
*a |
a[0][0] 地址,或 0 行起始地址 |
*(a + 0) |
a[0][0] 地址,或 0 行起始地址 |
a[0] |
a[0][0] 地址,或 0 行起始地址 |
&a |
取得 a[0][0] 地址,或 0 行起始地址 |
&a[0] |
取得 a[0][0] 地址,或 0 行起始地址 |
&a[0][0] |
取得 a[0][0] 地址 |
a[0][0] |
a[0][0] 的值 |
*a[0] |
a[0][0] 的值 |
*(*(a + 0) + 0) |
a[0][0] 的值 |
**a |
a[0][0] 的值 |
语句/表达式 |
含义 |
a + 3 |
a[3][0] 地址,或 3 行起始地址 |
*(a + 3) |
a[3][0] 地址,或 3 行起始地址 |
a[3] |
a[3][0] 地址,或 3 行起始地址 |
&a[3] |
取得 a[3][0] 地址,或 3 行起始地址 |
&a[3][0] |
取得 a[3][0] 地址 |
a[3][0] |
a[3][0] 的值 |
*a[3] |
a[3][0] 的值 |
*(*(a + 3) + 0) |
a[3][0] 的值 |
**(a + 3) |
a[3][0] 的值 |
语句/表达式 |
含义 |
*(a + 3) + 2 |
a[3][2] 地址 |
a[3] + 2 |
a[3][2] 地址 |
&a[3][2] |
取得 a[3][2] 地址 |
a[3][2] |
a[3][2] 的值 |
*(a[3] + 2) |
a[3][2] 的值 |
*(*(a + 3) + 2) |
a[3][2] 的值 |
提示:*
表示解引用一次,得到的只是地址;**
表示解引用两次,得到的是值。
作函数形参
语句/表达式 |
含义 |
void fun (int a, int b[][]) |
错误写法,二级指针不等同于二维数组 |
void fun (int a, int **b) |
错误写法,二级指针不等同于二维数组 |
void fun (int a, int b[4][10]) |
形参 b 显式给出数组的行列大小,必须将形如[4][10]的整型数组作为实参 |
void fun (int a, int b[][10]) |
形参 b 必须至少显式给出数组的列大小,形如[x][10]的整型数组可以作为实参 |
void fun (int a, int (*b)[10]) |
形参 b 必须至少显式给出数组的列大小,形如[x][10]的整型数组可以作为实参 |
void fun (int a, int *b) |
可以将二维数组 T 作为 b 的实参,但在函数内部只能使用一维数组的方式去访问 T |
4. 函数指针
定义声明
声明 |
类型 |
含义 |
int f() |
int () |
返回值为整型的函数 |
int *f() |
int *() |
(指针函数)返回值为整型指针的函数 |
int (*f)() |
int (*)() |
(函数指针)指向函数的指针,该函数的返回值为整型 |
int *(*f)() |
int *(*)() |
指向函数的指针,该函数的返回值为整型指针 |
int (*f[10])() |
int (*[10])() |
(数组指针)有 10 个函数指针的数组,这些函数的返回值均为整型 |
int *(*f[10])() |
int *(*[10])() |
(数组指针)有 10 个函数指针的数组,这些函数的返回值均为整型指针 |
int *(*(*f[10]))() |
int *(*(*[10]))() |
指向一个有 10 个函数指针的数组的指针,这些函数的返回值均为整型指针 |
int (*f)(int, int) |
int (*)(int, int) |
指向函数的指针,该函数有两个整型参数,其返回值为整型 |
int (*f)(int*, int*) |
int (*)(int*, int*) |
指向函数的指针,该函数有两个整型指针参数,其返回值为整型 |
int *(*f)(int*, int*) |
int *(*)(int*, int*) |
指向函数的指针,该函数有两个整型指针参数,其返回值为整型指针 |
提示:如何记忆指针函数和函数指针的区别?
- X 型函数(函数返回的是 X 类型的数据):整型函数、浮点型函数、字符型函数组、指针函数
- X 型指针(指针指向 X 类型的数据):整型指针、浮点型指针、字符型指针、数组指针、函数指针
取地址(&)和解引用(*)
(已定义函数int f(int a, int *b)
,函数指针int (*p1)(int, int*)
,函数指针void (*p2)(int, int*)
,变量int x, y
)
语句/表达式 |
含义 |
f |
函数 f 的地址 |
&f |
函数 f 的地址 |
*f |
函数 f 的地址 |
p1 = f; |
f 的地址赋值给 p1 |
p1 = &f; |
f 的地址赋值给 p1 |
p1 = *f; |
f 的地址赋值给 p1 |
(*p1)(x, &y); |
执行 p1 指向的函数 f |
(void (*)(int, int*))f |
对函数 f 进行类型强制转换(int -> void) |
p2 = (void (*)(int, int*))f; |
f 的地址赋值给 p2 |
p2 = (void (*)(int, int*))&f; |
f 的地址赋值给 p2 |
p2 = (void (*)(int, int*))*f; |
f 的地址赋值给 p2 |
(*p2)(x, &y); |
执行 p2 指向的函数 f |
注:关于不同参数列表的函数指针类型强制转换,请参考关于函数指针类型强制转换的一些摸索。
作函数形参(回调函数)
语句/表达式 |
含义 |
int fun (int a, int b, int (*callback)(int, int*)) |
最后一个参数是函数指针,它的返回值为 int,有两个参数 |
typedef int (*callback)(int, int*); |
定义一个函数指针 |
int fun (int a, int b, callback p) |
最后一个参数是函数指针 |