c语言-浅谈指针(3)

文章目录

    • 1.字符指针变量
      • 常见的字符指针初始化
      • 另一种字符指针初始化
      • 例:
    • 2.数组指针变量
      • 什么是数组指针变量
      • 数组指针变量创建
      • 数组指针变量初始化
      • 例(二维数组传参的本质)
    • 3.函数指针变量
      • 什么是函数指针变量呢?
      • 函数指针变量创建
      • 函数指针使用
      • typedef关键字
    • 4.函数指针数组
      • 概念
      • 创建
      • 通过函数指针数组实现计算器


1.字符指针变量

常见的字符指针初始化

字符指针 char *

常见的初始化:

int main() {
	char a = 'w';
	char* p = &a;
	return 0;
}

跟 int *类型的初始化一样,下面介绍另一种字符指针的初始化

另一种字符指针初始化

这种是将一个字符串直接赋给字符指针变量

如:

char* p = "abcdefg";

那么它能不能像一般的字符数组那样直接打印出来呢,我们通过代码看看:

int main() {
	char a[] = "abcdefg";
	char* p = "abcdefg";
	printf("%s", a);//打印字符数组
	printf("%s", *p);//打印指针初始化出来的字符串
	return 0;
}

运行结果:
![在这里插入图片描述](https://img-blog.csdnimg.cn/35a2012446a34c129a0a10bb3d6b4703.png

由结果看出:只有字符数组打印出来了,而字符指针没打印出来,这是为什么呢? 其实是因为这样初始化只是把字符串的首个字符的地址传给字符指针变量 p ,这样的话 *p=a。 在这里%s是打印字符串的,所以打印不了一个字符。

那我们用打印一个字符的 %c 试试

int main() {
	char a[] = "abcdefg";
	char* p = "abcdefg";
	printf("%s\n", a);
	printf("%c", *p);
	return 0;
}

运行结果
在这里插入图片描述
字符指针只打印一个首字符,验证了首个字符的地址传给字符指针变量 p

那么我们可以用循环的方式将这个字符串打印出来

int main() {
	char* p = "abcdefg";
	int s = strlen(p);//求字符长度
for(int i=0;i<s;i++)
	printf("%c", *(p+i));//将字符一一打印出来
	return 0;
}

运行结果
在这里插入图片描述

例:

这是《剑指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");
 else
 printf("str1 and str2 are not same\n");
 
 if(str3 ==str4)
 printf("str3 and str4 are same\n");
 else
 printf("str3 and str4 are not same\n");
 
 return 0;}

答案:
在这里插入图片描述

分析:
1.这道题其实就是比较首地址 2这⾥str3和str4指向的是⼀个同⼀个常量字符串。C/C++会把常量字符串存储到单独的⼀个内存区域,当⼏个指针指向同⼀个字符串的时候,他们实际会指向同⼀块内存。但是⽤相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。

2.数组指针变量

数组指针是指针不是数组

什么是数组指针变量

我们通过其他类型的指针进行类比一下 整形指针变量: int * pint; 存放的是整形变量的地址,能够指向整形数据的指针。 浮点型指针变量: float * pf; 存放浮点型变量的地址,能够指向浮点型数据的指针。 那么数组指针变量就是存放数组地址,指向数组的指针变量吧

数组指针变量创建

创建:int (*p)[10]
解释:p先和*结合,说明p是⼀个指针变量变量,然后指着指向的是⼀个⼤⼩为10个整型的数组。所以p是⼀个指针,指向⼀个数组,叫数组指针。
注意:[ ]的优先级要于 * 号的,所以必须加上()来保证p先和 * 结合。

数组指针变量初始化

数组指针是用来存放数组的指针的,那么我们将数组取地址在赋给数组指针变量即可
如:

int arr[10] = { 1,2,3,4,5,7,8,9,0.6 };
int(*p)[10] = &arr;

解释:
在这里插入图片描述
图解(一维数组)
在这里插入图片描述

例(二维数组传参的本质)

在数组里面的二维数组可以看成一个一维数组,只不过是每个元素就是一个数组而已,所以,根据数组名是数组元素的地址这个规则,⼆维数组的数组名表⽰的就是第⼀⾏的地址,是⼀维数组的地址。

在这里插入图片描述
1.通过二维数组接收参数
这是一般的方法

void qin(int arr[2][5] ){
	for (int i = 0; i < 2; i++) {
		for (int j = 0; j < 5; j++)
			printf("%d ", arr[i][j]);
	}
	printf("\n");
}
int main() {
	int arr[2][5] = { 1,2,3,4,5,6,7,8,9,0 };
	qin(arr);

	return 0;
}

2.通过数组指针来了解二维数组的本质

void qin(int(*p)[5]) {//*(p+1)==arr[0]+j
	for (int i = 0; i < 2; i++) {
		for (int j = 0; j < 5; j++)
			printf("%d ", (*(p + i))[j]);//  
			//(*p)==*(p+i),当i 加1时相当于跳过二维数组中的一个一维数组,
			//当然跟据前面的知识也可以写成这样 *(*(p+i)+j)
}
	printf("\n");
}
int main() {
	int arr[2][5] = { 1,2,3,4,5,6,7,8,9,0 };
	qin(arr);//在前面指针内容里说过,数组名就是首元素的地址,
	//那么我们这里把二维数组当成一维数组,那么首元素不就是一整个一维数组的地址吗

	return 0;
}

图解:
在这里插入图片描述
总结:⼆维数组传参,形参的部分可以写成数组,也可以写成指针形式。

3.函数指针变量

什么是函数指针变量呢?

数组指针是用来存数组的地址的那么函数指针呢?
其实函数指针也是用来存地址的,存的是函数的地址,这里就有很多人有疑问了,函数有地址?
那么我们来做个测试:

int add(int x, int y) {
	return x + y;
}
int main() {
	printf("%p", &add);
	return 0;
}

运行结果:
在这里插入图片描述
很明显函数是有地址

这里还有一个注意的是函数名和&函数名都是代表函数的地址,如:add==&add

我们来测试一下吧

int add(int x, int y) {
	return x + y;
}
int main() {
	printf("&add=%p\n", &add);
	printf("add=%p", add);
	return 0;
}

运行结果:
在这里插入图片描述
耶,是一样的

函数指针变量创建

创建:int (*p) (int, int)
在这里插入图片描述

函数指针使用

我们用函数指针来实现一下加法

int add(int x, int y) {
	return x + y;
}
int main() {
	int (*p)(int, int) = add;
	int a = 3, b = 9;
	printf("%d\n", p(a, b));//这里不用 * 也可以因为函数调用的时候本身就是用地址去调用
	printf("%d\n", (*p)(a, b));
	return 0;
}

运行结果:
在这里插入图片描述

typedef关键字

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

如:

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

那么如何来重命名指针类型呢

typedef  int(*)(int ,int)   te  //错误
typedef  int(*te)(int, int)   //正确命名,将重命名放到原来放函数指针名称的位置
typedef  (*te)[10]//数组指针重命名

4.函数指针数组

概念

把函数的地址存到⼀个数组中,那这个数组就叫函数指针数组(就是将函数指针放到一个数字里)

创建

int (*) (intint)//函数指针类型
int  (*p[10])(int ,int)//函数指针数组--p先和[]结合形成数组,再和函数指针类型结合形成函数指针数组

通过函数指针数组实现计算器

我们这里要实现一个简单的计算器,分别有加法、减法、乘法、除法,那么我们就要实现四个函数来实现,然后还要一个选择来选择实行什么计算,最后还需要一个菜单就完成了

我们先用一般的方法来实现计算器:

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

使⽤函数指针数组的实现:

我们将这4个函数都放到一个函数指针数组里,当我们需要实行什么计算就通过什么下标来访问这个函数

#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 }; 
 //储存函数地址,这里的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);//输入下标
 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" ); 
 }

以上就是我的分享
谢谢大家的观看!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/161947.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringSecurity6 | 自动配置(下)

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; Java从入门到精通 ✨特色专栏&#xf…

我叫:冒泡排序【JAVA】

1.什么是冒泡排序&#xff1f; 冒泡排序(Bubble Sorting)的基本思想是:通过对待排序序列从前向后&#xff08;从下标较小的元素开始)&#xff0c;依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就象水底下的气泡一样逐渐向上冒。 2.来个实战应用 我们…

从键盘输入5个学生的信息(姓名、学号、成绩), 存入一个结构体数组中,计算平均分,并按成绩 高低排序并输出.

代码如下 #include<stdio.h> #include<string.h> #include<stdlib.h> /* 1.练习结构体数组排序   从键盘输入5个学生的信息&#xff08;姓名、学号、成绩&#xff09;,存入一个结构体数组中&#xff0c;计算平均分&#xff0c;并按成绩高低排序并输出. */…

python学习:break用法详解

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 在执行while循环或者for循环时&#xff0c;只要循环条件满足&#xff0c;程序会一直执行循环体。 但在某些场景&#xff0c;我们希望在循环结束前就强制结束循环。 Python中有两种强制结束循环的方法&#xff1a; continue语…

heatmap | cell cycle genes in Seurat

目的&#xff1a;使用bulk 数据&#xff0c;查看HeLa 双胸苷阻断法 细胞同步化 释放 [0, 3, 4.5, 6, 9, 10.5, 12, 15, 18, 19.5, 21, 22.5, 25.5, 30] 小时后 cell cycle 基因的表达情况。 1.结果 S phase G2M phase S G2M phase 不方便看&#xff0c;横过来看&#xff1a;…

Linux下运行Jmeter压测

一、在Linux服务器先安装SDK 1、先从官网下载jdk1.8.0_131.tar.gz&#xff0c;再从本地上传到Linux服务器 2、解压&#xff1a;tar -xzf jdk1.8.0_131.tar.gz&#xff0c;生成文件夹 jdk1.8.0_131 3、在/usr/目录下创建java文件夹&#xff0c;再将 jdk1.8.0_131目录移动到/u…

【操作系统】磁盘物理地址怎么表示

常见主存物理地址是一串01串&#xff0c;那磁盘物理地址呢&#xff1f; 磁盘物理地址由以下组成&#xff1a; 柱面号磁头号扇区号 重点知识点辨析&#xff1a; 磁盘物理地址的翻译是由磁盘驱动程序进行的&#xff0c;目的是将逻辑上的蔟号转化为上述的物理地址 408真题溯源…

harmonyOS鸿蒙开发工具下载安装以及使用流程

注册账号 进入鸿蒙官方网站&#xff1a;https://www.harmonyos.com/ 推荐使用手机号注册 进行实名认证 下载开发环境 华为集成开发环境IDE DevEco Device Tool下载 | HarmonyOS设备开发 下载开发工具 HUAWEI DevEco Studio和SDK下载和升级 | HarmonyOS开发者 安装 无脑下一…

使用共享内存进行通信的代码和运行情况分析,共享内存的特点(拷贝次数,访问控制),加入命名管道进行通信的代码和运行情况分析

目录 示例代码 头文件(comm.hpp) log.hpp 基础版 -- 服务端 代码 运行情况 加入客户端 代码 运行情况 两端进行通信 客户端 代码 注意点 服务端 代码 两端运行情况 共享内存特点 拷贝次数少 管道的拷贝次数 共享内存的拷贝次数 没有访问控制 管道 共享…

Spring学习③__Bean管理

目录 IOC接口ApplicationContext 详解IOC操作Bean管理基于xml方式基于xml方式创建对象基于xml方式注入属性使用set方法进行注入通过有参数的构造进行注入p 名称空间注入&#xff08;了解&#xff09; 基于xml方式注入其他类型属性xml 注入数组类型属性 IOC接口 IOC思想基于IOC…

龙芯 操作系统选择和安装

龙芯3a5000及之后的cpu底层架构已经从mips64el改为了loongarch64 所以这里分了2种来说明&#xff0c;分别对应3a4000之前的和3a5000之后的 龙芯的系统安装难点在于操作系统的选取和引导 一、烧录工具 制作安装盘使用常规的烧录工具是不行滴&#xff0c;会提示没有\boot\initrd…

Vue 路由缓存 防止路由切换数据丢失 路由的生命周期

在切换路由的时候&#xff0c;如果写好了一丢数据在去切换路由在回到写好的数据的路由去将会丢失&#xff0c;这时可以使用路由缓存技术进行保存&#xff0c;这样两个界面来回换数据也不会丢失 在 < router-view >展示的内容都不会被销毁&#xff0c;路由来回切换数据也…

【SpringBoot3+Vue3】四【实战篇】-前端(vue基础)

目录 一、项目前置知识 二、使用vscode创建 三、vue介绍 四、局部使用vue 1、快速入门 1.1 需求 1.2 准备工作 1.3 操作 1.3.1 创建html 1.3.2 创建初始html代码 1.3.3 参照官网import vue 1.3.4 创建vue应用实例 1.3.5 准备div 1.3.6 准备用户数据 1.3.7 通过…

DPDK初始化

rte_eal_init │ ├──rte_cpu_is_supported&#xff1a;检查cpu是否支持 │ ├──rte_atomic32_test_and_set&#xff1a;操作静态局部变量run_once确保函数只执行一次 │ ├──pthread_self() 获取主线程的线程ID,只是用于打印 │ ├──eal_reset_internal_config&#x…

UE 程序化网格 计算横截面

首先在构造函数内加上程序化网格&#xff0c;然后复制网格体到程序化网格组件上&#xff0c;将Static Mesh&#xff08;类型StaticMeshActor&#xff09;的静态网格体组件给到程序化网格体上 然后把StaticMesh&#xff08;类型为StaticMeshActor&#xff09;Instance暴漏出去 …

异地工业设备集中运维、数据采集,一招搞定

为了提升运维效率&#xff0c;能够及时发现和响应设备的故障、异常和潜在问题。 越来越多的企业都在搭建“集中式”的远程智慧运维体系&#xff0c;以提高运维效率和降低成本。 异地工业设备远程运维&#xff0c;提升响应效率、降低运维成本 以国内陕西某机床公司为例&#xff…

在vmware中给linux添加硬盘

1.必须在断开linux电源的情况下&#xff0c;才能添加硬盘成功。注&#xff1a;自己好几次在开机状态下添加硬盘都失败了&#xff0c;然后关机后&#xff0c;又试了下&#xff0c;居然成功了。

Python (十一) 迭代器与生成器

迭代器 迭代器是访问集合元素的一种方式&#xff0c;可以记住遍历的位置的对象 迭代器有两个基本的方法&#xff1a;iter() 和 next() 字符串&#xff0c;列表或元组对象都可用于创建迭代器 字符串迭代 str1 Python str_iter iter(str1) print(next(str_iter)) print(next(st…

Docker入门学习笔记

学习笔记网址推送&#xff1a;wDocker 10分钟快速入门_哔哩哔哩_bilibili docker是用来解决什么问题的&#xff1f; 例如当你在本地主机写了个web应用&#xff0c;而你打算将该应用发送给其他客户端进行案例测试和运行&#xff0c;若是传统做法&#xff0c;就比较复杂&#xf…

《轻购优品》新零售玩法:消费积分认购+众筹新玩法

《轻购优品》新零售玩法&#xff1a;消费积分认购众筹新玩法 引言&#xff1a;2023年开年已来&#xff0c;政府的工作报告提出“把恢复和扩大消费摆在优先位置”&#xff0c;并且把2023年定位为“消费提振年”&#xff0c;以“全年乐享全年盛惠”为主题多地政府共同发力&#x…