【C语言】指针(三)

目录

一、字符指针

1.1 ❥ 使用场景

1.2 ❥ 有关字符串笔试题

二、数组指针

2.1 ❥ 数组指针变量

2.2 ❥ 数组指针类型

2.3 ❥ 数组指针的初始化

三、数组指针的使用

3.1 ❥ 二维数组和数组名的理解

3.2 ❥ 二维数组传参

四、函数指针

4.1 ❥ 函数的地址

4.2 ❥ 函数指针变量

4.3 ❥ 函数指针变量的使用

五、函数指针数组

六、转移表


一、字符指针

字符指针:指向字符的指针 

1.1 ❥ 使用场景

【使用场景一】

#include <stdio.h>int main()
{char c = 'w';char* pc = &c;*pc = 'x';printf("%c\n", *pc);return 0;
}

【使用场景二】

#include <stdio.h>int main()
{char* p = "abcdef";//这里把首字符a的地址赋给了变量pprintf("%s\n", p);return 0;
}

注意

这里printf中%s的格式逻辑是:从给定的地址处开始,逐个向后输出字符,直到遇见结束标记\0为止。解引用的操作符在printf函数内部造成。

如果由用户解引用,那printf函数将只能拿到单个字符,反而无法实现功能。

易错点1:

场景二中如果修改*p的值,代码就会报错。报错类型如下:

原因:

"abcdef"是个常量字符串。

常量字符串的意思是:这个字符串本身是不能被更改的。

而这个*p没有被限制,它其实是可以去改变后面的字符串的,所以char* p="abcdef"; 报警告是正常的。

防止方法:在char*p 前加const

易错点2:

不能解引用p,解引用打印的是一个字符,一个字符不能用%s打印。

char* p = "abcdef";
printf("%s", *p);//error

以下是将字符串放在数组里面:

char arr[] = "abcdef";

而数组的内容是可变的。

1.2 ❥ 有关字符串笔试题

#include <stdio.h>int main()
{char arr1[] = "hello";char arr2[] = "hello";const char* p1 = "hello";const char* p2 = "hello";if (arr1 == arr2)printf("arr1=arr2\n");elseprintf("arr1!=arr2\n");if (p1 == p2)printf("p1=p2\n");elseprintf("p1!=p2\n");return 0;
}

运行结果如下:

原因如下:

arr1!=arr2

  1. 我们知道:arr1和arr2是数组首元素的地址,这两个数组是两块独立的内存空间,它们只是存储的内容相同,都是hello字符串。

p1=p2

  1. 这里的p1和p2指向的是同一个常量字符串。c/c++会把常量字符串存储到一个单独的一个内存区域,当几个指针指向同一个字符串的时候,它们实际会指向同一块内存(代码段中)。
  2. arr1,arr2,p1,p2都是放在栈区,指向的hello(常量字符串)是放在代码段。

二、数组指针

整型指针:存放整型变量的地址,能够指向整型数据的指针

浮点型指针:存放浮点型变量的地址,能够指向浮点型数据的指针

数组指针:存放数组的地址,指向数组的指针

2.1 ❥ 数组指针变量

看下面两行代码,p1,p2分别是什么?

int* p1[10];
int(*p2)[10];
  • p1是指针数组

p1是数组名,该数组里存放了10个元素,每个元素是int*类型

  • p2是数组指针

因为p2先和*结合,说明p是一个指针变量,然后指向一个大小为10个整型的数组,所以p是一个指针,指向一个数组,所以叫数组指针。

注意:

  1. [ ]的优先级要高于*号,所以必须加上()来保证p先和*结合。
  2. int(*p2)[10];里面的*不是解引用的意思,这颗星就代表是指针,只有前面不加类型的时候才是解引用。

2.2 ❥ 数组指针类型

去掉指针变量名就是指针(变量)的类型。

看如下代码:

它们跳过的字节不同就是因为他们的类型不同导致。

注意:

  1. 一个指针是否是野指针取决于你是否用它。这个指针虽然指向这里,但是没有产生坏的结果。只要不使用它就没关系。
  2. 虽然它指向的空间不属于“我”,但是它并不危险。

2.3 ❥ 数组指针的初始化

数组指针存放的是数组的地址。

所以初始化的时候要给整个数组的地址。代码如下:

注意:

[ ]里的元素个数不能省略,不然编译器会自动认为是数组元素个数为0

三、数组指针的使用

3.1 ❥ 二维数组和数组名的理解

首先我们来理解一下二维数组及其数组名:

在c语言中,只有一维数组(N维数组的元素是数组),数组名作为指针时永远指向第一个元素。

如:

  • 数组a[3];  *a=a[0]
  • 数组a[3][4];  *a=a[0]   只不过这时候a[0]又是一个数组。

这时候的a[0]又是指向它自己元素的第一个元素,又有 *a[0]=a[0][0]

  • 这种方式可以推广到N维数组,所有数组直接对数组名取地址(如:&a),得到的指针指向该数组,而不是指向第一个元素。注意这点区别。

举个例子:  int board[3][4];

board:一维数组的地址。

二维数组的数组名,数组名就是首元素地址。我们知道,可以把一维数组看作二维数组的元素。所以,board就是一维数组的地址。

&board:取出的是整个二维数组的地址。

board[0]:第一行第一个元素的地址。

解引用,相当于拿到第一行数组的数组名,也就是首元素地址,即第一行第一个元素的地址。

board[0]=*board=&board[0][0]

&board[0]:第一行的地址。

board=&board[0]

3.2 ❥ 二维数组传参

清楚了上面的概念之后,我们来看下面一段代码:

之前二维数组传参时,形参部分用的是数组接收。

#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;
}

发现:实参arr是数组名,通过刚才的分析,直到数组名是首元素地址,首元素地址就是第一行的地址,也就是一维数组的地址,那么它的类型就是数组指针类型。

那么实参就可以写成数组指针的形式,代码如下:

#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;
}

问题:为什么*(p+i) 跳过的是一行数组?

回答:

数组的类型决定了它+1跳过几个字节。

p的类型是:int(*)[5];

p是指向一个整型数组的,数组5个元素 int [5]

p+1 :跳过一个5个int元素的数组

四、函数指针

函数指针:存放函数的地址,指向函数的指针

4.1 ❥ 函数的地址

函数是否有地址呢?我们来测试一下:

由测试结果可知:函数存在地址,取&函数名函数名拿到的都是函数的地址。

4.2 ❥ 函数指针变量

我们通过函数指针来存储函数的地址。

int (*pf) (int x, int y) = &Add;
int (*pf) (int , int ) = Add;//xy可以省略,只写类型
  1. 地址要存起来,放到(指针)变量里去。
  2. pf是变量名,*pf说明是指针,指向的是函数,所以加上括号()。
  3. 函数的参数是int (参数名写不写无所谓,只要类型交代清楚即可),函数的返回值类型也是int。
int (*pf) (int x, int y)
|      |    ------------
|      |        |
|      |        pf指向函数的参数类型和个数的交代
|       函数指针变量名
pf指向函数的返回类型int (*) (int x, int y) //pf函数指针变量的类型

4.3 ❥ 函数指针变量的使用

通过函数指针调用指针指向的函数。

代码如下:

#include <stdio.h>
int Add(int x, int y)
{return x + y;
}
int main()
{int(*pf)(int, int) = Add;printf("%d\n", (*pf)(2, 3));//输出结果为5printf("%d\n", pf(2, 3));//输出结果为5return 0;
}

把函数的地址存到pf里,通过解引用pf找到函数,找到这个函数要调用这个函数,调用函数需要传参,所以();(传参),传2,3。这样的话它会把2和3相加,得到5。

问题一:为什么*pf必须带上括号()呢?

回答:因为假如不带上括号,调用返回5,就会对5进行解引用。

问题二:为什么能写成pf(2, 3)这种形式?

回答:在C语言里,pf前的*其实是个摆设,可以不写,也可以写多个。

这是个技术细节问题,不涉及到语法原则,从不同的思考角度出发,观点会略有不同,但不影响C语言实践,初学者也不必过多纠结。

应用:通过函数指针的方式进行调用

#include <stdio.h>int Add(x, y)
{return x + y;}
void cale(int(*pf)(int,int))
{int a = 3;int b = 5;int ret = pf(a, b);printf("%d\n", ret);
}
int main()
{cale(Add);return 0;
}

这里的cale没有直接调用Add函数,而是通过函数指针的方式进行调用。

五、函数指针数组

函数指针数组:把一个函数的地址存放到一个数组中。

是个函数指针类型的数组。

去掉 函数名+[ ] 就是该数组的类型。

int (*parr[3])( );

解释:parr先和[ ]结合,说明parr是数组,数组的内容是int(*)()类型的函数指针。

当对函数指针数组进行初始化的时候,后面初始化的可以省略掉数组的大小,它会根据后面初始化的内容来确定数组的大小。

例如:

int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; 
int(*p[])(int x, int y) = { 0, add, sub, mul, div };

六、转移表

使用了函数指针数组,避免大篇幅地修改内容;也可实现跳转的功能。

所以函数指针数组也叫:转移表。

计算机的一般实现

#define _CRT_SECURE_NO_WARNINGS 1#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.rhkb.cn/news/330599.html

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

相关文章

知识分享:大数据信用花导致的评分不足多久能恢复

随着金融风控领域越来越科技化&#xff0c;基于大数据技术的金融风控成为了贷前风控不可或缺的重要环节&#xff0c;相信很多人在申贷的时候都听说过大数据信用和综合评分等词语&#xff0c;那大数据信用花导致的评分不足多久能恢复呢?本文带大家一起去了解一下。 首先&#x…

【面试干货】矩阵对角线元素之和

【面试干货】矩阵对角线元素之和 1、实现思想2、代码实现 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 1、实现思想 创建一个3x3的二维数组来表示输入的矩阵。通过嵌套循环读取输入的矩阵元素&#xff0c;并将其保存到数组中。再次嵌套循…

C++:vector基础讲解

hello&#xff0c;各位小伙伴&#xff0c;本篇文章跟大家一起学习《C&#xff1a;vector基础讲解》&#xff0c;感谢大家对我上一篇的支持&#xff0c;如有什么问题&#xff0c;还请多多指教 &#xff01; 如果本篇文章对你有帮助&#xff0c;还请各位点点赞&#xff01;&#…

抖音跳转微信卡片制作教程 小白也能搞

实测可以正常跳转&#xff0c;很牛逼&#xff0c;给大家分享一下~ 这是我做出来抖音发出去的效果&#xff0c;大家会制作了可以去卖钱&#xff0c;市场上一个这个卡片都要卖50-200&#xff0c;很不错的&#xff01;&#xff01; https://pan.baidu.com/s/1xPmGAWPcbAp7eXg7Dc…

若依 ruoyi-vue 用户账号前后端参数校验密码 手机号 邮箱

前端 <el-dialog :title"title" :visible.sync"open" width"800px" append-to-body><el-form ref"form" :model"form" :rules"rules" label-width"120px"><el-row><el-col :span…

【竞技宝】欧洲杯:吉鲁退出法国队,欧洲杯后主动让贤

吉鲁是法国队功勋中锋&#xff0c;为球队立下过赫赫战功。法国队能在2018年拿到久违的世界杯冠军&#xff0c;吉鲁身为主力锋霸功不可没。每当&#xff0c;法国队在比赛中遇到僵局&#xff0c;吉鲁总会站出来&#xff0c;为球队做出应有的贡献。吉鲁在法国队的作用不仅仅体现在…

Day48 Javascript详解

Day48 Javascript详解 文章目录 Day48 Javascript详解一、什么是javascript二、javascript特点三、 Javascript的历史四、Javascript vs Java五、JS的基本数据类型六、JS基本数据类型的特殊点七、数组 一、什么是javascript JavaScript是一种高级的、解释型的编程语言&#xf…

【论文速读】|探索ChatGPT在软件安全应用中的局限性

本次分享论文&#xff1a;Exploring the Limits of ChatGPT in Software Security Applications 基本信息 原文作者&#xff1a;Fangzhou Wu, Qingzhao Zhang, Ati Priya Bajaj, Tiffany Bao, Ning Zhang, Ruoyu "Fish" Wang, Chaowei Xiao 作者单位&#xff1a;威…

[Linux] 进程概念

目录 1.冯诺依曼硬件体系结构 2.操作系统&#xff08;OS&#xff09; 3.系统接口 4.进程的概念 5.进程状态 6.四个其他概念 7.环境变量 8.进程地址空间 1.冯诺依曼硬件体系结构 在冯诺依曼体系结构中&#xff0c;计算机是由输入、输出、存储设备和中央处理器cpu组成的。图中体结…

子比主题ACG美化插件[全开源]

WordPress插件是一种可以扩展和增强WordPress网站功能的应用程序。子比主题ACG美化插件听起来像是一个专门为ACG&#xff08;动画、漫画、游戏&#xff09;爱好者设计的美化插件&#xff0c;它可能包含多种功能来改善网站的外观和用户体验。 内置功能开关100意味着这个插件提供…

【电子学会】2023年09月图形化一级 -- 芝麻开门

芝麻开门 1. 准备工作 &#xff08;1&#xff09;删除小猫角色&#xff0c;添加角色Key&#xff1b; &#xff08;2&#xff09;删除白色背景&#xff0c;添加背景Castle 1和Pathway。 2. 功能实现 &#xff08;1&#xff09;点击绿旗&#xff0c;钥匙在舞台中间&#xff…

机械臂与Realsense D435 相机的手眼标定ROS包

本教程主要介绍机械臂与 Realsense D435 相机手眼标定的配置及方法。 系统&#xff1a;Ubuntu 20.0.4 ◼ ROS&#xff1a;Noetic ◼ OpenCV 库&#xff1a;OpenCV 4.2.0 ◼ Realsense D435&#xff1a;librealsense sdk&#xff08;2.50.0&#xff09;、realsense-ros 功能包&…

基于CNN卷积神经网络的金融数据预测matlab仿真,对比BP,RBF,LSTM

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1 反向传播网络&#xff08;BP&#xff0c;多层感知器MLP&#xff09; 4.2 径向基函数网络&#xff08;RBF&#xff09; 4.3 卷积神经网络&#xff08;CNN&#xff09; 4.4 长短期记忆网…

ST-SLAS Technology 实验室自动化与筛查学会技术

文章目录 一、期刊简介二、征稿信息三、期刊表现四、投稿须知五、出版支持 一、期刊简介 SLAS Technology ——SLAS技术强调促进和改进生命科学研发的科学和技术进步;药物递送;诊断;生物医学和分子成像&#xff1b;以及个性化和精准医疗。这包括高通量和其他实验室自动化技术;…

网络信息安全

目录 引言 网络信息安全的基本概念 定义 主要目标 网络信息安全的范围 主要威胁 恶意软件 黑客攻击 拒绝服务攻击 社交工程 内部威胁 常用技术和防护措施 加密技术 防火墙 入侵检测和防御系统 访问控制 多因素认证 安全审计和监控 安全培训和意识提升 未来发…

【软件工程】【23.10】p3

关键字&#xff1a; 软件工程定义及目的、需求规约定义及性质、模块的控制域及作用域、类和类图、调试特征、瀑布模型

JAVA 6

这次我学习了第六次Java课程 这次课程我学习了新的理论 面向对象 对象可以是任何事物Object 在Java中对象可以具体为类 这便是许多的类 类 分为两个大方面 属性 方法 在代码中还有一个部分那就是运行代码 在类中的属性和方法也是有所不同 public-公开 任何代码都能访…

初识C语言——第二十八天

代码练习1&#xff1a; 用函数的方式实现9*9乘法表 void print_table(int n) {int i 0;int j 0;for (i 1; i< n; i){for (j 1; j< i; j){printf("%d*%d%-3d ", i, j, i * j);}printf("\n");}}int main() {int n 0;scanf("%d", &a…

hive初始化失败报错:Error: Duplicate key name ‘PCS_STATS_IDX‘ (state=42000,code=1061)

意思是key name ‘PCS_STATS_IDX’ (state42000,code1061)重复了&#xff0c;问题出在不是第一次初始化&#xff0c;因为我们在hive-site.xml中配置了 javax.jdo.option.ConnectionURL jdbc:mysql://192.168.200.137:3306/metastore?createDatabaseIfNotExisttrue JDBC conne…

JavaScript初识2

目录 JS语言是解释型语言 一、运算符 1.赋值运算符 2.一元运算符 3.比较运算符 4.逻辑运算符 5.运算符优先级 二、语句 1.表达式和语句 2.分支语句 &#xff08;1&#xff09;If 语句 &#xff08;2&#xff09;三元表达式 &#xff08;3&#xff09;switch 语句 …