C语言——字符、数组和函数指针变量

目录

  • 1. 字符指针变量
  • 2. 数组指针变量
    • 2.1 数组指针变量是什么?
    • 2.2 数组指针变量怎么初始化
  • 3. 二维数组传参的本质
  • 4. 函数指针变量
    • 4.1 函数指针变量的创建
    • 4.2 函数指针变量的使用
    • 4.3 typedef关键字
  • 5. 函数指针数组
  • 6. 转移表

1. 字符指针变量

  • 在指针的类型中我们知道有⼀种指针类型为字符指针 char*
  • ⼀般使用:
int main()
{char ch = 'w';char *pc = &ch;*pc = 'w';return 0;
}
  • 还有⼀种使用方式如下:
int main()
{const char* pstr = "hello bit.";//这⾥是把⼀个字符串放到pstr指针变量⾥了吗?printf("%s\n", pstr);return 0;
}
  • 代码 const char* pstr = “hello bit.”; 特别容易让同学以为是把字符串 hello bit 放到字符指针 pstr 里了,但是本质是把字符串 hello bit. 首字符的地址放到了 pstr 中

在这里插入图片描述

  • 上面代码的意思是把⼀个常量字符串的首字符 h 的地址存放到指针变量 pstr 中
  • 《剑指offer》中收录了⼀道和字符串相关的笔试题,我们⼀起来学习⼀下:
#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)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");return 0;
}
  • 运行结果如下:

在这里插入图片描述

  • 这⾥str3和str4指向的是⼀个同⼀个常量字符串。C/C++会把常量字符串存储到单独的⼀个内存区域,
  • 当几个指针指向同⼀个字符串的时候,他们实际会指向同⼀块内存。但是⽤相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同
  • 我们来调试一下会更加明显:

在这里插入图片描述

2. 数组指针变量

2.1 数组指针变量是什么?

  • 之前我们学习了指针数组,指针数组是⼀种数组,数组中存放的是地址(指针)。数组指针变量是指针变量?还是数组?
  • 答案是:指针变量
  • 我们已经熟悉:
  1. 整形指针变量: int * pint; 存放的是整形变量的地址,能够指向整形数据的指针
  2. 浮点型指针变量: float * pf; 存放浮点型变量的地址,能够指向浮点型数据的指针
  • 那数组指针变量应该是:存放的应该是数组的地址,能够指向数组的指针变量
  • 下⾯代码哪个是数组指针变量?
	int *p1[10];int (*p2)[10];
  • 思考⼀下:p1, p2分别是什么?
  • 这里的p1先和[10]结合,表示的是一个元素个数为10的数组,接着前面的int * 表示这个数组存储的元素的类型为 int *
  • 这里的p2先和 * 结合,表示指针,指针指向了一个元素个数为10的数组,数组元素的类型为 int ,这里的p2就是一个指针,指向了数组,所以叫数组指针
  • 这里要注意:[]的优先级要高于 * 号的,所以必须加上()来保证p先和 * 结合

2.2 数组指针变量怎么初始化

  • 数组指针变量是用来存放数组地址的,那怎么获得数组的地址呢?就是我们之前学习的 &数组名
int arr[10] = {0};
&arr;//得到的就是数组的地址
  • 如果要存放个数组的地址,就得存放在数组指针变量中,如下:
 int(*p)[10] = &arr;

在这里插入图片描述

  • 我们调试也能看到 &arr 和 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 [5] ,所以第⼀行的地址的类型就是数组指针类型 int(*)[5] 。
  • 那就意味着⼆维数组传参本质上也是传递了地址,传递的是第⼀行这个⼀维数组的地址,那么形参也是可以写成指针形式的。如下:
#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 ", *(*(p+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 函数指针变量的创建

  • 什么是函数指针变量呢?
  • 根据前面学习整型指针,数组指针的时候,我们的类比关系,我们不难得出结论:
  • 函数指针变量应该是用来存放函数地址的,未来通过地址能够调用函数的
  • 那么函数是否有地址呢?
  • 我们做个测试:
#include <stdio.h>
void test()
{printf("hehe\n");
}
int main()
{printf("test: %p\n", test);printf("&test: %p\n", &test);return 0;
}
  • 运行结果如下:

在这里插入图片描述

  • 确实打印出来了地址,所以函数是有地址的,函数名就是函数的地址,当然也可以通过 &函数名 的方式获得函数的地址
  • 如果我们要将函数的地址存放起来,就得创建函数指针变量咯,函数指针变量的写法其实和数组指针非常类似。如下:
void test()
{printf("hehe\n");
}void (*pf1)() = &test;
void (*pf2)()= test;//函数的&arr和arr是一个东西,区别于数组int Add(int x, int y)
{return x+y;
}int(*pf3)(int, int) = Add;
int(*pf3)(int x, int y) = &Add;//x和y写上或者省略都是可以的
  • 函数指针类型解析:

在这里插入图片描述

4.2 函数指针变量的使用

  • 通过函数指针调用指针指向的函数
#include <stdio.h>
int Add(int x, int y)
{return x+y;
}
int main()
{int(*pf3)(int, int) = Add;printf("%d\n", (*pf3)(2, 3));printf("%d\n", pf3(3, 5));return 0;
}
  • 输出结果:

在这里插入图片描述

4.3 typedef关键字

  • typedef 是用来类型重命名的,可以将复杂的类型,简单化
  • 比如,你觉得 unsigned int 写起来不方便,如果能写成 uint 就用便多了,那么我们可以使用:
typedef unsigned int uint;
//将unsigned int 重命名为uint
  • 如果是指针类型,能否重命名呢?其实也是可以的,比如,将 int* 重命名为 ptr_t ,这样写:
typedef int* ptr_t;
  • 但是对于数组指针和函数指针稍微有点区别:
    比如我们有数组指针类型 int(*)[5] ,需要重命名为 parr_t ,那可以这样写:
typedef int(*parr_t)[5]; //新的类型名必须在*的右边
  • 函数指针类型的重命名也是⼀样的,⽐如,将 void(*)(int) 类型重命名为 pf_t ,就可以这样写:
typedef void(*pfun_t)(int);//新的类型名必须在*的右边

5. 函数指针数组

  • 数组是⼀个存放相同类型数据的存储空间,我们已经学习了指针数组,比如:
int *arr[10];
//数组的每个元素是int*
  • 那要把函数的地址存到⼀个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?
int (*parr1[3])();//parr1是以void为参数,返回值为int的函数的地址,指针的类型为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;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);switch (input){case 1:printf("输⼊操作数:");scanf("%d %d", &x, &y);ret = add(x, y);printf("ret = %d\n", ret);break;case 2:printf("输⼊操作数:");scanf("%d %d", &x, &y);ret = sub(x, y);printf("ret = %d\n", ret);break;case 3:printf("输⼊操作数:");scanf("%d %d", &x, &y);ret = mul(x, y);printf("ret = %d\n", ret);break;case 4:printf("输⼊操作数:");scanf("%d %d", &x, &y);ret = div(x, y);printf("ret = %d\n", ret);break;case 0:printf("退出程序\n");break;default:printf("选择错误\n");break;}} while (input);return 0;
}
  • 使用函数指针数组的实现:
#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.hqwc.cn/news/457945.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解析基于检索排序的知识图谱问答系统

目录 前言1 问句的表示与语义理解1.1 问句表示的重要性1.2 端到端网络的优势 2 知识图谱中的排序问题2.1 知识图谱的核心作用2.2 查询匹配的转化与排序问题2.3 实体链接的关键性2.4 路径的构建与系统优化 3 难点与挑战3.1 实体链接、命名实体识别和消歧3.2 排序模型的挑战 4 优…

泛娱乐社交出海洞察,Flat Ads解锁海外增长新思路

摘要:解读泛娱乐社交应用出海现状与趋势,解锁“掘金”泛娱乐社交出海赛道新思路。 根据全球舆情监测机构 Meltwater 和社交媒体机构We are Social最新发布数据显示,全球社交媒体活跃用户数量已突破50亿,约占世界人口总数62.5%。庞大的用户数量意味着广阔的增量空间,目前,随着全…

板块零 IDEA编译器基础:第三节 下载和在IDEA中集成 Tomcat服务器 来自【汤米尼克的JAVAEE全套教程专栏】

板块零 IDEA编译器基础&#xff1a;第三节 下载和在IDEA中集成 Tomcat服务器 一、为什么选择Tomcat&#xff08;1&#xff09;常见的JAVA WEB服务器&#xff08;2&#xff09;选择Tomcat的理由 二、Tomcat 8.5下载解压三、Tomcat 结构目录四、在IDEA中集成Tomcat 假设我们已经…

23.HarmonyOS App(JAVA)堆叠布局StackLayout使用方法

不常用 StackLayout直接在屏幕上开辟出一块空白的区域&#xff0c;添加到这个布局中的视图都是以层叠的方式显示&#xff0c;而它会把这些视图默认放到这块区域的左上角&#xff0c;第一个添加到布局中的视图显示在最底层&#xff0c;最后一个被放在最顶层。上一层的视图会覆盖…

过年回家必备,向日葵远程控制帮您假期轻松“应急”

春节假期临近&#xff0c;无论是回家过年还是假期出游&#xff0c;我们都可能遇到突然的“业务”需要临时处理&#xff0c;可是电脑没在身边又怎么办呢&#xff1f;别慌&#xff0c;我们可以用向日葵远程控制。 通过使用向日葵远程控制&#xff0c;手机就能远程控制办公电脑&a…

微信小程序(三十六)事件传参

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.传参步骤 2.传参接收解构步骤 源码&#xff1a; index.wxml <button type"primary" bind:tap"onclick" mark:index"{{0}}" mark:remb"{{1}}" class"But&quo…

DataX详解和架构介绍

系列文章目录 一、 DataX详解和架构介绍 二、 DataX源码分析 JobContainer 三、DataX源码分析 TaskGroupContainer 四、DataX源码分析 TaskExecutor 五、DataX源码分析 reader 六、DataX源码分析 writer 七、DataX源码分析 Channel 文章目录 系列文章目录DataX是什么&#xff…

在WORD中设置公式居中编号右对齐设置方式

1 软件环境 Office Microsoft Office LTSC 专业增强版2021 2 最终效果 3 操作步骤 编辑公式&#xff1b;光标定位到公式的最后&#xff08;不是行的最后&#xff09;&#xff1b;输入#编号光标定位在公式最后&#xff08;不是行的最后&#xff09;&#xff0c;按Enter键回车…

(五)springboot 配置多数据源连接mysql和hive

项目结构如下 mysql 执行如下建表语句&#xff0c;并插入一条测试数据 1 2 3 4 5 CREATE TABLE user ( id int(11) NOT NULL, name varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_ge…

Java实现网上药店系统 JAVA+Vue+SpringBoot+MySQL

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 药品类型模块2.3 药品档案模块2.4 药品订单模块2.5 药品收藏模块2.6 药品资讯模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 角色表3.2.2 药品表3.2.3 药品订单表3.2.4 药品收藏表3.2.5 药品留言表…

【Java安全】ysoserial-URLDNS链分析

前言 Java安全中经常会提到反序列化&#xff0c;一个将Java对象转换为字节序列传输&#xff08;或保存&#xff09;并在接收字节序列后反序列化为Java对象的机制&#xff0c;在传输&#xff08;或保存&#xff09;的过程中&#xff0c;恶意攻击者能够将传输的字节序列替换为恶…

D盘不见了如何恢复?4个恢复方法(新版)!

“很奇怪&#xff01;我的电脑d盘不知道为什么突然不见了&#xff0c;我还保存了很多重要的文件在里面呢&#xff0c;有什么恢复d盘的方法吗&#xff1f;” 在我们的日常生活中&#xff0c;电脑已经成为了我们工作、学习和娱乐的重要工具。然而&#xff0c;有时候我们会遇到一些…