概念
pointer(指针)是一个变量类型, 用来存储另一个变量的内存地址
c/c++ 中, 每个变量在内存中都有一个唯一的地址, 可以用指针来表示
指针变量是指针的具体实现, 它本身也是一个变量, 专门用来保存其他变量的内存地址
通过指针, 可间接访问或修改指针所指向变量的值, 而不需要直接使用原变量名
操作
定义指针
int *p; // 指向 int 类型的指针
char *p; // 指向 char 类型的指针
double *p; // 指向 double 类型的指针
指针变量的值是所存储变量在内存中的地址
获取地址(取地址)
使用&运算符可以获取变量地址, 并将地址赋值给指针变量完成初始化
#include <stdio.h>
int main() {
int a = 0xFF;
int *p = &a;
printf("a address = %p\n", &a);
printf("p value = %p\n", p);
return 0;
}
运行结果
a address = 0x7ffa2ed1128
p value = 0x7ffa2ed1128
图示
graph LR;
subgraph a[int a]
V2[value: 255]
A2[address: 0x7ffa2ed1128]
A2-->V2
end
subgraph p[int* p]
V1[value: 0x7ffa2ed1128]
end
V1-->A2
解引用(访问指针指向的值)
使用 * 运算符可以访问指针指向的变量值
#include <stdio.h>
int main() {
int a = 0xFF;
int *p = &a;
printf("a value: %d\na address: %p\n", a, &a);
printf("p value: %p\n*p value: %d\n", p, *p);
return 0;
}
运行结果
a value = 255
a address = 0x7ffc241ed608
p value = 0x7ffc241ed608
*p value = 255
图示
graph LR;
subgraph a[int a]
V2[value: 255]
A2[address: 0x7ffc241ed608]
A2-->V2
end
subgraph p[int* p]
V1[value: 0x7ffc241ed608]
end
V1--"*解引用"-->V2
指针属性
指针指向(指针值)
指针的值就是它所存储变量的地址, 即指针指向该变量
#include <stdio.h>
int main() {
int a = 10;
int *p = &a;
printf("a = %d, &a = %p\n", a, &a);
printf("*p = %d, p = %p\n", *p, p);
return 0;
}
运行结果
a = 10, &a = 0x7ffff423e928
*p = 10, p = 0x7ffff423e928
图示
graph LR;
subgraph a[int a]
V2[value: 10]
A2[address: 0x7ffff423e928]
A2-->V2
end
subgraph p[int* p]
V1[value: 0x7ffff423e928]
end
V1-->A2
改变指针指向
通过给指针变量赋新地址, 可以改变它的指向
#include <stdio.h>
int main() {
int a = 0xFF;
int b = 0xAA;
int* p = &a;
printf("p = %p, *p = %d\n", p, *p);
p = &b; // 改变指针指向
printf("p = %p, *p = %d\n", p, *p);
return 0;
}
运行结果
p = 0x7fff62e598a8, *p = 255
p = 0x7fff62e5989c, *p = 170
图示
graph LR;
subgraph S1[int a]
direction LR
V2(value: 0xFF)
A2(address: 0x7fff62e598a8)
end
subgraph S2[int* p]
V1(value: 0x7fff62e598a8)
end
V1--->A2
graph LR;
subgraph S3[int b]
direction LR
V3(value: 0xAA)
A3(address: 0x7fff62e5989c)
end
subgraph S2[int* p]
V1(value: 0x7fff62e5989c)
end
V1-->A3
间接修改变量值
通过指针指向的地址可以间接修改变量值
#include <stdio.h>
int main() {
int a = 0xFF;
int* p = &a;
printf("a = %d, &a = %p\n",a, &a);
printf("p = %p, *p = %d\n", p, *p);
*p = 0xAA;
printf("p = %p, *p = %d\n", p, *p);
printf("a = %d, &a = %p\n",a, &a);
return 0;
}
运行结果
a = 255, &a = 0x7ffc9fcdabf8
p = 0x7ffc9fcdabf8, *p = 255
p = 0x7ffc9fcdabf8, *p = 170
a = 170, &a = 0x7ffc9fcdabf8
图示
graph LR;
subgraph S1[int a]
direction LR
V2(value: 0xFF)
A2(address: 0x7ffc9fcdabf8)
end
subgraph S2[int* p]
V1(value: 0x7ffc9fcdabf8)
end
V1-->A2
graph LR;
subgraph S1[int a]
direction LR;
V2(value: 0xAA)
A2(address: 0x7ffc9fcdabf8)
end
subgraph S2[int* p]
V1(value: 0x7ffc9fcdabf8)
end
V1-->A2==>V2
类型
指针类型
指针变量去掉变量名后是指针类型
| 指针变量 | 指针类型 |
|---|---|
| int *p1 | int * |
| char *p2 | char * |
| double **p3 | double ** |
指针变量大小只与系统位数有关, 与类型无关, 因为不论何类型指针都是存储地址值
32位系统指针大小为4字节, 64位系统指针大小位8字节
#include <stdio.h>
int main() {
int* p = NULL;
double* p1 = NULL;
printf("sizeof p = %d\nsizeof p1 = %d\n", sizeof(p), sizeof(p1));
return 0;
}
指针指向类型
指针变量去掉* 变量名表示指向类型
| 指针变量 | 指针指向类型 |
|---|---|
| int *p1 | int |
| char *p2 | char |
| double **p3 | double * |
指向区域大小
// 指针指向4字节大小区域
int a = 3;
int *p = &a;
// 指针指向1024 字节 大小区域
const int SIZE = 1024;
char *p = (char *)malloc(sizeof(char) * SIZE);
特殊指针
常量指针
常量指针是指针
其指向常量(存储常量地址), 指向可变(可存储其他变量), 指向值不可变(不能通过地址间接修改变量值)
const int *p
或
int const *p
指针类型 int *, 指针指向类型 const int
graph LR;
subgraph S1[const int a]
direction LR
V[value: 不可变]
A[address]
end
subgraph S2[const int* p]
V2[value]
direction LR
end
S2-->A==>V
- 示例, 尝试改变常量指针指向和指向值
#include <stdio.h>
int main(void) {
const int a = -1;
const int b = 1;
const int *p = &a;
printf("%d\n", *p);
// 尝试改变指针指向
p = &b;
printf("%d\n", *p);
// 尝试改变指针指向值, 报错
*p = -1;
printf("%d\n", *p);
return 0;
}
运行时报错
error: read-only variable is not assignable
指针常量
指针常量是常量
其本身是常量(指针是常量类型), 指向不可变(不能再存储其他变量地址), 指向值可变(可通过地址间接修改变量值)
int *const p;
指针类型 int *const, 指针指向类型 int
graph LR;
subgraph S1[int a]
V[value]
A[address]
end
subgraph S2[int *const p]
direction LR
V2[value: 不可变]
end
S2==绑定==>A
- 示例, 修改指针常量
#include <stdio.h>
int main(void) {
int a = -1;
int *const p = &a;
printf("%d\n", *p);
// 尝试改变指针指向值
*p = 1;
printf("%d\n", *p);
int b = 1;
// 尝试改变指针指向, 报错
p = &b;
printf("%d\n", *p);
return 0;
}
运行时报错
error: cannot assign to variable 'p' with const-quakufued type 'int *const'
note: variable 'p' declared const here
函数指针
其指向一个函数, 即存储函数的地址, 通过函数指针可间接调用函数
返回值 (*)(参数...,)
- 示例, 通过指针间接调用函数
#include <stdio.h>
int get_max(int x, int y) {
return x > y ? x : y;
}
int main() {
int (*p)(int, int) = NULL;
p = get_max;
printf("%d\n", p(1, 2));
return 0;
}
指针数组
指针数组是数组, 数组中元素为指针
int *p[3];
指针类型 int *, 指针指向类型 int
- 示例, 指针数组使用
#include <stdio.h>
int main(void) {
int *p[3];
int a = 1;
int b = 2;
int c = 3;
p[0] = &a;
p[1] = &b;
p[2] = &c;
for (int i = 0; i < 3; i++) {
printf("p[%d] = %d, &p[%d] = %p\n", i, *p[i], i, &p[i]);
}
return 0;
}
运行结果
p[0] = 1, &p[0] = 0x7fffa48dbfd0
p[1] = 2, &p[1] = 0x7fffa48dbfd8
p[2] = 3, &p[2] = 0x7fffa48dbfe0
对象指针
指针指向一个对象(存储对象地址)
二级指针
二级指针指向指针, 其值是另一个指针的地址
int **a;
- 示例, 二级指针使用
#include <stdio.h>
int main() {
int a = 0xFF;
int *p = &a;
int **sp = &p;
printf("a: %d\n, &a: %p\n\n", a, &a);
printf("*p: %d\n, p: %p\n, &p: %p\n\n", *p, p, &p);
printf("**sp: %d\n, *sp: %p\n, sp: %p\n", **sp, *sp, sp);
return 0;
}
运行结果
a: 255
&a: 0x7ffd79d476d8
*p: 255
p: 0x7ffd79d476d8
&p: 0x7ffd79d476d0
**sp: 255
*sp: 0x7ffd79d476d8
sp: 0x7ffd79d476d0
图示
graph LR;
subgraph S1[int a]
direction LR
V1(value: 0xFF)
A1(address: 0x7ffd79d476d8)
end
subgraph S2[int* p]
direction LR
V2(value: 0x7ffd79d476d8)
A2(address: 0x7ffd79d476d0)
end
subgraph S3[int** sp]
direction LR
V3(value: 0x7ffd79d476d0)
end
V3-->A2
V2-->A1
数组指针
数组指针是指针, 指针指向数组(存储数组首元素地址)
int (*p)[3];
指针类型 int *, 指针指向类型 int [3]
- 示例, 数组指针使用
#include <stdio.h>
int main(void) {
int a[3] = {1, 2, 3};
int (*p)[3] = &a;
for (int i = 0; i < 3; i++) {
printf("&a[%d] = %p, a[%d] = %d\n", i, &a[i], i, a[i]);
}
for (int i = 0; i < 3; i++) {
printf("(*p + %d) = %p, *(*p + %d) = %d\n", i, (*p + i), i,*(*p + i));
}
return 0;
}
运行结果
&a[0] = 0x7ffdeaf17b0, a[0] = 1
&a[1] = 0x7ffdeaf17b4, a[1] = 2
&a[2] = 0x7ffdeaf17b8, a[2] = 3
(*p + 0) = 0x7ffdeaf17b0, *(*p + 0) = 1
(*p + 1) = 0x7ffdeaf17b4, *(*p + 1) = 2
(*p + 2) = 0x7ffdeaf17b8, *(*p + 2) = 3
图示
graph LR;
subgraph S1["int a[]"]
subgraph A1[a0]
V0["value"]
A0["address"]
end
subgraph A2[a1]
V12["value"]
A12["address"]
end
A0-->A12
end
subgraph S2["int (*p)[]"]
V2[value]
direction LR
end
S2==>A0