您的位置:首页 > 健康 > 养生 > 【C语言从入门到入土】第六章 指针(下)

【C语言从入门到入土】第六章 指针(下)

2024/12/23 15:24:48 来源:https://blog.csdn.net/v13111329954/article/details/139547508  浏览:    关键词:【C语言从入门到入土】第六章 指针(下)

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取内容
  1. 指针当作数组名,下标法访问
  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)呢?

  1. a是父数组,地址是第一整行的地址,a+1向下偏移一行,4*4=16个字节
  2. a[0]是子数组,地址为第一行第一列的地址,
  3. 以往的 int *p = arr; int arr[ ]={1,2,3}, 我们知道指针变量是存放地址的变量,那么arr就是数组的首地址, *arr就是取内容之后,即 *arr=1;所以 *a就是第一行第一列的地址,与a[0]等价,也与 *(a+0)等价

在这里插入图片描述

那么a[0]+1又是什么意思呢?

  1. a[0]+1第0行第一列的地址,是地址的意思,,*(a+0)+1
  2. 也可以说是第0个子数组的第1个元素的地址
  3. 而第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;
}

回调函数的底层逻辑,

  1. 线程 int pthread_create(pthread_t *id,const pthread_attr_t attr, voidstart_rtn)(void), void *restrict arg);
  2. 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.总结

中小公司大概率考题

在这里插入图片描述

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com