1、概念
- 指针就是地址,地址就是指针
- 什么是地址?
- 在内存中,由多个内存单元构成,每个内存单元占8位(bit) —> 1字节,内存中以字节为单位;
每一个字节都有相应的编号,这个编号就叫做地址。
- 地址具有唯一性。
- 32位机
- 代表寻址能力
最小地址编号 0000 0000 0000 0000 0000 0000 0000 0000 —>0x00000000
最大地址编号 1111 1111 1111 1111 1111 1111 1111 1111 —>0xffffffff
- 指针是 指针变量、地址、指针 的统称
- 指针变量:用来存储不同地址常量【用来存储地址】
2、指针变量
(1)指针变量的定义
<存储类型><数据类型> * <变量名>; // * 用在定义变量时,表示该变量是一个指针变量
int *p; // 表示定义一个整型的指针变量 p
(2)指针变量的初始化
char *q = NULL //指针q 指向零地址空间,避免成为野指针
【NULL 代表零地址空间,该空间既不允许读 也不允许写】
【野指针是随机的、不正确的、没有明确限制的】
【野指针不能被赋值,但可以给予指向】
(3)直接访问与间接访问
直接访问:直接通过变量的地址访问
间接访问:通过存储该变量的地址的指针变量去访问
(4)指针的运算
1)&:
取地址,取变量的地址
* :
1.乘 运算符
2.定义指针
3.取内容,取指针指向变量的内容
【 & 与 * 互为逆运算 】
2)指针的偏移
p + 1 :以p所指向的地址为基准,向高地址偏移1个数据类型(指针所指向的数据类型)的大小
int a=10;int *p=&a;p+1往高地址偏移4个字节
p - 1 :以p所指向的地址为基准,向低地址偏移1个数据类型(指针所指向的数据类型)的大小
p + n :以p所指向的地址为基准,向高地址偏移n个数据类型(指针所指向的数据类型)的大小
p - n :以p所指向的地址为基准,向低地址偏移n个数据类型(指针所指向的数据类型)的大小
3)++ / --
单独使用
p++:以p所指向的地址为基准,向高地址偏移1个数据类型(指针所指向的数据类型)的大小
p - -:以p所指向的地址为基准,向低地址偏移1个数据类型(指针所指向的数据类型)的大小
++p:以p所指向的地址为基准,向高地址偏移1个数据类型(指针所指向的数据类型)的大小
- - p:以p所指向的地址为基准,向低地址偏移1个数据类型(指针所指向的数据类型)的大小
结合使用
*p++: p先与++结合,此时p的指向要变,输出不变
(*p)++: p先与 * 结合,此时p的指向不变,输出不变
*++p: p先与++结合,此时p的指向要变,输出要变
*(++p): 和 *++p相同
4)-
p - q:必须要数据类型相同,输出相差的元素个数
(5)指针的数据类型
指针的数据类型与其所指向的数据类型相同
(6)指针的字节大小
只有主机的位数有关
64位 指针的空间大小位8个字节
// 指针表示地址,地址位数 = 主机位数 / 8;
3、指针与一维数组
int a[5] = {10,20,30,40,50};
一维数组 数组名:
- 代表整个一位数组空间
- 代表首元素地址
[ ]:变址运算符(偏移并引用)
int a[5] = {10,20,30,40,50};
a[0] <==> *(a + 0);
int *p = a;
*(a + 0) <==> *(p + 0);
a[0] <==> *(a + 0) <==> *(p + 0);
4、指针与字符串数组
char s[ ] = "hello";
char *p = s;
puts ("hello");
// 定义一个字符指针q,指向指向字符串常量的首元素;
char *q = "hello";
puts (q) ;
printf ("%p\n", s);
printf ("%p\n", q);
// q指向字符串常量“hello”的首地址,不能修改常量的值;
*q = 'A';
5、多级指针——指向指针的指针
用来保存指针变量的地址,用 ** 来定义变量
(1)二级指针
<存储类型> <数据类型> **<变量名>;
eg:
int a = 10;
int *p = &a;
int **pp = &p;
printf(“%d”,**pp); // 输出:10
6、指针与二维数组
int a[2][3];
int *p = &a[0][0];
7、数组指针
int a[ ] = {1,2,3};
int (*p)[3] = &a; // 定义一个行指针变量 p
分析:
a = &a;值相同,但引用的内存空间不同!!!
a 表示数组首元素的地址;
&a 表示整个数组的地址;地址值 = 数组首元素的地址值;
a + 1 偏移4字节;
&a + 1 偏移12字节(元素个数 * 4); 【16进制,一个字符4字节】
*&a = a; // * 与 & 互为逆运算,【记忆方法:&升维(增加引用空间,* 降维(降低引用空间】
*(*&a + 1)= 2 // 2为a[2]的值;
*&a + 1; 偏倚4字节
*(*&a + 1); 先偏移4字节,再取当前地址空间的内容
(1)定义数组指针
<存储类型> <数据类型> (*<指针变量名>)[元素个数];
元素个数:一次能引用的元素个数【一般写列的个数】
int (*p)[3];
eg:
int a[2][3] = {0};
int (*p)[3] = &a; // 定义后,p表示数组a 的首行地址,引用空间为12字节
二维数组的数组名代表首行的地址
8、指针数组
存储大量同类型的指针变量
(1)一般形式
<存储类型> <数据类型> * 数组名[数组元素个数]
int *p[3]; // 定义一个指针数组,该数组存储int *类型的指针
(2)main函数参数
argc:代表命令行参数个数
char *argv[]: 指针数组,用来保存命令行参数中的字符串
9、特殊指针
(1)万能指针——能访问任意类型的指针
int a=10,b=20,c=30;
void *p = &a;
【万能指针不能直接接引用,取内容前必须强转为某一数据类型】
printf(“%d”,*(int *)p);
(2)const 修饰指针
1)const int *p = &a;
*p = 100; // 错误
p = &b;
【可以修改指针的指向,但不能修改指针指向空间的内容】
2)int * count p = &a;
*p = 100;
p = &b; // 错误
【不能修改指针的指向,可以修改指针指向空间的内容】
3)count int * count p = &a;
*p = 100; // 错误
p = &b; // 错误
【既不能修改指向,也不能修改指针执行空间的内容】