深度理解指针(3)

news2024/11/13 15:01:43

hello,各位小伙伴们在上期的最后我们了解到了指针数组,是用来存储指针的数组。这期我们将会学习深度理解指针(3)有关指针的内容,仍然与数组分不开,让我们踏上此次列车来进行新的旅途吧!

目录

 字符指针

经典例题

数组指针

二维数组传参本质 

 函数指针变量的创建

小试牛刀 

typedef关键字

函数指针数组


 字符指针

我们通常会这样使用字符指针:

#include<stdio.h>
int main ()
{
    char ch = 'a';
    char*pch = &ch;
    &ch = 'w';
    return 0;
}

但是还会有一种特殊的使用方式:

#include<stdio.h>
int main ()
{
    char*ptr = "hello liyaoyao";
    printf("%s",ptr);
}

 这是什么意思呢?如果难以理解不妨看一下下面与之类似的:

#include<stdio.h>
int main ()
{
    char arr[] = "hello liyaoyao";
    char*ptr = arr;
    //arr和ptr的类型相同,同时指向的对象也相同可以互相替换
    //printf("%s",arr);
    printf("%s",ptr);
}

 看来这段代码之后也许小伙伴们心里已经清楚了。char*ptr = "hello liyaoyao";是将字符串的首地址赋给ptr,同时后面为常量字符串是不可以被修改的。但如果以创建一个字符数组的形式,字符串的内容可以被修改,小伙伴们可以试试哦。(程序会崩掉)!

经典例题

#include <stdio.h>
int main()
{
	char str1[] = "hello liyaoyao.";
	char str2[] = "hello liyaoyao.";
	const char* str3 = "hello liyaoyao.";
	const char* str4 = "hello liyaoyao.";
	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;
}

 想一想结果会是什么呢?答案会在最后揭晓!

数组指针

存放整型地址的变量为int*,存放字符地址的变量为char*,那么存放数组地址的变量为什么呢?

--------数组指针:存放的是数组的地址,能够指向数组的指针变量。

数组指针变量的格式为:

#include<stdio.h>
int main ()
{
    int arr[10] = { 0 };
    int (*parr)[10] = &arr;
}//再次提醒:arr为数组首元素地址,&arr[0]也为数组首元素地址,arr为整个数组的地址。

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

二维数组传参本质 

在前期的扫雷游戏中我们就已经大概了解到了二维数组的传参问题。通常情况下我们会使用下标的形式对二维数组进行访问。

#include<stdio.h>
void Print(int arr[][5],int row,int col)
{
    int i = 0;
    for(i = 0;i < 3;i++)
    {
        int j = 0;
        for(j = 0;j < 5;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};
    Print(arr,3,5);
}

在函数实参部分arr是二位数组的数组名,表示二维数组首元素地址,我们不禁会想首元素到底是谁,地址又是哪个呢?

我们可以这样理解:二维数组其实是一维数组的数组,那二维数组的首元素地址应该是第一行数组的地址。根据前面介绍的数组指针,我们可以通过修改形参部分来对其进行接收。

函数定义部分:

void Print(int (*p)[5],int row,int col)
{
    int i = 0;
    for(i = 0;i < row;i++)
    {
        int j = 0;
        for(j = 0;j<col;j++)
        {
            //printf("%d",*(*(p+i)+j));
            //printf("%d",(*(p+i))[j]);
            printf("%d",p[i][j]);
        } 
    }
}

 函数指针变量的创建

根据前面的学习我们了解到变量有地址,数组有地址,那么函数是否存在地址呢?

答案是存在!

#include<stdio.h>
int Sub(int x,int y)
{
    return x - y;
}
int main ()
{
    printf("%p\n",Sub);
    printf("%p\n",&sub);
}//两者的输出结果是相同的

对函数取地址有两种使用方式:1,函数名作为函数地址     2,取地址函数名作为函数的地址。

创建函数指针变量与数组指针变量的创建类似。

//……
int function(int x,int y)
{
    …………
}
int main()
{
    int (*p)(int x,int y) = &function;
}

 函数指针变量的使用:

//……
int function(int x,int y)
{
    ///
}
int main ()
{
    int(*p)(int x,int y); 
    int ret = function(2,3);
    ret = (*p)(2,3);
    ret = p(2,3);
}//三种使用方式均可以

小试牛刀 

 让我们来看两个有意思的代码并尝试理解它:

1、

#include<stdio.h>
int main ()
{
    (*(void(*))0)();
    return 0;
}

我们试着从0开始入手,0的前面有一个括号,括号里面为void(*),在一个数字的前面加上括号就是强制类型转化,即将0强制类型转化成void(*)类型,即0从一个整型转化成了函数的地址(变成了函数指针变量),同时这个函数没有参数。

该段代码出自C陷阱与缺陷。 

2、 

#include<stdio.h>
int main ()
{
    void(*signal(int , void(*)(int)))(int);
    return 0;
}

这句代码是函数的声明,signal是函数的名字,signal函数有两个参数一个是int类型,另一个是void(*)(int)类型,即函数指针类型,指向的参数类型是int类型,返回值是void。同时该函数指针指向的参数类型是int类型,返回值是void。

typedef关键字

typedef旨在类型的重定义,将复杂的参数类型定义为简单类型。

//…………
//指针的重定义
typedef int* prt;
int a = 0;
//int *pa = &a;
ptr pa = &a;

//…………
//指针数组的重定义
typedef int *ptr_t[10] ;
//int *arr[10];
ptr_t arr;

//…………
//数组指针的重定义
typedef int (*ptr_t1)[2];
int arr[2] = {0};
//int (*parr)[2] = &arr;
ptr_t1 parr[2] = &arr;

这样重定义虽然在书写的时候变得简单了,但对于变量类型的理解做出了阻碍,在具体写代码时要看情况合理使用。

函数指针数组

前面我们讲到了指针数组(整型指针数组、字符指针数组),下面我们来学习一下函数指针数组。

定义: 

函数的地址存到一个数组中,那这个数组就叫函数指针数组。

 

#include<stdio.h>
int Add(int x,int y)
{
    //
}
int Sub(int x,int y)
{
    //
}
int main ()
{
    int (*ptr[2])={Add,Sub};
    return 0;
}

 函数指针数组的用途:转移表

模拟实现一下计算器的+、-、*、\的功能。

通常情况下我们会这样使用:

#include<stdio.h>
Print_menu()
{
	printf("**************************************\n");
	printf("*******   1.  add      2.sub    ******\n");
	printf("*******   3.  mul      4.div    ******\n");
	printf("************     0.exit     **********\n");
	printf("**************************************\n");

}
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()
{
	Print_menu();
	int input = 0;
	int x = 0;
	int y = 0;
	
	do
	{
		int ret = 0;
		printf("请选择>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入两个整数:");
			scanf("%d %d", &x, &y);
			ret = Add(x, y);
			printf("%d", ret);
			break;
		case 2:
			printf("请输入两个整数:");
			scanf("%d %d", &x, &y);
			ret = Sub(x, y);
			printf("%d\n", ret);
			break;
		case 3:
			printf("请输入两个整数:");
			scanf("%d %d", &x, &y);

			ret = Mul(x, y);
			printf("%d\n", ret);
			break;
		case 4:
			printf("请输入两个整数:");
			scanf("%d %d", &x, &y);
			ret = Div(x, y);
			printf("%d\n", ret);
			break;
		case 0:
			break;
		default:
			printf("输入错误,请重新输入:");
		}

	} while (input);
	return 0;
}

但我们发现在switch语句中每一种情况的格式是一样的,只有函数不一样,多次书写会造成代码冗余的情况,我们可以使用函数指针数组来进行简化!

主函数部分:

int main()
{
	Print_menu();
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	int (*ptr[5])(int x, int y) = { 0,Add,Sub,Mul,Div };
	do
	{
		printf("请选择>");
		scanf("%d", &input);
		if (input >= 1 && input <= 4)
		{
			printf("请输入两个整数:");
			scanf("%d %d", &x, &y);
			ret = (*ptr[input])(x, y);
			printf("%d\n", ret);
		}
		else if (input == 0)
		{
			printf("计算结束!");
		}
		else
		{
			printf("输入错误请重新输入!\n");
		}
	} while (input);
	return 0;
}

 使用函数指针数组可以将这几个传参相同的函数地址存放在一个数组中,在每次使用时,直接调用即可!

 

经典例题答案公布:

str1 and str2 are not same
str3 and str4 are same

这里的判断部分比较的都是首元素地址,str1和str2都是创建一个新的数组,与数组内容无关,str1与str2在内存中的地址不同所以会输出not same。str3与str4都是字符指针变量,将一常量赋给str1和str2,因为常量内容是一样的,所以不需要在额外申请一段空间来进行存储,所以str1与str2所代表的首元素地址是相同的。

 OK这期就到这里啦!下期我们继续学习指针的内容有关回调函数和qsort函数的内容,拜拜。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【实施】软件实施方案(word套用)

软件实施方案 二、 项目介绍 三、 项目实施 四、 项目实施计划 五、 人员培训 六、 项目验收 七、 售后服务 八、 项目保障措施 软件开发全套资料获取&#xff1a;&#xff08;本文末个人名片也可直接获取&#xff09; 软件产品&#xff0c;特别是行业解决方案软件产品不同于一…

【ES6】使用Proxy实现单例模式

前言 由于JS没有private关键字&#xff0c;无法私有化构造器&#xff0c;所以下面代码无法限制&#xff1a; class Person {constructor() {console.log("Person created");} }const p1 new Person(); const p2 new Person();console.log(p1 p2); // false实现 …

【机器学习】小样本学习的实战技巧:如何在数据稀缺中取得突破

我的主页&#xff1a;2的n次方_ 在机器学习领域&#xff0c;充足的标注数据通常是构建高性能模型的基础。然而&#xff0c;在许多实际应用中&#xff0c;数据稀缺的问题普遍存在&#xff0c;如医疗影像分析、药物研发、少见语言处理等领域。小样本学习&#xff08;Few-Shot Le…

【网络】IP协议详解

前言 IP协议是网络层协议&#xff0c;应用层希望让数据可靠的从A主机到B主机&#xff0c;就设计出了传输层策略TCP协议。而实际上&#xff0c;网络从A网络到B网络不仅依赖于传输层可靠的策略&#xff0c;还依赖于跨网络传输数据的能力。这个跨网络的能力就IP协议。 数据从A主…

【深度好文】非地面网络NTN的3GPP研究发展历程

目录 基本概念 NTN频段 3GPP版本演进 Pre Rel-15 Rel-15 Rel 16 Rel 17 Rel 18 Rel 19 3GPP标准后续研究 NTN 的无线相关 SI/WI 通过 NTN 提供物联网支持的无线相关 SI/WI 通过 NTN 提供物联网支持的系统/核心网络相关 SI/WI 参考 缩写 基…

变声器免费的直接说话的那种!不整虚的,一键变声!好听!

听说网络上一堆推荐软件测评的&#xff0c;一半斗志推销自己家的软件&#xff0c;好不好用其次&#xff0c;关键是名声已经在外&#xff01;今天俺老孙也不整这些虚的&#xff0c;直接上干货&#xff0c;测评2024最新的电脑变声软件&#xff0c;帮助大家了解这六款国内外不同系…

8月25日微语报,星期日,农历七月廿二

8月25日微语报&#xff0c;星期日&#xff0c;农历七月廿二&#xff0c;周末愉快&#xff01; 一份微语报&#xff0c;众览天下事&#xff01; 1、两部门预拨5000万元中央自然灾害救灾资金支持辽宁防汛救灾。 2、重达2492克拉&#xff01;博茨瓦纳发现世界第二大钻石。 3、…

了解ROS Nodes(节点/结点)

1.相关概念 Nodes:A node is an executable that uses ROS to communicate with other nodes.Messages: ROS data type used when subscribing or publishing to a topic.Topics: Nodes canpublishmessagesto a topic as well assubscribetoa topic to receive messages.Master…

LLM 直接偏好优化(DPO)的一些研究

今天我们来聊聊大型语言模型&#xff08;LLMs&#xff09;吧。要让这些聪明的家伙和咱们人类的价值观还有喜好对上号&#xff0c;这事儿可不简单。以前咱们用的方法&#xff0c;比如基于人类反馈的强化学习&#xff08;RLHF&#xff09;&#xff0c;虽然管用&#xff0c;但是它…

3.2-CoroutineScope/CoroutineContext:GlobalScope

文章目录 GlobalScope 是一个特殊的 CoroutineScope&#xff0c;它是一个单例的 CoroutineScope&#xff0c;我们可以直接用它启动协程&#xff1a; GlobalScope.launch {}我们在 IDE 用 GlobalScope 时会有一条黄线&#xff0c;提示的是要小心使用它因为容易用错、容易写出问…

标配M4芯片!苹果三款Mac新品蓄势待发

Mark Gurman透露&#xff0c; 苹果正在测试M4系列Mac新品&#xff0c;包含MacBook Pro、Mac mini和iMac&#xff0c;这些设备会在今年10月同台亮相。 根据曝光的开发者日志&#xff0c;上述Mac设备新品测试了两种M4芯片&#xff0c;一种是10核CPU10核GPU&#xff0c;一种是8核C…

无人机PX4飞控 | 电源系统详解与相关代码

无人机需要一个稳压电源用于飞控供电&#xff0c;同时用于电机、舵机、外围设备等的供电。 供电系统一般是一块电池或多块电池 电源模块通常用于“分离”飞行控制器的稳压电源&#xff0c;也用于测量电池电压和PX4学习笔记飞行器消耗的总电流。 PX4可以使用这些信息来推断剩余的…

Steam昨夜故障原因公布:遭DDoS攻击 与《黑神话》在线人数无关

24日晚&#xff0c;Steam平台突然崩溃&#xff0c;国内国外玩家纷纷反馈无法登录&#xff0c;相关话题迅速登上热搜。不少玩家猜测Steam崩溃是因为《黑神话&#xff1a;悟空》在线人数过多导致。 不过&#xff0c;根据完美世界竞技平台发布的公告&#xff0c;此次Steam崩溃是由…

新书推荐:《分布式商业生态战略:数字商业新逻辑与企业数字化转型新策略》

近两年&#xff0c;商业经济环境的不确定性越来越明显&#xff0c;市场经济受到疫情、技术、政策等多方因素影响越来越难以预测&#xff0c;黑天鹅事件时有发生。在国内外经济方面&#xff0c;国际的地缘政治对商业经济产生着重大的影响&#xff0c;例如供应链中断&#xff0c;…

Python画笔案例-010 绘制台阶图

1、绘制台阶图 通过 python 的turtle 库绘制一个台阶图的图案&#xff0c;如下图&#xff1a; 2、实现代码 引入新的命令&#xff1a;turtle.ycor()&#xff0c;获取当前海龟的y 坐标值&#xff0c;turtle.xcor()是获取海龟的 x 坐标值&#xff1b; turtle.setx(x) &#xff0…

NC 最长上升子序列(三)

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 描述 给定数组 arr…

C++ STL 容器

引言--多看案例 STL概念 STL(Standard Template Library, 标准模板库 ), 是惠普实验室开发的一系列软件的统 称。 STL 6 大组件 容器 : 作用 : 容纳存储数据 分类 : 序列式容器&#xff1a; 强调值的排序&#xff0c;每个元素均有固定的位置&#xff0c; 除非用删除或插…

深度学习与神经网络戴做讲解

深度学习指导&#xff0c;计算机视觉指导。检测&#xff0c;分割&#xff0c;视频处理&#xff0c;估计&#xff0c;人脸&#xff0c;目标跟踪&#xff0c;图像&视频检索/视频理解&#xff0c;医学影像&#xff0c;GAN/生成式/对抗式&#xff0c;图像生成/图像合成&#xf…

C++ 设计模式——迭代器模式

迭代器模式 C 设计模式——迭代器模式1. 主要组成成分2. 迭代器模式范例2.1 抽象迭代器2.2 抽象容器2.3 具体的迭代器2.4 具体的容器2.5 主函数示例 3. 迭代器 UML 图3.1 迭代器 UML 图解析 4. 迭代器模式的优点5. 迭代器模式的缺点6. 迭代器模式的适用场景7. 现代C中的迭代器总…

【kubernetes】相关pod的创建和命令

【书写方法】&#xff1a; 管理使用k8s集群时&#xff0c;创建资源的Yaml文件非常重要&#xff0c;如何快速手写呢&#xff1f; 根据命令提示书写&#xff1a; kubectl explain [资源名称]例如打算写pod资源文件时&#xff0c;可查看如下&#xff1a; # 查看pod下所有字段 …