#include<初见C语言之指针(3)>

目录

一、字符指针变量

二、数组指针变量

1.什么是数组指针变量?

2.数组指针怎么初始化?

三、二组数组传参的本质

四、函数指针变量

1.什么是函数指针变量?

2.函数指针变量使用 

 3.有趣代码

 3.1typedef关键字

五、函数指针数组

六、转移表

总结


一、字符指针变量

字符指针变量的指针类型是char*;

一般使用:

int main()
{
    char ch = 'w';
    char* pc = &ch;
    printf("%c\n", ch);
    *pc = 'p';
    printf("%c\n", *pc);
    
    return 0;

}

%c是打印字符的,%s是打印字符串的首地址

其他用法:

int main()
{
    const char* p= "hello world";//常量字符串,是不能修改的
    printf("%c\n", *p);
    //*p = 'q';//err
    
    return 0;

}

很容易把代码hello world放在指针*p中,但是本质是把字符串的首地址放在*p中。

画图演示:

下面我们再来看看一段代码:

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

画图演示:

所以str1和str2不相同,str3和str4相同;(常量字符串,是不能修改的)


二、数组指针变量

1.什么是数组指针变量?

类比的方法:

  • 字符指针 char* p 指向字符的指针,存放的是字符地址
  • 整型指针 char* p 指向整型的指针,存放的是整型地址

所以数组指针是一种指针变量,是存放数组地址的指针变量

char ch = 'w';

char* pc =&ch;//字符指针

int n =10;

int* p =&n;//整型指针

数组指针变量

int (*p)[10];

解释:p先和*结合,说明p是一个指针变量,然后指向的是一个大小为10个整型的数组

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

2.数组指针怎么初始化?

在前面的学习中我们就知道,数组名是数组首元素的地址

int main()
{
    int arr[10] = { 10 };
    int(*p)[10] = &arr;//取出的是数组的地址
    //p应该是数组指针
    
    return 0;
}

&arr和p的类型是一致的 


三、二组数组传参的本质

二维数组的一般写法

void test(int arr[3][5],int r,int c)
{
    int i = 0;
    for (i = 0; i < r; i++)
    {
        int j = 0;
        for (j = 0; j < c; j++)
        {
            printf("%d ", arr[i][j]);
        }
    }
}


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

二维数组名是谁的地址?

根据数组名是数组⾸元素的地址这个规则

  1. 二维数组的首元素就是第一行(一维数组)
  2. 每一行都是一个元素(一维数组)

画图演示:

void test(int (*p)[5], int r, int c)
{
    int i = 0;
    for (i = 0; i < r; i++)
    {
        int j = 0;
        for (j = 0; j < c; j++)
        {
            //printf("%d ", (*p+i)[j]);
            printf("%d ", *(*(p+i)+j));// arr[i]==*(arr+i)
        }
        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;
}

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


四、函数指针变量

1.什么是函数指针变量?

类比:

  • 数组指针,指向数组,存放数组地址
  • 整型指针,指向整型,存放整型地址

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

int Add(int a, int b)
{
    return a + b;
}

int* test(char* s)
{
    return 0;
}
//
函数指针变量的写法和数组指针变量写法类似

int main()
{
    int*(*pt)(char) = &test;
    int arr[8] = { 0 };
    int(*pa)[8] = &arr;//pa是数组指针变量
    int (*pf)(int,int) = &Add;//pf是函数指针变量
    int x = 10;
    int y = 20;
    int z = Add(x,y);
    printf("%p\n", &Add);
    printf("%p\n", Add);


    return 0;
}

&函数名和函数名都是表示函数地址 

函数指针变量

int (*pf) (int x,int y);

解析: 

2.函数指针变量使用 

int Add(int a, int b)
{
	return a + b;
}

int main()
{
	int (*pf1)(int, int) = &Add;//pf就是函数指针变量
	int (*pf2)(int, int) = Add;

	int r1 = (*pf1)(3,7);
	int r2 = (*pf2)(3,7);//*可以
	int r4 = pf2(3,7);
	int r3 = Add(3, 7);

	printf("%d\n", r1);
	printf("%d\n", r2);
	printf("%d\n", r3);
	printf("%d\n", r4);

	return 0;
}

 3.有趣代码

代码1:

void (*p)();

int main()
{
    (*(void (*)())0)();//函数调用
    //1.将0强制类型转换成void(*)()类型的函数指针
    //2.调用0地址放的这个函数
    
    return 0;
}

 代码2

int main()
{
    void (*signal(int, void(*)(int)))(int);//函数声明
    //声明的函数名叫:signal
    //signal的函数有2个参数,第一个参数的类型是int
    //第二个参数的类型是void(*)(int)的函数指针类型,该指针可以指向一个函数,指向的函数参数是int,返回类型是void
    // signal函数的返回类型是void(*)(int)的函数指针,该指针可以指向一个函数,指向的函数参数是int,返回类型是void
    
    //void (*)(int)signal(int, void(*)(int));//err
    return 0;
}

 3.1typedef关键字

typedef对整型类型
typedef unsigned int uint;

int main()
{
    unsigned int num1;
    uint num2;
    return 0;
}


typedef对指针类型重命名
typedef int* pint;

int main()
{
    int* p1 = NULL;
    pint p2 = NULL;
    return 0;
}


数组指针重命名
typedef int(*parr_t)[5];
//*parr_t等价于int(*)[5]
int main()
{
    int arr[5] = { 10 };
    int(*p)[5] = &arr;//p是数组指针变量,p是变量的名字
    //int (*)[5] -- 数组指针类型
    parr_t p2 = &arr;
}
void test(char* s)
{

}


//对函数指针类型重命名产生新的类型pf_t
typedef void(*pf_t)(char*);

int main()
{
    void(*pf)(char*) = test;
    //void (*)(char*)
    pf_t pf2 = test;
}


五、函数指针数组

类比:

  • 字符指针数组 char* arr1[5];
  • 整型指针数组  int* arr2[5];

如果要把多个相同类型的函数指针存放在一个数组中,这个数组就叫:函数指针数组

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()
{
    int (*pf1)(int,int ) = add;
    int (*pf2)(int,int ) = sub;
    int (*pf3)(int,int ) = mul;
    int (*pf4)(int,int ) = div;


    int (*pfarr[4])(int, int) = { add,sub,mul,div };//pfarr就是函数指针数组
    int i = 0;
    for(i=0;i<4;i++)
    {
        int ret = pfarr[i](8,4);
        printf("%d\n", ret);
    }

    return 0;
}

int (*pfArr[5])(int, int) = { NULL,Add,Sub,Mul,Div };

解析:

 

六、转移表

我们这里写一个计算机,实现加减乘除法;

第一种写法:

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;
}
//想写一个计算器
//完成2个整数的运行
//1.加法
//2.减法
//3.乘法
//4.除法
void menu()
{
	printf("**************************\n");
	printf("*****1.Add     2.Sub******\n");
	printf("*****3.Mul     4.Div******\n");
	printf("*****     0.exit    ******\n");
	printf("**************************\n");
	printf("**************************\n");
	printf("**************************\n");
}
int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	do
	{
		
		menu();
		int input = 0;
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入2个操作数:");
			scanf("%d %d", &x, &y);
			ret = Add(x, y);
			printf("%d", ret);
			break;
		case 2:
			printf("请输入2个操作数:");
			scanf("%d %d", &x, &y);
			ret = Sub(x, y);
			printf("%d", ret);
			break;
		case 3:
			printf("请输入2个操作数:");
			scanf("%d %d", &x, &y);
			ret = Mul(x, y);
			printf("%d", ret);
			break;
		case 4:
			printf("请输入2个操作数:");
			scanf("%d %d", &x, &y);
			ret = Div(x, y);
			printf("%d", ret);
			break;
		case 0:
			printf("退出计算器\n");
			break;
		default:
			printf("选择错误,重新选择\n");
			break;
		}
	} while (input);
	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;
}
//想写一个计算器
//完成2个整数的运行
//1.加法
//2.减法
//3.乘法
//4.除法
void menu()
{
	printf("**************************\n");
	printf("*****1.Add     2.Sub******\n");
	printf("*****3.Mul     4.Div******\n");
	printf("*****     0.exit    ******\n");
	printf("**************************\n");
	printf("**************************\n");
	printf("**************************\n");
}
int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	//创建一个函数指针的数组
	//转移表
	int (*pfArr[5])(int, int) = { NULL,Add,Sub,Mul,Div };
	
	do
	{
		menu();
		int input = 0;
		printf("请选择:");
		scanf("%d", &input);
		if (input >= 1 && input <= 4)
		{
			printf("请输入2个操作数:");
			scanf("%d %d", &x, &y);
			pfArr[input](x, y);
		}
		else if (input == 0)
		{
			printf("退出计算机\n");
		}
		else
		{
			printf("选择错误\n");

		}

	} while (input);
	

	return 0;
}

这一种写法就看起来清爽很多,这样也可以添加更多的功能。

我们这里可以小结一下之前所学指针:

拓展:

char*(*(*p)[4])(int,char*) = &pfArr;//取出的是函数指针数组的地址

//p就是指向函数指针数组的指针;

所以我们就有第三种写法:

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;
}
想写一个计算器
完成2个整数的运行
1.加法
2.减法
3.乘法
4.除法
void menu()
{
	printf("**************************\n");
	printf("*****1.Add     2.Sub******\n");
	printf("*****3.Mul     4.Div******\n");
	printf("*****     0.exit    ******\n");
	printf("**************************\n");
	printf("**************************\n");
	printf("**************************\n");
}
void calu(int (*pf)(int,int))
{
	int x = 0;
	int y = 0;
	int ret = 0;
	printf("请输入2个操作数:");
	scanf("%d %d", &x, &y);
	ret = pf(x, y);
	printf("%d", ret);
}
int main()
{
	int input = 0;
	
	do
	{
		menu();
		int input = 0;
		printf("请选择:");
		scanf("%d", &input);

		switch (input)
		{
		case 1:
			calu(Add);
			break;
		case 2:
			calu(Sub);
			break;
		case 3:
			calu(Mul);
			break;
		case 4:
			calu(Div);
			break;
		case 0:
			printf("退出计算器\n");
			break;
		default:
			printf("选择错误,重新选择\n");
			break;
		}

	} while (input);

	return 0;
}

第二种是将函数指针数作为跳板、第三种写法我们都是将 取出函数指针数组的地址作为跳板


总结

指针的第三节主要是字符指针变量、数组指针变量、二维数组传参的本质、函数指针变量、函数指针数组以及转移表。

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

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

相关文章

深度学习03价值学习

Q*类似于先知&#xff0c;知道动作的后果 价值学习是得到一个近似的价值函数

【C语言_数组_复习篇】

目录 一、数组的概念 二、数组的类型 三、一维数组 3.1 一维数组的创建 3.2 一维数组的初始化 3.3 一维数组的访问 3.4 一维数组在内存中的存储 四、二维数组 4.1 二维数组的创建 4.2 二维数组的初始化 4.3 二维数组的访问 4.4 二维数组在内存中的存储 五、字符串数组 5.1…

SAP前台处理:物料主数据创建<MM01>之采购视图

一、背景&#xff1a; 终于来到了物料主数据&#xff0c;我觉得物料账是SAP最重要的一项发明&#xff0c;也一直是SAP的一项重要优势&#xff0c;物料账记录了一个个物料的生生不息&#xff1b; 本章主要讲解物料主数据和财务相关的主要内容&#xff1a;这里特别提示由于作者…

“先干后看”式循环

题目&#xff1a;计算 &#xff0c;直到最后一项小于 。 注意题目要求&#xff1a;计算到最后一项小于 。也就是这一项是要参与到计算中的&#xff0c;计算完这一项才能触发循环停止条件。 这种先运算后判断的情况很对do-while循环的胃口。 do就是做嘛&#xff0c;while就是…

扇区架次数动态展示

打开前端Vue项目&#xff1a;kongguan_web&#xff0c;完成前端src/components/echart/SectorFlightChart.vue页面设计&#xff0c;使用ECharts插件实现柱状图和饼状图 在src/components目录下创建echart目录&#xff0c;完成src/components/echart/SectorFlightChart.vue 的页…

【SM3哈希算法】算法原理

【SM3哈希算法】算法原理 参考&#xff1a; 文章目录 【SM3哈希算法】算法原理一、算法简介二、基本原理三、具体流程1、填充2、分组3、迭代压缩 C语言实现 一、算法简介 SM3算法是一种密码散列函数标准&#xff0c;由国家密码管理局发布。它的安全性和SHA-256相当&#xff0…

【leetcode热题】 阶乘后的零

给定一个整数 n &#xff0c;返回 n! 结果中尾随零的数量。 提示 n! n * (n - 1) * (n - 2) * ... * 3 * 2 * 1 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;0 解释&#xff1a;3! 6 &#xff0c;不含尾随 0示例 2&#xff1a; 输入&#xff1a;n 5 输出&a…

二叉树遍历(牛客网)

描述 编一个程序&#xff0c;读入用户输入的一串先序遍历字符串&#xff0c;根据此字符串建立一个二叉树&#xff08;以指针方式存储&#xff09;。 例如如下的先序遍历字符串&#xff1a; ABC##DE#G##F### 其中“#”表示的是空格&#xff0c;空格字符代表空树。建立起此二叉树…

Java毕业设计 基于springboot vue招聘网站 招聘系统

Java毕业设计 基于springboot vue招聘网站 招聘系统 springboot vue招聘网站 招聘系统 功能介绍 用户&#xff1a;登录 个人信息 简历信息 查看招聘信息 企业&#xff1a;登录 企业信息管理 发布招聘信息 职位招聘信息管理 简历信息管理 管理员&#xff1a;注册 登录 管理员…

const,static深度总结——c++穿透式分析

前言&#xff1b;c类和对象的知识点中除了几种默认函数&#xff0c; 比较重要的还有使用const和static修饰成员相关知识点。const在c中特性很简单。 但是在使用中&#xff0c; 比较容易疏忽大意出现问题。 static特性也很简单&#xff0c; 但是比起const来要直接的多。 在使用中…

2023 re:Invent 使用 PartyRock 和 Amazon Bedrock 安全高效构建 AI 应用程序

前言 “ Your Data , Your AI , Your Future .&#xff08;你的数据&#xff0c;你的 AI &#xff0c;你的未来。&#xff09; 亚马逊云科技开发者社区为开发者们提供全球的开发技术资源。这里有技术文档、开发案例、技术专栏、培训视频、活动与竞赛等。帮助中国开发者对接世界…

javaAPI操作Elasticsearch

mapping属性 mapping是对索引库中文档的约束, 常见的mapping属性包括: type: 字段数据类型,常见的简单类型有: 字符串: text(可分词的文本), keyword(精确值, 例如: 品牌,国家)数值: long, integer, short, byte, double, float布尔: boolean日期: date对象: object index: 是否…

LLMs之Grok-1:Grok-1的简介、安装、使用方法之详细攻略

LLMs之Grok-1&#xff1a;Grok-1的简介、安装、使用方法之详细攻略 导读&#xff1a;马斯克旗下的xAI公司宣布开源名为Grok-1的混合专家模型&#xff0c;参数量达3140亿&#xff0c;为目前最大的开源大语言模型。xAI此举或将引领人工智能开源趋势&#xff0c;同时也将对不太Ope…

协议分类笔记

1.3 协议分类 通信的协议还是比较复杂的&#xff0c;java.net 包中包含的类和接口&#xff0c;它们提供低层次的通信细节。我们可以直接使用这些类和接口&#xff0c;来专注于网络程序开发&#xff0c;而不用考虑通信的细节。 java.net 包中提供了两种常见的网络协议的支持&a…

DevExpress WinForms crack,DevExpress WinForms组件套件和库

DevExpress WinForms crack,DevExpress WinForms组件套件和库 Reporting & Analytics - Reports, Pivot Tables, PDF Viewer. The DevExpress WinForms Subscription includes royalty-free user interface components for next-gen decision support systems. Whether you…

Java基础经典10道题

目录 for循环的嵌套 题目一: 求101到200之间的素数的个数,并打印 代码分析: 注意点: 题目二:开发验证码 代码分析: 题目三:数组元素的复制 代码分析: 题目四:评委打分 健壮版代码: 代码分析:看源码 注意点: 题目五:数字加密 优化版代码: 代码分析: 题目六:数字…

MeterSphere和Jmeter使用总结

一、MeterSphere 介绍 MeterSphere 是⼀站式开源持续测试平台&#xff0c;涵盖测试跟踪、接⼝测试、UI 测试和性能测试等&#xff0c;全 ⾯兼容 JMeter、Selenium 等主流开源标准&#xff0c;能够有效助⼒开发和测试团队在线共享协作&#xff0c;实现端到 端的测试管理跟踪…

2、RabbitMQ_安装

RabbitMQ安装文档 RabbitMQ官网下载地址&#xff1a;https://www.rabbitmq.com/download.html 1.安装依赖 在线安装依赖环境&#xff1a; yum install build-essential openssl openssl-devel unixODBC unixODBC-devel make gcc gcc-c kernel-devel m4 ncurses-devel tk tc x…

Java语言: 多线程

1. 线程调度 1.1 线程状态 线程是cpu任务调度的最小执行单位&#xff0c;每个线程拥有自己独立的程序计数器、虚拟机栈、本地方法栈。 线程状态&#xff1a;创建、就绪、运行、阻塞、死亡 1.2 线程状态切换 1.3 阻塞唤醒过程 阻塞&#xff1a; 这三个方法的调用都会使当前…

视频私有云,HDMI/AV多硬件设备终端接入,SFU/MCU视频会议交互方案。

在视频业务深入的过程中越来越多的硬件设备接入视频交互的视频会议中远程交互&#xff0c;有的是视频采集&#xff0c;有的是医疗影像等资料&#xff0c;都需要在终端承显&#xff0c;这就需要我们的设备终端能多设备&#xff0c;多协议接入&#xff0c;设备接入如下。 1&#…