文章目录
1️⃣指针概念
1.指针就是个变量,用来存放地址,地址唯一标识的一块内存空间。
2.指针的大小是固定的4/8个字节(32位平台/64平台)。
3.指针是有类型的,指针的类型决定了指针的+ - 整数的范围和指针解引用操作的时候的权限。
4.指针的运算。
1.字符指针
在指针的类型中有一种指针类型为字符指针 char ;*
一般使用:
#include<stdio.h>intmain(){char ch ='w';char* pc =&ch;//取ch地址放入pc中*pc ='c';//通过pc解引用把ch存入的数据改成'c'return0;}
还有一种使用方法:
intmain(){constchar* pstr ="hello bit.";//这里是把一个字符串放到pstr指针变量里了吗?printf("%s\n", pstr);//显然不是!输出 hreturn0;}
** 代码:
const char* pstr = "hello bit."
特别容易认为是把字符串
hello bit
放到字符指针
pstr
里了,但本质上是把字符串
hello bit.
首字符的地址放到了
pstr
中。**
上面代码的意思是把一个常量字符串的首字符
h
的地址存放到指针变量
pstr
中。
🆚于是有了这样的面试题:
intmain(){char str1[]="hello bit.";char str2[]="hello bit.";constchar* str3 ="hello bit.";constchar* str4 ="hello bit.";if(str1 == str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n");if(str3 == str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return0;}
这里最终输出的结果是:
这里的
str3
和
str4
指向的是同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当几个指针,去指向同一个字符串的时候,它们实际会指向同一块内存,但是用相同的常量字符串去初始化不同的数组的时候,不同数组会开辟不同的内存块,所以
str1
和
str2
不同,
str3
和
str3
相同。
2.🅰️指针数组
**
指针数组是一个存放指针的数组
**
int* arr1[10];//整形指针的数组char* arr2[4];//一级字符指针数组char** arr3[4];//二级字符指针数组
举例:
int a =1;int* pa =&a;int* arr1[3]={pa};//arr1[0]访问pa的地址
-* ——————————————————*
3.🅱️数组指针
3.1数组指针的定义
**
数组指针是指针
**
整形指针int* pa
能够指向整形数据的指针,那数组指针应该是:能够指向数组的指针。
int(*p)[10];
**解释:
p
先和
*
结合,说明
p
是一个指针变量,然后指针指向的是一个大小为10个整形的数组。所以
p
是一个指针,指向一个数组,叫数组指针。
这里要注意:[]
的优先级要高于
*
号的,所以必须加上
()
来保证
p
先和
*
结合。**
3.2 &数组名 🆚 数组名
对于下面的数组:
int arr[10];
arr
是数组名,数组名表示数组首元素的地址。
那
&arr
是什么呢?我们看一段代码:
#include<stdio.h>intmain(){int arr[10]={0};printf("%p\n", arr);printf("%p\n",&arr);return0;}
由此可见数组名 和 &数组名打印的地址是一样的。
难道两个真的一样吗?
再来看一段代码:
#include<stdio.h>intmain(){int arr[10]={0};printf("arr = %p\n", arr);printf("&arr= %p\n",&arr);printf("arr+1 = %p\n", arr+1);printf("&arr+1= %p\n",&arr+1);return0;}
根据上面的运行结果我们发现:其实数组名 和 &数组名,虽然值是一样的,但是意义应该不一样。
实际上:
&arr
表示的是整个数组的地址,而不是数组首元素的地址。(细品一下)
本例中&arr
的类型是:
int(*)[10]
,是一种数组指针类型。
数组的地址+1,跳过整个数组大小,所以&arr+1
相当于
&arr
的差值是40。
**为什么
&arr+1
跳过40个字节?
因为:arr
的类型是
int [10]
10个
int
相当于40个字节。**
数组的类型
决定了+1/ -1 跳过几个字节。
3.3数组指针的使用
#include<stdio.h>intmain(){int arr[10]={1,2,3,4,5,6,7,8,9,0};int(*p)[10]=&arr;//把数组arr的地址赋值给数组指针变量p//但是我们一般很少这样写代码return0;}
一个数组指针的使用:一般使用在二维数组上
voidprint1(int(*pa)[5],int r,int c)//int (*pa)[5]一维数组指针接收{int i =0;for(i =0; i < r; i++){//pa+0指向第1行, + 1指向第2行...//+0找到第一个1维数组,+1找到第2个一维数组int j =0;for(j =0; j < c; j++){//printf("%d ", pa[i][j]);//等同于下列printf("%d ",*(*(pa+i)+j));// (pa+i)——>&arr+i找到第i行地址//*(pa+i)——>arr[i] //解引用找第i行数组名,相当于首元素地址//*(*(pa+i)+j)——>arr[i][j]//再解引用找到第i行第j个元素}printf("\n");}}intmain(){int arr[3][5]={1,2,3,4,5,2,3,4,5,6,3,4,5,6,7};print1(arr,3,5);//二维数组的首元素地址是第一行//所以这里传参的是//相当于传了一个一维数组的地址return0;}
**你要知道:
int (*p) [5]
p
的类型是:
int(*)[5]
,指向一个整形数组,数组有5个元素
p+1
跳过5 个
int
元素的数组**
学了指针数组和数组指针我们来一起回顾并看看下面代码的意思:
int arr[5];//arr是一个数组,数组有5个元素,每个元素是int型int*parr1[10];//parr是数组,数组有10个元素,每个元素是int*型int(*parr2)[10];//parr2是一个指针,指向一个有10个元素的数组,每个元素是int型,所以parr2是数组指针int(*parr3[10])[5];//parr3是一个数组,数组有10个元素,每个元素是int(*)[5],//该指针指向一个含有10个元素的数组,所以parr3是一个数组指针数组
**对
int (*parr3[10])[5];
不理解,可以看下列补充:**
4.数组参数、指针参数
在写代码的时候难免要把【数组】或者【指针】传给函数,那函数的参数该如何设计呢?
4.1一堆数组传参
#include<stdio.h>voidtest(int arr[])//ok,数组大小可以不写{}voidtest(int arr[10])//ok{}voidtest(int*arr)//ok,整形地址放入整形指针{}voidtest2(int*arr[20])//ok,数组传参,形参可以数组也可以指针接收,20也可以省略{}voidtest2(int**arr)//ok,二级指针接收一级指针地址//数组名相当于首元素地址,arr2是int*类型的地址{}intmain(){int arr[10]={0};int*arr2[20]={0};//20个元素,每个元素int*..//数组名相当于首元素地址,arr2是int*类型的地址test(arr);//数组名相当于首元素地址test2(arr2);}
4.2二维数组传参
voidtest(int arr[3][5])//ok,二维数组接收{}voidtest(int arr[][])//NO,形参的二维数组,行可省,列不行{}voidtest(int arr[][5])//ok,行省了,列没省{}voidtest(int*arr)//NO,arr是二维数组,//首元素地址此时代表的是第一行的地址,相当于是一维数组//一维数组不能放入一级指针{}voidtest(int* arr[5])//NO{}voidtest(int(*arr)[5])//ok,指针指向5个元素,每个元素是int{}voidtest(int**arr)//NO,二级指针只能接收一级指针的变量的地址{}intmain(){int arr[3][5]={0};test(arr);//数组传参一般写成--数组名//除非传某个元素,——>&arr[1],arr[1]}
** 总结:
1 . 二维数组传参,函数形参的设计只能省略第一个 [ ] 的数字。
因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
这样才方便运算。
2 . 二维数组的数组名,表示首元素地址,其实是第一行的地址,第一行是一个一维数组
3.二级指针只能接收一级指针变量的地址**
4.3一级指针传参
#include<stdio.h>voidprint(int*p,int sz){int i =0;for(i=0; i<sz; i++){printf("%d\n",*(p+i));//*(p+i)==p[i]//(p+i),如果i=1,//+i跳过一个整形地址,再解引用,//就找到了相对应的元素}}intmain(){int arr[10]={1,2,3,4,5,6,7,8,9};int*p = arr;//1的地址赋给pint sz =sizeof(arr)/sizeof(arr[0]);//一级指针p,传给函数print(p, sz);return0;}
思考:当一个函数的参数部分为一级指针的时候,函数能接收什么参数?
voidtest1(int*p){}//test1函数能接收什么参数?intmain(){int a =10;test1(&a);//okint* pa =&a;test1(pa);//okint arr[5];test1(arr);//ok}
只要传给函数本质是一个指针,实参和形参类型能匹配上,就没问题。
4.4二级指针传参
#include<stdio.h>voidtest(int** ptr){printf("num = %d\n",**ptr);}intmain(){int n =10;int*p =&n;int**pp =&p;test(pp);//ok,本身是二级指针,传参二级指针接收test(&p);//ok,二级指针接收一级指针变量地址return0;}
思考:当函数的参数为二级指针的时候,可以接收什么参数?
voidtest(char**p){}intmain(){char c ='b';char*pc =&c;char**ppc =&pc;char* arr[10];test(&pc);//ok,二级指针接收一级指针变量地址test(ppc);//ok,本身是二级指针,传参二级指针接收test(arr);//Ok?//ok,每个元素是int*,首元素地址就是int*return0;}//三级指针解引用也可以....四级....五级....
如果你觉得文章不错,记得点赞+分享喔~如果有地方不对,欢迎随时评论指出 ~
版权归原作者 Origin! 所有, 如有侵权,请联系我们删除。