指针
变量的地址就叫做指针
指针变量
指针变量也是一个变量,存储地址的变量,可以间接的访问指向的变量.
- 语法
定义,赋值
数据类型* 指针表变量的名称;
int* p1;
//类型是 int* 读作 int 指针.
double* p2;
float* p3;
char* p4;
1. 只能存储对应类型变量的地址
2. 不能赋一个非地址类的数据
int* p1 = #
//正确的赋值方式
取地址
占位符是%p
printf("%p",&p1)
//p1这个变量的地址
printf("%p",p1)
//p1变量的值,就是*p1指向的变量的地址
1.p1 操作的是 p1这个指针变量.
2.可以取P1的值,也可以为 p1赋值
3.&p1拿到的是 p1的值
操作变量
*p1代表p1指针指向的变量
*p1完全等价于 num
*p1 = 100;
//将100赋值给 num
printf("%d",*p1)
//输出 num 的值
X 错误的
p1 = 200;
注意的问题
1.批量声明
int* p1,p2,p3;
//这样批量声明p2,p3都是 int 类型
//指针类型是*变量名
2.野指针
int* p1;
//指向一块随机的空间,操作起来会出现各种错误
//访问的时候,如果随机空间没人用就不会报错,如果有人用就会BAD_ACCESS
//如果赋值就相当危险
NULL 值
如果没有变量的地址给这个指针变量,就初始化一个 NULL,代表指针变量不指向内存中的任何地址.
- NULL == 0;
所以也可以直接复制指针变量一个0;
- 0代表不指向任何空间
- 如果访问一个 NULL 值的指针100%报错.
多个指针指向同一个变量
int num = 100;
int* p1 = #
int* p2 = p1;
//把p1的值赋给了 p2
*p2 也就指向了 num
*p2 = 100;
函数传递数组
void test2(int arr[],int len)
//int arr[] 不是传递的这个数组,而是这个数组的地址
指针作为函数参数
void test3(int *p1)
{
*p1 = 1000;
//函数内部修改变量值
}
//声明
test(&num);
//传递对应类型变量的地址
执行完函数,num 的值为1000;
- 有什么作用
在函数内部修改实参的值,函数只能又一个返回值,当需要返回多个值时就可以用指针修改.
int getMaxAndMin(int arr[],int len,int* min);
//声明函数
int max = getMaxAndMin(arr,sizeof(arr)/sizeof(arr[0]),&min);
//调用函数
void getZuiDaHeZuiXiao (int arr[],int len,int* pMax,int* pMin);
//声明无返回值函数
getZuiDaHeZuiXiao(arr,sizeof(arr)/sizeof(arr[0]),&max,&min);
//调用无返回值函数,传递两个变量的地址
- 指针为什么分类型
- 变量在内存中不同类型占用不同的字节数指针是最低字节地址
- 根据指针类型,确定指向的变量在内存中占用多少字节
> 3. 占几个字节连续操作几个字节找到整个变量.从而正确有效的操作访问和修改变量
多级指针
- 一级指针中存储的是1个普通变量的地址.
- 二级指针中存储的是1个一级指针变量的地址.
三级指针中存储的是1个二级指针变量的地址
- 语法
一级指针: 数据类型* 指针名;
二级指针: 数据类型** 指针名;
int** p2 = p1;
//这样是错误的,把普通变量的地址赋给了二级指针.
int** p2 = &p1;
//这样就把一级指针的地址赋给了二级指针
//他就指向了一级指针地址中存储的普通变量的地址,
//就指向了普通变量&num == p1
*p1 == num
&p1 == p2
**p2 == num
*p2 == p1
&p2 == p3- 操作
int num = 10;
int* p1 = #
int** p2 = &p1;*p1 = 200;
//代表 p1指针改变了 num 的值为200.
(num == 200);int age =110;
*p2 =&age;
//代表p2这个指针指向的变量,也就是 p1
//这句话执行完毕,p1就指向 age了
(*p1 == age)
同时
(**p2 == age)
指针与整数的加减运算
指针与整数加减运算,是在指针地址的基础之上加1个单位变量占用的字节数.
int* p1 = #
int* p2 = p1+1;
//如果 &num 的地址是0x11901,p2的值就是0x11905
//不能写成*p1 + 1,这样写就是 num+1了.
//*(p1+1)才是正确的表达式
指针与数组
int arr[] = {10,20,30,40,50,60,70};
int *p1 = &arr[0];
//表示p1指针指向了数组中的第0个元素.
int* p2 = arr;
//这么做完全也是没有问题,因为arr代表数组的地址.也就是数组的第0个元素的地址.
使用指针来遍历数组.
第一种方式
int arr[7] = {10,20,30,40,50,60,70};
int* p1 = arr;
//p1指针指向了数组的第0个元素.
for(int i = 0; i < 7; i++)
{
printf(“%d\n”,*(p1+i));
}第二种方式
int arr[7] = {10,20,30,40,50,60,70};
for(int i = 0; i < 7; i++) { printf("%d\n",*(arr+i)); }
//arr本身就是一个指针地址.
第三种方式
int arr[7] = {10,20,30,40,50,60,70};
int* p1 = arr;for(int i = 0; i < 7; i++) { printf("%d\n",*(p1++)); } *p1 //. 代表p1指针指向的变量. 就是数组中第0个元素. // *(p1+1);//p1+1 的结果是第1个元素的地址 *(p1+1) 代表数组中第1个元素. // *(p1+2);//p1+2 的结果是第2个元素的地址. *(p1+2) 代表数组中第2个元素. **注意的地方** 每次循环.p1的值都会变化. 最后1次执行完毕之后. p1指针指向外面去了. p1就不再执行数组中的任何元素了.
注意: 数组名代表数组的地址.而数组一旦创建.数组的地址就确定了.不能改变
所以.我们不能为数组名赋值. 不能修改数组名的值. 但是可以使用数组名的值.
结论: 无法修改数组名的值. 不能为数组名赋值.任何值都不可以.
WHY? 因为数组名代表数组的地址. 而数组一旦创建数组的地址是无法改变的. 所以你不能给他赋值.
数组名是1个常量.1个地址常量
当数组作为函数的参数的时候
void test(int arr[],int len);
//系统在编译的时候已经把数组换成了指针
void test(int *arr,int len)
//在声明参数的时候,不是创建数组,而是创建一个存储数组地址的指针变量
所以 sizeof计算函数参数的长度时永远都是8.
函数如果带了1个参数 这个参数是1个数组.
直接写1个指向数组的第0个元素的指针,=再传入长度
索引的本质
指针变量后面可以使用中括弧,在中括弧中写上下标来访问数据.
p1[n]; 前提是p1是1个指针变量.
完全等价于 *(p1+n);
int num1 = 100;
int num2 = 200;
int* p1 = &num2;
p1[0] == (p1+0) == num2;
p1[1] == (p1+1) == num1;
-拿数组举例*arr == arr[0];
*(arr+1) == arr[1];
1.只要是指针都可以使用中括弧下标.就相当于是去访问指针指向的变量.
2.操作数组我们虽然使用的中括弧下标来操作,实际上内部仍然使用的指针来操作.
存储指针的数组
声明
元素类型 数组名[数组长度];
int* arr[3];
类是是 int* 长度为3
int* arr[3] = {p1,p2,p3};
int* arr[3] = {&num1,&num2,&num3};int arr[3] = {10.20,30};
int* pArr[3] = {arr,&arr[1],&arr[2]};
*(pArr[0]) = 100;
//pArr 数组第0个元素是 arr 数组第0个元素的指针,赋值100,arr[0]==100
指针之间的减法
long res = &arr[7] - &arr[1] == 6;
指针指向的同一个数组内的两个变量之间相差几个元素
int num1 = 0;
double num2 = 0.0;
double num3 = 0.0;
int num4 =0;
int* p1 =&num1;
int* p2 =&num4;
long res = p2-p1 == 5;
double+double+int = 20个字节.
//一般只用在同一个数组内,不然结果没有意义.
指针之间的比较运算
- 比较运算符都可以用在两个指针之间
- 变量分配字节空间的时候,从高往低分配.
- 比较运算符可以判断两个指针指向的地址谁在高字节谁在低字节. >,>=,<,<=
- 也可以用 ==、!=来判断两个指针指向的地址是不是同一个地址.
int* p1 = #
int* p2 = p1;
这个时候 p1,p2都指向同一个变量.