当前位置: 首页 > news >正文

C语言——深入理解指针(3)

目录

1. 字符指针

2. 数组指针

2.1 数组指针变量

2.2 数组指针变量的初始化

3.二维数组传参(本质)

4. 函数指针

4.1 函数指针变量的创建

4.2 函数指针的使用

4.3 typedef 

5. 函数指针数组

6. 转移表(函数指针数组的使用)


1. 字符指针

在指针的类型中有一种指针类型为字符指针 char*

#include<stdio.h>
int main()
{char ch = 'h';char* pc = &ch;//pc就是字符指针const char* pstr = "hello";//常量字符串//这里不是把字符串存在p中,而是把第一个字符的地址存放在p中//1,你可以把字符串相信成一个字符数组//2.当常量字符串出现在表达式中的时候,他点值是第一个字符的地址printf("%c\n", "hello"[3]);printf("%s\n", pstr);
}

 

 举例:

#include <stdio.h>
int main()
{char str1[] = "hello bit.";char str2[] = "hello bit.";const char* str3 = "hello bit.";const char* str4 = "hello bit.";if (str1 == str2)//str1和str2是两个不同地址的字符数组。printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n");if (str3 == str4)//str3和str4指向的是同一个常量字符串。printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0;
}

 注意:内容相同的常量字符串只会保存一份。

2. 数组指针

2.1 数组指针变量

在前面的文章中,我们学了指针数组,它是一种数组,里面存的是地址(指针)。

数组指针其实跟指针数组不同,数组指针是:指针变量。

我们回忆一下指针之前的一些指针变量:

整形指针变量: int * p1; 存放的是整形变量的地址,能够指向整形数据的指针。

浮点型指针变量: float * p2; 存放浮点型变量的地址,能够指向浮点型数据的指针。

那么数组指针变量就应该是:存放的是数组的地址,能够指向数组的指针变量。

数组指针变量
int (*p)[10];
p先和*结合,说明p是⼀个指针变量变量,然后指着指向的是⼀个⼤⼩为10个整型的数组。

注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合

2.2 数组指针变量的初始化

int arr[10] = {0};
&arr;//得到的就是数组的地址
int(*p)[10] = &arr;//p数组指针指向arr数组的指针
&arr表示整个数组的地址

 数组指针类型:

int (*p) [10] = &arr;| | || | || | p指向数组的元素个数| p是数组指针变量名p指向的数组的元素类型

3.二维数组传参(本质)

之前我们需要把一个二维数组传参给一个函数的时候,我们是这么写的

#include <stdio.h>
void test(int a[3][5], int r, int c)//这里的形参是二维数组
{int i = 0;int j = 0;for (i = 0; i < r; i++){for (j = 0; j < c; j++){printf("%d ", a[i][j]);}printf("\n");}
}
int main()
{int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7} };test(arr, 3, 5);return 0;
}

 那还有什么其他的写法吗?

 我们可以看到二维数组可以被分为多个一维数组,可以把每个一维数组看成一个元素,那么二维数组的首元素就是第一个一维数组。根据数组名是数组⾸元素的地址这个规则,⼆维数组的数组名表示的就是第⼀⾏的地址,是⼀维数组的地址。既然是数组地址我们就要用数组指针 int (*p)来存,但是一维数组里面还有很多数,这时我们就要在数组指针后面加个数组 int(*p)[] ,来把这些数存下来。

⼆维数组传参本质上也是传递了地址,传递的是第⼀行这个⼀维数组的地址。

int main()
{int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7} };test(arr, 3, 5);return 0;
}
#include <stdio.h>
void test(int (*p)[5], int r, int c)
{int i = 0;int j = 0;for (i = 0; i < r; i++){for (j = 0; j < c; j++){printf("%d ", a[i][j]);}printf("\n");}
}
int main()
{int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7} };test(arr, 3, 5);return 0;
}

总结:⼆维数组传参,形参的部分可以写成数组,也可以写成指针形式。

4. 函数指针

4.1 函数指针变量的创建

根据我们前面学的指针变量,我们大概可以推出,函数指针变量就是用来存放函数地址的,后面可以通过地址来调用函数。

函数是有地址的,且函数名就是函数的地址,我们可以通过&函数来获得函数的地址。要存地址,那就要用到指针,存函数的地址,就要用函数指针。函数指针的创建跟数组指针差不多。

int (*p) (int x, int y)|     | ------------ |     |     ||     | p指向函数的参数类型和个数的交代|     ||     函数指针变量名|p指向函数的返回类型

举例:

#include <stdio.h>
int Add(int x, int y)
{return x + y;
}
int main()
{//int(*pf3)(int, int) = Add;int(*pf3)(int x, int y) = &Add;//x和y写上或者省略都是可以的printf("%p\n", pf3);return 0;
}

4.2 函数指针的使用

#include <stdio.h>
int Add(int x, int y)
{return x + y;
}
int main()
{//int(*pf3)(int, int) = Add;int(*pf3)(int x, int y) = &Add;printf("%d\n", pf3(1, 2));//pf3(1, 2)==Add(1,2)printf("%d\n", (*pf3)(1, 2));return 0;
}

 我们根据之前学的来看两个代码

(*(void (*)())0)();
void (*signal(int , void(*)(int)))(int);

4.3 typedef 

typedef 是用来给类型重名的,可以将复杂的类型,简单化。

typedef unsigned int uint;
//将unsigned int 重命名为uint

指针类型一样可以重命名。

typedef int* ptr_t;
//将 int* 重命名为 ptr_t

注意:数组指针和函数指针的类型重名有点不同

数组指针:

typedef int(*parr_t)[5]; //新的类型名必须在*的右边
//将指针类型 int(*)[5] ,重命名为 parr_t

函数指针:

typedef void(*pfun_t)(int);//新的类型名必须在*的右边
//将 void(*)(int) 类型重命名为 pf_t

5. 函数指针数组

数组是一个存放相同类型数组空间。那把函数的地址存到⼀个数组中,那这个数组就叫函数指针数组。

int (*parr1[3])();
int (*)() 类型的函数指针。

6. 转移表(函数指针数组的使用)

计算机的实现:

#include <stdio.h>
int add(int a, int b)
{return a + b;
}
int sub(int a, int b)
{return a - b;
}
int mul(int a, int b)
{return a * b;
}
int div(int a, int b)
{return a / b;
}
int main()
{int x, y;int input = 1;int ret = 0;int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表--函数指针数组的存入do{printf("*************************\n");printf(" 1:add 2:sub \n");printf(" 3:mul 4:div \n");printf(" 0:exit \n");printf("*************************\n");printf("请选择:");scanf("%d", &input);if ((input <= 4 && input >= 1)){printf("输入要求的数:");scanf("%d %d", &x, &y);ret = (*p[input])(x, y);//函数指针数组的元素使用printf("ret = %d\n", ret);}else if (input == 0){printf("退出计算器\n");}else{printf("输入错误,请重新输入\n");}} while (input);return 0;
}
http://www.lryc.cn/news/248263.html

相关文章:

  • 图书管理系统源码,图书管理系统开发,图书借阅系统源码配置和运行图解源码已附加
  • FFmpeg介绍
  • 修改网卡PHY的灯-RK3568
  • 11月29日作业
  • 【从删库到跑路 | MySQL总结篇】表的增删查改(进阶下)
  • 【机器学习 | 可视化系列】可视化系列 之 决策树可视化
  • 配置阿里云的yum仓库
  • Kubernetes之kubeadm日志展示篇—K8S私有云worker节点gluster安装部署
  • P3368 【模板】树状数组 2 (区间修改,单点查询)
  • 智慧城市运营管理平台解决方案:PPT全文61页,附下载
  • Vue性能优化方法
  • 关于网站的favicon.ico图标的设置需要注意的几点
  • PHP中关于func_get_args()方法
  • EMA训练微调
  • Kafka集群部署详细教程
  • 交叉编译
  • 数据结构与算法之递归: LeetCode 46. 全排列 (Typescript版)
  • SQL中 JOIN 的两种连接类型:内连接(自然连接、自连接、交叉连接)、外连接(左外连接、右外连接、全外连接)
  • 微信小程序记住密码,让登录解放双手
  • 国内划片机行业四大企业之博捷芯:技术驱动,领跑未来
  • 后端整合Swagger+Knife4j接口文档
  • k8s中批量处理Pod应用的Job和CronJob控制器介绍
  • UE5 范围内随机生成
  • 杂记 | 使用Docker安装并配置MongoDB以支持事务(单副本,并解决了证书文件错误的问题)
  • css三角,鼠标样式,溢出文字
  • 远程桌面访问MATLAB 2018B,提示License Manger Error -103,终极解决方案
  • Jmeter基础和概念
  • 【Linux 带宽限速】trickle,限制docker 上传速度
  • MindStudio学习记录三:推理应用开发 acl mindx sdk
  • 【RT-DETR改进】SIoU、GIoU、CIoU、DIoU、AlphaIoU等二十余种损失函数