4.通过指针引用数组,重要面试
文章目录
- 4.通过指针引用数组,重要面试
- 4.1定义一个指针变量指向数组
- 4.2指针增量和数组的关系
- 4.3通过指针引用数组
- 1.下标法
- 2.指针法
- 2.1偏移
- 2.2取内容
- 数组名和指针的区别
- 两种方法效率对比
- 编程案例
- 1.函数封装数组初始化,遍历
- 2.将数组中的n个元素按逆序存放,函数封装
- 5.指针与二维数组
- 树立认知
- 小总结(嵌入式工程师笔试题会考)
- 6.数组指针
- 例题
- 7.函数指针
- 1.如何定义一个函数指针
- 2.好用之处
- 8.指针数组
- ==**1.定义,注意和数组指针的区别(面试会考)**==
- 2.函数指针的使用
- 9.指针函数
- 10.二级指针
- 11.总结
4.1定义一个指针变量指向数组
01指向数组首元素的地址
02等于数组名:指向数组起始位置
#include <stdio.h>
int main()
{01/*回顾我们之前的写法* int a =10;* int *p;* p =&a; */02//现在我们变成了数组int arr[3] = {1,2,3};int *p;01.p = &arr[0];//指针指向数组的首地址//还有另外一种写法02.p =arr; //等于整个数组名也是取的数组的首地址,,,注意前面不要 + &printf("address of a is %p\n ",&a);//指向一个固定的地址//数组名就是数组的首地址,数组的首地址就是首个元素的地址printf("address of p is %p\n",p);return 0;
}
4.2指针增量和数组的关系
我们知道数组在一个内存中是一个连续的地址,那么指针的增量与数组之间又存在着什么关系呢?
#include <stdio.h>
int main()
{int arr[3] = {1,2,3};int *p;p =arr;
printf("第0元素的数值是 %d\n",*p);
printf("第1元素的数值是 %d\n",*(p+1));//这里我们的+1并不是值+1,而是指针偏移,类型为整型,偏移四个字节,字符型,偏移一个字节
printf("第2元素的数值是 %d\n",*(p+2));//注意必须要加括号,下面的实操只是凑巧加起来是那个数,*p会首先结合然后再相加return 0;
}
但是通过上面的方法有没有感觉很蠢,如果他有一百个元素呢?难道需要输入一百次嘛?所以我们出现了现在的for循环
#include <stdio.h>
int main()
{int arr[3] = {1,2,3};int *p;p =arr;for(int i =0;i<3;i++){printf("第%d元素的数值是 %d\n",i,*(p+i)); printf("地址是 %p\n",(p+i)); }return 0;
}
4.3通过指针引用数组
#include <stdio.h>
int main()
{int arr[3] = {1,2,3};int *p;p =arr;//方法一for(int i =0;i<3;i++){printf("%d\n",*p++); //这个的意思是指针先*取值,然后在此基础上面再+1}p =arr; //这里我们需要注意的是重新给指针变量p 赋值,要不然经过上面的一系列操作,指针已经跑飞了//方法二for(int i =0;i<3;i++){printf("%d\n",*p);p++; //指针进行 偏移} return 0;
}
1.下标法
就是之前学过的数组的下标法遍历arr[ i ];
2.指针法
2.1偏移
上面的例子都是说的偏移,在此不再赘述
2.2取内容
- 指针当作数组名,下标法访问
- 数组名拿来加
///这个与上面的指针偏移不一样,所以不会跑飞,注意区别他们的不同的地方
#include <stdio.h>
int main()
{int arr[3]={1,2,3};int *p =arr;//方法一:for(int i =0;i<3;i++){printf("第%d个元素是%d\n",i,p[i]);//见怪不怪哈哈哈,他确实可以这样写}//方法二:for(int i =0;i<3;i++){printf("第%d个元素是%d\n",i,*(p+i));//见怪不怪哈哈哈,他确实可以这样写printf("第%d个元素是%d\n",i,*(arr+i));//见怪不怪哈哈哈,他确实可以这样写} return 0;
}
数组名和指针的区别
1.arr++可行否??
///这个与上面的指针偏移不一样,所以不会跑飞,注意区别他们的不同的地方
#include <stdio.h>
int main()
{int arr[3]={1,2,3};//那这个数组是一个常量int *p =arr;//首先p是一个保存地址的变量,那他保存的地址是可以改的,这叫做变量//方法一:for(int i =0;i<3;i++){printf("第%d个元素是%d\n",i,*p++);//,他这样可以写}//方法二:for(int i =0;i<3;i++){printf("第%d个元素是%d\n",i,*arr++);//那这样呢?数组常量可以吗?} //编译不过,指针常量return 0;
}
我们看到arr++是用不了的,
2.sizeof的使用
#include <stdio.h>
int main()
{int arr[3]={1,2,3};int *p =arr;printf("sizeof arr is %d\n",sizeof(arr));//一个元素四个字节,一共三个,所以12个字节printf("sizeof arr is %d\n",sizeof(p));//在OS中,用8个字节来表示一个地址printf("sizeof int is %d\n",sizeof(int));printf("sizeof pointer is %d\n",sizeof(int *));//在OS中,用8个字节来表示一个地址printf("sizeof pointer is %d\n",sizeof(char *));//在OS中,用8个字节来表示一个地址return 0;
}
两种方法效率对比
编程案例
1.函数封装数组初始化,遍历
练习函数数组指针结合
//首先来回顾一下我们以前写的代码
#include <stdio.h>
void initarr(int arr[],int size)//第二种传递形式参数的方法void printfarr(int *parr,int size)
//练习函数指针数组结合
{for(int i=0;i<size;i++){printf("请输入第%d个元素\n",i+1);scanf("%d",&arr[i])/*第二种方法scanf("%d",parr);//因为这个本身就是地址了指针偏移 parr++; */}
}
void printfarr(int arr[],int size)
{for(int i=0;i<size;i++){printf("%d\n",arr[i]);}
}
int main()
{int arr[5];int size = sizeof(arr)/sizeof(arr[0]);initarr(arr,size);//实际参数,数组的首地址:名,元素的地址printfarr(&arr[0],size);//那么我们知道,指针变量是存放地址的变量return 0;
}
#include <stdio.h>
void initarr(int *parr,int size)
//练习函数指针数组结合
{for(int i=0;i<size;i++){printf("请输入第%d个元素\n",i+1);scanf("%d",parr++);//因为这个本身就是地址了 }
}
void printfarr(int *parr,int size)
{for(int i=0;i<size;i++){printf("%d\n",*parr++);}
}
int main()
{int arr[5];int size = sizeof(arr)/sizeof(arr[0]);initarr(arr,size);//实际参数,数组的首地址:名,元素的地址printfarr(&arr[0],size);//那么我们知道,指针变量是存放地址的变量return 0;
}
2.将数组中的n个元素按逆序存放,函数封装
如何逆序存放呢? 第0个跟第4个换,第1个跟第3个换,2不动即可,,直接做一个临时变量,茶杯法直接交换即可
如果是奇数的话
那如果是偶数呢?结果同样成立
#include <stdio.h>
void initarr(int *parr,int size)
//练习函数指针数组结合
{for(int i=0;i<size;i++){printf("请输入第%d个元素\n",i+1);scanf("%d",parr++);//因为这个本身就是地址了 }
}
void reversedarr(int *parr,int size)
//练习函数指针数组结合
{for(int i=0;i<size/2;i++){ int j=size-1-i;int tmp;tmp = parr[i];parr[i] = parr[j];parr[j] = tmp;//以下是对上面的函数用指针来实现,自我感觉变抽象了哈哈哈哈tmp = *(parr+i);*(parr+i) = *(parr+j);*(parr+j) = tmp;}putchar('\n');
}
void printfarr(int *parr,int size)
{for(int i=0;i<size;i++){printf("%d ",*parr++);//因为这个本身就是地址了}
}
int main()
{int arr[5];int size = sizeof(arr)/sizeof(arr[0]);initarr(arr,size);//实际参数,数组的首地址:名,元素的地址printfarr(&arr[0],size);//那么我们知道,指针变量是存放地址的变量reversedarr(arr,size);printfarr(&arr[0],size);return 0;
}
5.指针与二维数组
父子数组,为了研究清楚地址的概念,把二维回归到一维数组
二维数组本质还是数组,不同点是数组元素还是个数组(子数组),以往的我们 int arr[ ]={1,2,3};如果arr+1 = 1;那么这次我们,如果是a[0]+1呢?如下图,a[0]是第一行第一列的地址,a[0]+1=3;
首地址的表示:1.数组名 2.首个元素的地址。
**下面我们来思考几个问题?? 1.a是谁的地址 2.a[0]又是谁的地址 3.a跟 (a+0)呢?
- a是父数组,地址是第一整行的地址,a+1向下偏移一行,4*4=16个字节
- a[0]是子数组,地址为第一行第一列的地址,
- 以往的 int *p = arr; int arr[ ]={1,2,3}, 我们知道指针变量是存放地址的变量,那么arr就是数组的首地址, *arr就是取内容之后,即 *arr=1;所以 *a就是第一行第一列的地址,与a[0]等价,也与 *(a+0)等价
那么a[0]+1又是什么意思呢?
- a[0]+1第0行第一列的地址,是地址的意思,,*(a+0)+1
- 也可以说是第0个子数组的第1个元素的地址
- 而第0个子数组的第1个元素表示方式是a[0] [1],不要乱
//一定要记住arr[0]=*arr=*(arr+0)
#include <stdio.h>
int main()
{int arr[3][4] ={{1,2,3,4};{5,6,7,8};{9,10,11,12}};printf("arr是父亲数组:%p,偏移1后是%p\n",arr,arr+1);printf("arr[0]是子数组:%p,偏移1后是%p\n",arr[0],arr[0]+1);printf("arr[0]是子数组:%p,偏移1后是%p\n",*arr,*(arr+0)+1);return 0;
}
可以看到父数组偏移了16个字节,子数组是4个字节
树立认知
//一定要记住arr[0]=*arr=*(arr+0)
#include <stdio.h>
int main()
{int arr[3][4] ={{1,2,3,4};{5,6,7,8};{9,10,11,12}};for(int i= 0;i<3;i++){for(int j =0;j<4;j++){//这是我们以前的写法,工作后也可以这样写,但面试就要用指针了printf("address:0x%p,data:%p\n",&arr[i][j],arr[i][j]); //arr[0]+0 = &arr[0][0]printf("address:0x%p,data:%p\n",arr[i]+j,*(arr[i]+j)); //笔试题考,这个就是指针的偏移来取到地址printf("address:0x%p,data:%p\n",*(arr+i)+j,*(*(arr+i)+j)); //把arr[0]=*(arr+0)替代,是不是傻眼了}}return 0;
}
小总结(嵌入式工程师笔试题会考)
6.数组指针
数组指针,一个指向数组的指针
指针数组,一堆指针组成的一个数组
#include <stdio.h>
int main()
{int arr[3][4] ={{11,22,33,44},{55,66,77,88},{44,55,66,77}};//那么之前我们说的arr++,是否可以呢?arr++增加的数值是一整个数组(arr[0][0],arr[1][0]),//而p++是单个偏移(arr[0][0],arr[0][1])int i,j;int *p;//01.p = arr;//02.p = &arr[0][0];for(i=0;i<3;i++){for(j=0;j<4;j++){printf("%p\n",arr++);printf("%p\n",p++);}}return 0;
}
例题
输出二维数组任意行列的数
#include <stdio.h>void tips_input(int *hang,int *lie)
{printf("输入你想要的行列\n");scanf("%d%d",&hang,&lie);puts("done! ");
}
int get_data(int (*p)[4],int hang,int lie)
{int data;data = *(*(p+hang)+lie);return data;//第二种简单的写法 return arr[hang][lie];}
int main()
{int arr[3][4] = {{11,22,33,44},{55,66,77,88},{99,01,02,03}};int hang,lie;int data;//1.提醒用户输入想要的行列值tips_input(&hang,&lie);//2.找到行列值所对应的数data = get_data(arr,hang,lie);//3.打印出来printf("第%d行,第%d列的数为%d",hang,lie,data);return 0;
}
7.函数指针
指向一个函数地址的指针,类似于一个函数的地址入口,定义“函数地址”,数组名是一个地址,那么函数名也是一个地址
1.如何定义一个函数指针
跟普通变量一样,
int a;
int *p;char c;
char *p;int getData(int a, int b);//函数传参
int (*p)(int a,int b);//函数指针的调用
//如何使用函数指针
#include <stdio.h>
void printf_hello()
{printf("欢迎来到我的世界\n");
}
int main()
{printf_hello();//这是以前我们调用函数void (*p)(); //1.定义一个函数指针p = printf_hello; //2.指针指向函数(*p)(); //3.把函数指针里面的内容调用出来
//函数调用概念和变量一样return 0;
}
//OK,现在我有两个函数,类型不同,有两种访问的方式,1.直接访问(函数调用) 2.间接访问(函数指针)
//下面来着重介绍一下函数指针的用法
#include <stdio.h>
void printf()
{puts("欢迎来到我的世界");
}
int idata(int dataone)
{return ++dataone;
}
int main()
{//1.定义指针 2.指针指向函数 3.调用函数指针void (*p1)();int (*p2)(int a);//注意实参也不要丢了啊啊啊啊(原先丢了一次编译会出错)p1 = printf;p2 = idata;(*p1)();printf("将P2呈现出来是%d\n",(*p2)(12));return 0;
}
2.好用之处
根据程序运行过程的不同情况,调用不同的函数(Java接口)
练习题
#include <stdio.h>
int getMax(int a,int b)
{return a>b?a:b;
}
int getMin(int a,int b)
{return a<b?a:b;
}
int getSum(int a,int b)
{return a+b;
}int dataHandler(int a,int b,int(*p)(int ,int))//我们强调的是函数类型,而具体的形参名如果用不到的话可以不定义
{int ret;ret = (*p)(a,b);return ret;
}
int main()
{int a = 10;int b = 20;int cmd;int ret;//1.定义函数指针int (*pfunc)(int ,int );//2.根据你输入的值来决定指针指向那个函数printf("请输入1(求大者),2(求小者),3(求和)\n");scanf("%d",&cmd);switch(cmd){case 1: pfunc = getMax; break;case 2: pfunc = getMin; break;case 3: pfunc = getSum; break;default: printf("输入错误。@请输入1(求大者),2(求小者),3(求和)");//exit(-1);break;}//3.将函数指针调用出来// ret = (*pfunc)(a,b);//这是一种简单的写法,还有一种封装函数的方法,了解一下ret = dataHandler(a,b,pfunc);printf("result of is %d",ret);return 0;
}
回调函数的底层逻辑,
- 线程 int pthread_create(pthread_t *id,const pthread_attr_t attr, void(start_rtn)(void), void *restrict arg);
- QT的信号与槽
8.指针数组
1.定义,注意和数组指针的区别(面试会考)
简单来说就是,一个由指针组成的数组,
//数组指针的使用
#include <stdio.h>
int main()
{int a,b,c,d;a = 10;b = 20;c = 30;d = 40;int *p[4] = {&a,&b,&c,&d};for(int i = 0;i<4;i++){printf("%d\n",*p[i]); }return 0;
}
2.函数指针的使用
//函数指针数组
#include <stdio.h>
int getMax(int a,int b)
{return a>b?a:b;
}
int getMin(int a,int b)
{return a<b?a:b;
}
int getSum(int a,int b)
{return a+b;
}int main()
{int a = 10;int b = 20;int cmd;int ret;//1.定义函数指针
//这个由上面得来,那我如果想要函数指针数组呢?int (*pfunc[3])(int ,int ) ={getMax,getMin,getSum};//3.将函数指针调用出来for(int i = 0;i<3;i++){ret = (*pfunc[i])(a,b);//这是一种简单的写法,还有一种封装函数的方法,了解一下printf("result[%d] of is %d",i,ret);}return 0;
}
9.指针函数
一个返回值是指针的函数
概念
练习题:
#include <stdio.h>
int* get_pos_person(int pos,int (*p)[4])
{int *p2;p2 = (int*)(p+pos);//因为这两个变量的类型不一样,所以要强转一下return p2;
}
int main()
{int arr[3][4] ={{11,22,33,44},{15,23,54,84},{61,51,48,25}};int pos;int *ppos;printf("请输入想看的学生号数(0,1,2):\n");scanf("%d",&pos);ppos = get_pos_person(pos ,arr);for(int i =0;i<4;i++){printf("他的成绩分别为%d\n",*(ppos++));}return 0;
}
10.二级指针
一级指针指向一个变量,存放他的地址;那么这个一级指针它本身也有一个地址,如果我再定义一个指针来存放这个一级指针的地址,那么这个指针就是二级指针,以此类推可延伸至三级乃至多级指针
认知考虑的时候,其实所有东西跟一级指针一样,写法:int **p;
#include <stdio.h>
int main()
{int data = 100;int *p = &data;int **p2 = &p;//二级指针printf("data的值为%d\n",data);printf("data本身的地址为%p\n",&data);printf("p存放的值为%d\n",*p);printf("p存放的值为%p\n",p);printf("p的地址为%p\n",&p);printf("p2存放的值为%p\n",*p2);printf("p2存放的值为%p\n",p2);printf("p2的地址为%p\n",&p2);printf("p2存放的地址的值(也就是一级指针存放的值为)%d\n",**p2);return 0;
}
差别就是保存的是指针变量的地址。当你通过函数调用来修改调用函数指针指向的时候,就像通过函数调用修改某变量的值的时候一样
//1.如果我们不返回一个int型指针呢?只有一个空类型,应该怎么传参呢?(此代码为上面已用过的)
//2.当你通过函数调用来修改调用函数指针指向的时候,就像通过函数调用修改某变量的值的时候一样
#include <stdio.h>
//int* get_pos_person(int pos,int (*p)[4])
void get_pos_person(int pos,int (*p)[4],int **ppos)
{//第一个括号是强转的意思。,转为指针变量*ppos = (int *)(p+pos);//我们修改的ppos的地址,用一个二级指针来承接一级指针的地址然后改动一级指针的地址
}
int main()
{int arr[3][4] ={{11,22,33,44},{15,23,54,84},{61,51,48,25}};int pos;int *ppos;printf("请输入想看的学生号数(0,1,2):\n");scanf("%d",&pos);get_pos_person(pos ,arr,&ppos);for(int i =0;i<4;i++){printf("他的成绩分别为%d\n",*(ppos++));}return 0;
}
二级指针不能简单粗暴指向二维数组
11.总结
中小公司大概率考题