当前位置:网站首页>【C语言】指针进阶---指针你真的学懂了吗?
【C语言】指针进阶---指针你真的学懂了吗?
2022-07-07 18:25:00 【猪皮兄弟】
🧸🧸🧸大家好,我是猪皮兄弟🧸🧸🧸
这里是下面要讲的知识内容🥳🥳🥳
前言
今天我们学习的内容是指针进阶。
一、知识点分类
今天的指针进阶包括字符指针,数组指针,指针数组,数组传参和指针传参,函数指针,函数指针数组,指向函数指针数组的指针,回调函数等等。
二、传参
1.一维数组传参
//形参写成数组的形式和指针的形式都是可以的
//这里不会创建真实的数组,所以这里的大小写成多少都没有必要
//因为传过去的仅仅是数组的首元素地址
void test1(int arr[10]);
void test1(int arr[]);
void test1(int *arr);
void test2(int**arr);
void test2(int*arr[]);
int main()
{
int arr1[10]={
0};
int*arr2[20]={
0};
test1(arr1);
test2(arr2);
return 0;
}
2.二维数组的传参
二维数组的首元素代表的其实是第一行的地址
1.形参写成数组的形式
test(int arr[3][5]);因为传的是第一行的地址,所以这里的3也是没有意义的,只需要列就可以了(列是不能省略的)
2.形参写成指针
void test(int (*p)[5]);
这是数组指针,*p代表它是指针,[5]代表这个数组有五个元素,int 代表每个元素的类型是int
千万不能写成void test(int(*p)[3][5]);这表示的是整个二维数组,而传参只需要第一行的地址
三、各类指针及数组
1.数组名的特殊例子
数组名一般表示首元素地址,但是有两个例外
1.sizeof(数组名):sizeof里面跟一个单独的数组名计算的是整个数组的大小。
2.&数组名:&后面跟一个单独的数组名取出的是整个数组的地址
2.函数指针
函数指针–>指向函数的指针
函数指针的形式:
比如函数
int Add(int x,int y)
那么函数指针就是
int (*pf)(int,int);
int Add(int x,int y)
{
return x+y;
}
int test(char*str)
{
}
int main()
{
int arr[10];
int(*p)[10]=&arr;
printf("%p\n",&Add);
printf("%p\n",Add);//可以说明Add和&Add的地址是相同的
//写法不同,但意义一样,得到的都是函数的地址
int(*pf)(int,int)=Add;
//等同于
//int(*pf)(int,int)=&Add;
int ret=(*pf)(2,3);
//int ret=(********pf)(2,3);
//int ret=Add(2,3);
//这里的pf就是函数指针
int(*pf2)(char*)=test;//&test
printf("%d\n",(*pf)(2,3));
printf("%d\n",ret);
return 0;
}
对函数指针例子的解释
int main()
{
//1.
(*(void(*)())0)();
//void(*)()就是某一个函数指针的类型
//(void(*)())0也就是把0强制类型转换成这样的函数指针
//意味着0地址处放着这样一个函数,哪个函数的地址为0(这里知识假设访问0这个地址)
//应用程序是不能访问0这个地址的
//和int (*pf)(int,int)=Add;类比,也就是在pf处放了一个函数
//然后对这个地址解引用*(void(*)())0找到这个函数,再调用*(void(*)())0()
//2.
void (*signal(int, void(*)(int)))(int);
//void(*)(int)是signal的第二个参数,是一个函数指针
//signal函数的放回类型是一个函数指针
}
3.函数指针数组
int Add(int x,int y)
{
return x+y;
}
int Sub(int x,int y)
{
return x-y;
}
int Mul(int x,int y)
{
return x*y;
}
int Div(int x,int y)
{
return x/y;
}
int main()
{
//指针数组
//字符指针数组
char*arr[5];
//整型指针数组
int*arr2[4];
int(*pf1)(int,int)=Add;
int(*pf2)(int,int)=Sub;
int(*pf3)(int,int)=Mul;
int(*pf4)(int,int)=Div;
//函数指针数组
int (*pf[4])(int,int)={
Add,Sub,Mul,Div};
printf("%d",pf[0](1,2));
return 0;
//先是pf[4]代表是个数组,大小为4
//每个元素的类型是int (*)(int,int);
}
4.函数指针数组实现运算器
int Add(int x,int y)
{
return x+y;
}
int Sub(int x,int y)
{
return x-y;
}
int Mul(int x,int y)
{
return x*y;
}
int Div(int x,int y)
{
return x/y;
}
void menu()
{
printf("********************\n");
printf("*** 1.Add ***\n");
printf("*** 2.Sub ***\n");
printf("*** 3.Mul ***\n");
printf("*** 4.Div ***\n");
printf("*** 0.Exit ***\n");
}
//实现计算器
int main()
{
int input;
int x,y;
int ret;
int (*pf[5])(int,int) = (0,Add,Sub,Mul,Div);
//这样子初始化是因为数组是从0开始的
do
{
menu();
printf("请选择:>");
scanf("%d\n",&input);
if(input==0)
{
printf("退出计算器.....\n");
break;
}
else if(input>=1&&input<=4)
{
printf("请输入两个操作数:>");
scanf("%d %d",&x,&y);
ret=pf[input](x,y);
printf("ret=%d\n",ret);
}
else
{
printf("选择错误\n");
}
}while(input);
return 0;
}
三、回调函数与qsort函数
1.回调函数
概念:
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针也就是地址作为参数传递给另外一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对时间或条件进行响应.
A函数的地址传给B函数,在B函数中使用就被称为回调函数
void test()
{
printf("zhupi\n");
}
void print_zhupi(void(*pf)())
{
if(某个条件)//如果某个条件或时间发生,则响应
pf();
}
int main()
{
print_zhupi(test);
return 0;
}
用回调函数完成计算器
int Add(int x,int y)
{
return x+y;
}
int Sub(int x,int y)
{
return x-y;
}
int Mul(int x,int y)
{
return x*y;
}
int Div(int x,int y)
{
return x/y;
}
void calc(int (*pf)(int,int))
{
int x,y;
int ret=0;
printf("请输入两个操作数:>");
scanf("%d %d",&x,&y);
ret=pf(x,y);
printf("%d\n",ret);
}
void menu()
{
printf("********************\n");
printf("*** 1.Add ***\n");
printf("*** 2.Sub ***\n");
printf("*** 3.Mul ***\n");
printf("*** 4.Div ***\n");
printf("*** 0.Exit ***\n");
}
//实现计算器
int main()
{
int input;
int x,y;
int ret;
int (*pf[5])(int,int) = (0,Add,Sub,Mul,Div);
//这样子初始化是因为数组是从0开始的
do
{
menu();
printf("请选择:>");
scanf("%d\n",&input);
switch(input)
{
case 1:
calc(Add);
break;
case 2:
calc(Sub);
break;
case 3:
calc(Mul);
break;
case 1:
calc(Div);
break;
case 0:
exit(-1);
}
return 0;
}
2.qsort函数
qsort函数是一个库函数,是基于快速排序算法实现的一个排序的函数
qsort需要引的头文件是#include <stdlib.h>
qsort这个函数任意类型的数据它都可以排序
我们自己写的函数不一定能够做到
比如我们的冒泡排序
void bubble_sort(int arr[],int len)
{
for(int i=0;i<len-1;i++)
{
for(int j=0;j<len-1-i;j++)
{
if(arr[j]>arr[j+1])
{
int temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
}
int main()
{
int arr[]={
9,8,7,6,5,4,3,2,1,0};
int len=sizeof(arr)/sizeof(arr[0]);
bubble_sort(arr,len);
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
return 0;
}
//可以看出,并不具有通用性
qsort:
qsort里自己定义的排序函数需要返回>0 <0 =0的数字,得到结果之后qsort就可以根据返回的数进行排序
现在对qsort的参数进行分析
void qsort(void*base,
size_t num,
size_t width,
int(*cmmp)(const void*e1,const void*e2));
//base是待排序数据的其实位置
//num是数组的元素个数
//width是每个元素的大小
//elem1 elem2是待比较的两个元素的地址
//这个比较函数是要求qsort函数的使用者自定义一个比较函数
//因为元素类型不同,使用者根据实际情况,提供一个比较函数
举例=>
int cmp_int(const void*e1,const void*e2)
{
//void*是无确切类型的指针,无法直接解引用
//因为指针的类型就代表着解引用时能访问几个字节的权限,而void*是不明确的;
//void*是一个垃圾桶,什么类型都能往里放,到时候强制类型转换就可以了;
//1.
if(*(int*)e1>*(int*)e2)
{
return 1;
}
else if(*(int*)e1<*(int*)e2)
{
return -1;
}
else
{
return 0;
}
//2.
return (*(int*)e1-*(int*)e2;
}
void test()
{
int arr[]={
9,8,7,6,5,4,3,2,1,0};
int len=sizeof(arr)/sizeof(arr[0]);
qsort(arr,len,sizeof(arr[0]),cmp_int);
for(int i=0;i<10;i++)
{
printf("%d ",arr[i]);
}
}
int main()
{
test();
return 0;
}
使用qsort排序结构体
struct Stu
{
char mame[20];
int age;
double score;
};
int cmp_stu_by_name(const void*e1,const void*e2)
{
return strcmp(((struct Stu*)e1)->mame, ((struct Stu*)e2)->mame);
}
void test()
{
struct Stu arr[3] = {
{
"zhangsan",20,55.5},{
"lisi",30,88.0},{
"wangwu",10,90.0} };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr,sz,sizeof(arr[0]),cmp_stu_by_name);
}
int main()
{
test();
return 0;
}
3.通用的冒泡排序
int cmp_int(const void* e1, const void* e2)//比较什么自己决定
{
return *(int*)e1 - *(int*)e2;
}
void Swap(char*buf1,char*buf2,int width)
{
for (int i = 0; i < width; i++)
{
char temp = *buf1;
*buf1 = *buf2;
*buf2 = temp;
buf1++;
buf2++;
}
//一个字节一个字节的交换
}
void bubble_sort(void* base, size_t num, size_t width, int (*cmp)(const void* e1, const void* e2))
{
for (int i = 0; i < num - 1; i++)
{
for (int j = 0; j < num - 1 - i; j++)
{
//比较,因为知道每个元素的宽度,所以强制类型转换成char*
if (cmp((char*)base+j*width,(char*)base+(j+1)*width)>0)
{
//交换
Swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
}
}
}
}
void print_arr(int * base, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ",*(base+i));
}
}
void test2()
{
int arr[] = {
9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
print_arr(arr,sz);
}
int main()
{
test2();
return;
}
四、各种sizeof和strlen的计算!!!
1.一维数组
int main()
{
int a[] = {
1,2,3,4 };
printf("%d\n",sizeof(a));//16
printf("%d\n",sizeof(a+0));//4/8,括号里的不是单独的数组名,所以表示的是首元素的地址,地址也就是指针
printf("%d\n",sizeof(*a));//4 不是单独的数组名,所以a代表首元素地址,解引用,就是第一个元素
printf("%d\n",sizeof(a+1));//4/8 计算的是第二个元素的地址的大小
printf("%d\n",sizeof(a[1]));//4 计算的是第二个元素的大小
printf("%d\n",sizeof(&a));//4/8 &a取出的 是数组的地址,数组的地址也是地址,是地址(指针)大小就是4/8个字节
printf("%d\n",sizeof(*&a));//16 &a取出的是整个数组的地址,再解引用得到的就是整个数组,所以计算的是整个数组的大小
printf("%d\n",sizeof(&a+1));//4/8 &a拿到的是整个数组的地址,+1是跳过整个数组,但是也是地址
printf("%d\n",sizeof(&a[0]));//4/8 代表的是第一个元素的地址
printf("%d\n",sizeof(&a[0]+1));//4/8 代表的是第二个元素的地址
return 0;
}
2.字符数组
int main()
{
char arr[] = {
'a','b','c','d','e','f'};
printf("%d\n",sizeof(arr));//6
printf("%d\n",sizeof(arr+0));//4/8
printf("%d\n",sizeof(*arr));//1
printf("%d\n",sizeof(arr[1]));//1
printf("%d\n",sizeof(&arr));//4/8
printf("%d\n",sizeof(&arr+1));//4/8
printf("%d\n",sizeof(&arr[0]+1));//4/8
return 0;
}
int main()
{
char arr[] = {
'a','b','c','d','e','f' };
//strlen寻找结束表示\0
printf("%d\n",strlen(arr));//随机值,arr数组中没有\0,所以strlen函数会继续往后找\0,统计\0之前出现的字符个数
printf("%d\n",strlen(arr+0));//随机值
printf("%d\n",strlen(*arr));//error arr是数组首元素地址,*arr是数组的首元素,'a'-97,strlen把97当成一个地址,从这个地址出发
//向后找\0,可能会出现野指针问题,访问内存出错的问题,所以这个代码本身上是错误的,并没有得到一个有效的地址
//在指针变量的眼里,什么都是地址,因为他天生存在的意义就是存放地址
printf("%d\n",strlen(arr[1]));//error
printf("%d\n",strlen(&arr));//随机值,找\0
printf("%d\n",strlen(&arr+1));//随机值
printf("%d\n",strlen(&arr[0]+1));//随机值
return 0;
}
int main()
{
char arr[] = "abcdef";
printf("%d\n",sizeof(arr));//7
printf("%d\n",sizeof(arr+0));//4/8
printf("%d\n",sizeof(*arr));//1
printf("%d\n",sizeof(arr[1]));//1
printf("%d\n",sizeof(&arr));//4/8
printf("%d\n",sizeof(&arr+1));//4/8
printf("%d\n",sizeof(&arr[0]+1));//4/8
printf("%d\n", strlen(arr));//6
printf("%d\n", strlen(arr + 0));//6
printf("%d\n", strlen(*arr));//error
printf("%d\n", strlen(arr[1]));//error
printf("%d\n", strlen(&arr));//6
printf("%d\n", strlen(&arr + 1));//随机值
printf("%d\n", strlen(&arr[0] + 1));//5
return 0;
}
int main()
{
char* p = "abcdef";//常量字符串
//p是'a'的地址
printf("%d\n",sizeof(p));//4/8
printf("%d\n",sizeof(p+1));//4/8 p+1是'b'的地址
printf("%d\n",sizeof(*p));//18
printf("%d\n",sizeof(p[0]));//1
printf("%d\n",sizeof(&p));//4/8 &p是指针的地址,二重指针
printf("%d\n",sizeof(&p+1));//4/8
printf("%d\n",sizeof(&p[0]+1));//4/8
printf("%d\n",strlen(p));//6
printf("%d\n",strlen(p+1));//5
printf("%d\n",strlen(*p));//error
printf("%d\n",strlen(p[0]));//error
printf("%d\n",strlen(&p));//随机值
printf("%d\n",strlen(&p+1));//随机值
printf("%d\n",strlen(&p[0]+1));//5
return 0;
}
3.二维数组
int main()
{
int a[3][4] = {
0 };
printf("%d\n",sizeof(a));//48 sizeof(数组名)计算的是整个数组的大小
printf("%d\n",sizeof(a[0][0]));//4
printf("%d\n",sizeof(a[0]));//16 a[0]是*(a+0)是第一行的首元素地址,也就是第一行的数组名,所以sizeof(a[0])其实计算的是第一行的大小
printf("%d\n",sizeof(a[0]+1));//4/8 a[0]是第一行的数组名,a[0]+1也就是第一行的第二个元素的地址
printf("%d\n",sizeof(*(a[0]+1)));//4 也就是a[0][1]
printf("%d\n",sizeof(a+1));//4/8 二维数组的数组名代表的是第一行的地址,所以a+1代表的是第二行的地址
printf("%d\n",sizeof(*(a+1)));//16 a+1是第二行的地址,解引用了之后就是第二行的首元素地址,所以计算的是第二行的大小
printf("%d\n",sizeof(&a[0]+1));//4/8 代表的是第二行的地址
printf("%d\n",sizeof(*(&a[0]+1)));//16 括号里表示的是第二行的首元素地址 也就是第二行的数组名,计算的是第二行数组的大小
printf("%d\n",sizeof(*a));//16 计算的是第一行数组的地址
printf("%d\n",sizeof(a[3]));//16 a[3]是第四行的首元素地址
return 0;
}
五、五彩斑斓的一些废话
当你看到这里,相信上面的内容已经倒背如流了吧️️️。各位如果觉得有帮助的话,动动小手指点点点。一键三连有问题吗?没有问题,这都是什么?人情世故。创作不易,多多支持。
边栏推荐
- 如何挑选基金产品?2022年7月份适合买什么基金?
- Vulnhub tre1
- Don't fall behind! Simple and easy-to-use low code development to quickly build an intelligent management information system
- Force buckle 989 Integer addition in array form
- 使用 BR 恢复 Azure Blob Storage 上的备份数据
- OneSpin 360 DV新版发布,刷新FPGA形式化验证功能体验
- PHP method of obtaining image information
- Lingyun going to sea | saihe & Huawei cloud: jointly help the sustainable development of cross-border e-commerce industry
- 微服务远程Debug,Nocalhost + Rainbond微服务开发第二弹
- Flask1.1.4 Werkzeug1.0.1 源码分析:路由
猜你喜欢
Machine learning notes - explore object detection datasets using streamlit
Chapter 9 Yunji datacanvas company won the highest honor of the "fifth digital finance innovation competition"!
Opencv learning notes high dynamic range (HDR) imaging
VMWare中虚拟机网络配置
Apifox 接口一体化管理新神器
嵌入式系统真正安全了吗?[ OneSpin如何为开发团队全面解决IC完整性问题 ]
Force buckle 2319 Judge whether the matrix is an X matrix
CodeSonar如何帮助无人机查找软件缺陷?
Yolov6:yolov6+win10--- train your own dataset
一键部署Redis任意版本
随机推荐
Don't fall behind! Simple and easy-to-use low code development to quickly build an intelligent management information system
静态测试工具
机械臂速成小指南(十一):坐标系的标准命名
Machine learning notes - explore object detection datasets using streamlit
About cv2 dnn. Readnetfromonnx (path) reports error during processing node with 3 inputs and 1 outputs [exclusive release]
Force buckle 599 Minimum index sum of two lists
Force buckle 2315 Statistical asterisk
Oracle 存储过程之遍历
CJSON内存泄漏的注意事项
深度学习模型压缩与加速技术(七):混合方式
Is it safe to open a stock account at present? Can I open an account online directly.
【奖励公示】第22期 2022年6月奖励名单公示:社区明星评选 | 新人奖 | 博客同步 | 推荐奖
如何在软件研发阶段落地安全实践
Flask1.1.4 werkzeug1.0.1 source code analysis: Routing
TS快速入门-泛型
Force buckle 2319 Judge whether the matrix is an X matrix
Chapter 9 Yunji datacanvas company won the highest honor of the "fifth digital finance innovation competition"!
I wrote a markdown command line gadget, hoping to improve the efficiency of sending documents by garden friends!
使用 BR 备份 TiDB 集群数据到 Azure Blob Storage
I Basic concepts