C语言之指针(中)

news2024/12/28 15:04:08

目录

前言

一、字符指针

1.用法(两种)

2.例子

二、指针数组

三、数组指针

1.数组指针的定义

1.概念

2.例子

2.数组名

3.使用

1.使用的情景

2.例子

四、数组参数、指针参数  

1.数组传参

2.指针传参

五、函数指针

1.函数的地址

2.函数指针

3.例子

 4.两个特别的代码

1.代码1

2.代码2

六、函数指针数组

1.定义

2.用途

七、指向函数指针数组的指针

八、回调函数

彩蛋

总结


前言

承接同系列文章C语言之指针(上),本文将进一步介绍指针的相关知识。

一、字符指针

char*

1.用法(两种)

9fb038e0ef02498893137146d122707f.png

第一种用法是将字符型变量ch的地址放到指针pc中;

第二种用法本质是把字符串 hello world. 首字符的地址放到了pstr中,而并非将整个字符串内容放入pstr中。

2.例子

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

那么,这个程序最终输出的结果是什么呢?

1434dfe5b68e440c9a92050961f538eb.png

当两个指针指向同一个字符串的时候,他们实际上是指向同一块内存。
但是用相同的常量字符串去初始化 ,不同的数组的时候就会开辟出不同的内存块。
所以str1和str2不同,str3和str4相同。

二、指针数组

元素为指针的数组

在指针(上)的内容中我们就介绍了指针数组。

所以,这里做一个小测试题,回顾一下知识点。请说出以下代码分别表示什么?

int* arr1[10]; //整形指针的数组
char *arr2[4]; //一级字符指针的数组
char **arr3[5];//二级字符指针的数组

三、数组指针

1.数组指针的定义

1.概念

我们已经知道的指针变量类型中:
整形指针: int * pint;   即,能够指向整形数据的指针。
浮点型指针: float * pf;  即, 能够指向浮点型数据的指针。
那么,数组指针应该是:能够指向数组的指针

2.例子

int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9};
	int(*p)[10] = &arr;
	//解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。
    //所以p是一个指针,指向一个数组,叫数组指针。
	//注意:①[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。
    //②[]中的数字不能省略,省略会被编译器误认为所指向的数组是只有一个元素,即int(*p)[] = &arr[0];
}

2.数组名

int arr[10];

&arr表示的是整个数组的地址,arr表示的是数组首元素的地址。

两者的区别

675759aad33c4060b220a603b1b4e757.png

1.数组的地址+1,跳过整个数组的大小;
2.数组首元素的地址+1,跳过数组的一个元素大小。

3.使用

1.使用的情景

一般情况下,用在一维数组时并不方便,所以我们一般多用在二维数组、多维数组。

注意:既然数组指针指向的是数组,那数组指针中存放的应该是数组的地址。

2.例子

#include <stdio.h>
void print_arr1(int arr[3][5], int row, int col)//可以用数组接收
{
	int i = 0;
	int j = 0;
	for (i = 0; i<row; i++)
	{
		for (j = 0; j<col; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}
void print_arr2(int(*arr)[5], int row, int col)//也可以用指针接收
{
	int i = 0;
	int j = 0;
	for (i = 0; i<row; i++)
	{
		for (j = 0; j<col; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	print_arr1(arr, 3, 5);
	//数组名arr,表示首元素的地址
	//二维数组的首元素是二维数组的第一行
	//所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
	//所以可以用数组来接收,也可以用数组指针来接收
	print_arr2(arr, 3, 5);
	return 0;
}

运行结果如下:

c5c4d0cc6fed4e83b1bc250b66f7f205.png

四、数组参数、指针参数  

写代码的时候难免要把【数组】或者【指针】传给函数,那函数的参数该如何设计呢?

1.数组传参

1.一般传数组名即可,除非要传某个元素。

例如:

test(arr);

2.参数部分一般是数组或者指针。

例如:

void test1(int arr[ ]);//[]中的数字可写可不写
{}
void test2(int *parr);
{}
3.以上是一维数组例子,二维数组与一维数组差不多,但是 二维数组传参,函数形参的设计 只能省略第一个[ ]的数字
因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。 这样才方便运算。

2.指针传参

指针传参,一般用指针接收。

一级指针传参,用一级指针接收;

二级指针传参,用二级指针接收。

五、函数指针

指向函数地址的指针

1.函数的地址

对于函数来说,&函数名和函数名都是表示函数的地址。

2.函数指针

int ADD(int x,int y)
{}
int (*pf)(int,int) = ADD;//pf是指向参数为(int,int)且返回值是int型的函数的指针
int (*pf)(int,int) = &ADD;//形参可以不写,但是要写明形参的类型

3.例子

int ADD(int x, int y)
{
	return x + y;
}
void test(int (*pf)(int,int))
{
	int a = 0;
	int b = 0;
	int ret = (*pf)(a, b);//可以这样使用,由于pf里面存的就是函数的地址,所以和ADD是一样的,也可以用下面的方式使用
	//int ret = pf(a, b);//*就是一个摆设,方便理解是对指针pf解引用才调用函数ADD的,所以*可以不写或者写很多个。
}
#include <stdio.h>
int main()
{
	test(ADD);//将函数ADD()的地址传参给函数test()
	return 0;
}

 4.两个特别的代码

这两个代码都是我在《C陷阱与缺陷》书中看到的例子,因为它们与函数指针有关系,并且十分特别所以也向大家介绍一下。

1.代码1

//代码1
int main()
{
	( * ( void ( * ) ( ) ) 0 ) ( );
}

 说明:以上是一次函数调用,调用的是0作为地址的函数

分析过程分为两步:

(1)把0强制类型转换为无参数,返回值是void*类型的函数的地址

(2)调用0地址处的函数

2.代码2

//代码2
void( * signal ( int, void ( * ) ( int ) ) ) ( int );

 说明:以上代码是一次函数声明

分析:

(1)首先,signal是这个函数的函数名

(2)函数的参数:第一个参数类型是int,第二个参数类型是函数指针(该函数指针指向的函数参数是int,返回值是void)

(3)可以观察出里外两个函数指针的类型是相同的。

这里可以用typedef重定义一下这个函数类型的类型名,就可以对上面的代码进行相应的简化了。例如:

typedef void(*pf_t) (int);//此时,这个函数指针类型的名字的重定义为pf_t了
//相应的上述代码就可以简化为:
pf_t signal(int, pf_t);

六、函数指针数组

1.定义

我们都知道,数组是一个存放相同类型数据的存储空间,那么存放函数指针的数组就是 函数指针数组
int (*parr1[10])();

解释:数组名是parr1,该数组中存放有10个int(* )()类型的函数指针。

2.用途

转移表

七、指向函数指针数组的指针

本质上还是个指针,指向存放函数指针的数组的首元素地址。

定义:

void test(const char* str)
{
	printf("%s\n", str);
}
int main()
{
	//函数指针pfun
	void(*pf)(const char*) = test;
	//函数指针的数组pfArr
	void(*pfArr[5])(const char* str);
	pfArr[0] = test;
	//指向函数指针数组pfArr的指针ppfunArr
	void(*(*ppfArr)[5])(const char*) = &pfArr;
	return 0;
}

八、回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
回调函数不是由该函数 的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

彩蛋

大家看这样一个代码,请思考一下最后一个表示的是什么呢?

#include <stdio.h>
int main()
{
	int arr[5];//整型数组
	int *p1[10];//整型指针数组
	int(*p2)[10];//整型数组指针
	int*(*p3[10])[5];//?
	return 0;
}

没错,它表示的是数组指针数组(存放数组指针的数组)

bcac7b40aec3413c8bf4955d8a83e076.png


总结

以上就是今天要讲的内容,本文介绍了C语言中指针的相关知识,主要包括有字符指针指针数组数组指针数组参数与指针参数函数指针函数指针数组指向函数指针数组的指针以及回调函数的知识。但由于篇幅原因,在这一篇文章中仍不能将所有的知识全部总结完,因此后续还是会不断补充关于C语言中的指针的内容,希望大家继续支持。

本文的作者也只是一个正在学习C语言等编程知识的萌新,若这篇文章中有哪些不正确的内容,请在评论区向作者指出(也可以私信作者),欢迎大佬们指点,也欢迎其他正在学习C语言的萌新和作者进行交流。

最后,如果本篇文章对你有所启发的话,也希望可以支持支持作者,后续作者也会定期更新学习记录。谢谢大家!

2a79e5b7935a4f518a3156d5046aad96.gif

 

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

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

相关文章

【C++】泛型编程之模板初阶

文章目录1. 泛型编程2. 模板2.1 模板的概念3. 函数模板3.1 函数模板概念3.2 函数模板语法3.3 函数模板的原理3.4 函数模板的实例化3.5 模板参数的匹配原则3.6 普通函数与函数模板的区别4. 类模板4.1 类模板语法4.2 类模板的实例化4.3 类模板与函数模板区别4.4 类模板分文件编写…

Word控件Spire.Doc 【文本】教程(17) ;在Word中设置文本方向

Spire.NET的Spire.是MicrosoftDoc人员对Word文档进行操作打印的.NET类库。帮助单独安装Microsoft Word在开发环境下&#xff0c;轻松便捷地创建、编辑、转换和转换Word文档。拥有近10个专业开发经验Spire系列办公文档开发打印工具&#xff0c;专注于创建、编辑、转换和Word/Exc…

C++设计模式---组合模式

文章目录使用场景组合模式的定义安全组合模式使用场景 组合模式和类与类之间的组合是不同的概念。 组合模式主要用来处理树形结构的数据&#xff0c;如果要表达的数据不是树形结构&#xff0c;就不太适合组合模式。 比如我们有一个目录结构&#xff1a; 这个目录我们把它绘…

图解LeetCode——1704. 判断字符串的两半是否相似(难度:简单)

一、题目 给你一个偶数长度的字符串 s 。将其拆分成长度相同的两半&#xff0c;前一半为 a &#xff0c;后一半为 b 。 两个字符串 相似 的前提是它们都含有相同数目的元音&#xff08;a&#xff0c;e&#xff0c;i&#xff0c;o&#xff0c;u&#xff0c;A&#xff0c;E&…

几分钟实现对恶意IP地址进行拦截,腾讯云Web防火墙实在太香了!

一、概述 在平时上网中&#xff0c;我们经常听到“xxx被拉入黑名单”、“把xxx加入白名单”&#xff0c;黑白名单成了禁止访问和允许访问的代名词&#xff0c;黑白名单是一种常见的安全机制&#xff0c;用于隔离流量&#xff0c;然后对隔离的流量采取特定操作。 黑名单代表只…

Redis高可用之持久化

一 Redis高可用 什么是高可用 在web服务器中&#xff0c;高可用是指服务器可以正常访问的时间&#xff0c;衡量的标准是在多长时间内可以提供正常服务(99.9%、99.99%、99.999%等等)。 但是在Redis语境中&#xff0c;高可用的含义似乎要宽泛一些&#xff0c;除了保证提供正常…

Pytorch实战:基于鲸鱼WOA优化1DCNN的轴承故障诊断

目录 0.引言 1.关键点 2.WOA优化1DCNN超参数实战 2.1 数据准备 2.2 1DCNN故障诊断建模 2.3 采用WOA优化1DCNN超参数 0.引言 采用1DCNN进行轴承故障诊断建模&#xff0c;并基于鲸鱼优化算法WOA对1DCNN的超参数进行优化&#xff0c;以实现更高的精度。建立一个两层的1DCNN&a…

【Transformers】第 9 章 :处理很少或没有标签

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

[C]实现能在本地存储的简易通讯录

作者&#xff1a; 华丞臧. 专栏&#xff1a;【C语言】 各位读者老爷如果觉得博主写的不错&#xff0c;请诸位多多支持(点赞收藏关注)。如果有错误的地方&#xff0c;欢迎在评论区指出。 文章目录一、文件动态通讯录二、相关代码解析2.1 初始化2.2 销毁通讯录2.3 增加联系人2.4 …

NLP模型(一)——word2vec实现

文章目录1. 整体思路2. 数据处理3. 数据准备4. 创建数据管道5. 构建模型6. 模型训练7. 加载模型得到词向量8. 总结前面我介绍了word2vec算法的两种实现算法&#xff0c;Skip−gramSkip-gramSkip−gram 以及 CBOWCBOWCBOW 算法&#xff0c;我认为理解一个算法最好的方法就是复现…

stft的窗函数设计要求和方法(COLA)

在语音处理进行短时傅里叶变换的时候&#xff0c;对窗函数是有一定要求的&#xff0c;这篇文章将对这方面的问题进行简单的阐述。 一、背景描述 常用的语音处理需要进行这样处理: stft分帧会对信号产生截断&#xff0c;为尽可能避免这种影响&#xff0c;应考虑考虑加合适的窗 …

互融云借条APP系统开发 六大系统优势全面保障

借条是指借个人或公家的现金或物品时写给对方的条子。它是一种凭证性文书&#xff0c;通常用于日常生活以及商业管理方面。借条的本质就是借款合同&#xff0c;只不过形式比较简单&#xff0c;那么电子借条也就是简单的电子借款合同。与传统的纸质合同相比&#xff0c;电子借条…

目标检测算法——YOLOv5/YOLOv7改进之结合无参注意力SimAM(涨点神器)

目录 &#xff08;一&#xff09;前言介绍 1.摘要 2.不同注意力步骤比较 &#xff08;二&#xff09;相关实验 &#xff08;三&#xff09;YOLOv5结合无参注意力SimAM 1.配置.yaml文件 2.配置common.py 3.修改yolo.py SimAM&#xff1a;无参数Attention助力分类/检测/分…

想带着学生做一个操作系统,可行性有多大?

有知乎网友提问如下: 想带着学生做一个操作系统&#xff0c;可行性有多大&#xff1f; 个人觉得可行性非常大&#xff0c;如果只是做着来玩&#xff0c;让学生了解操作系统时如何实现的话。但是&#xff0c;如果你打算今后商业化的话&#xff0c;那就另当别论了。就算你能做出来…

单片机实验——水塔自动抽水系统设计(基于Proteus仿真)

实验内容及要求 自来水供水是现代生活的一大特点&#xff0c;水塔作为储水装置是自来水系统必不可少的重要设施&#xff0c;让水塔保持一定的水量是自来水不断供的必要条件&#xff0c;本设计模拟自来水系统中水塔的自动抽水机制&#xff0c;设计分为控制系统和虚拟水塔两部分…

若依管理框架-漏洞复现

文章目录 0x00 介绍0x01 默认口令漏洞0x02 SQL注入0x03 Shiro反序列化漏洞0x04 任意文件读取/下载0x05 定时任务0x06 `swagger-ui.html`接口文档泄漏0x07 Druid未授权访问摘抄免责声明0x00 介绍 RuoYi 是一个 Java EE 企业级快速开发平台,基于经典技术组合(Spring Boot、Apa…

黑产工具情报的分析方式浅析

接下来我们以恶意爬虫、抢券工具和注册机三种工具来谈一下黑产工具情报的分析方式。 对于企业方面来说&#xff0c;黑产工具情报可以有效的提高业务安全的攻防效率。通过分析工具利用的业务接口&#xff0c;不仅可以将黑产作恶行为进行有效的追踪&#xff0c;对其进行有效的处…

Java本地搭建宝塔部署实战springboot自动化立体智慧仓库WMS源码

大家好啊&#xff0c;我是测评君&#xff0c;欢迎来到web测评。 本期给大家带来一套Java开发的springboot自动化立体智慧仓库WMS源码。 技术架构 技术框架&#xff1a;SpringBoot layui HTML CSS JS运行环境&#xff1a;jdk8 IntelliJ IDEA maven3 宝塔面板 本地搭建教…

MongoDB入门与实战-第四章-SpringBoot集成MongoDB

目录参考MongoDB 连接java客户端方式引入驱动依赖测试创建客户端创建集合查询文档查询集合大小条件查询排序投影聚合查询复合聚合插入文档批量插入更新文档删除文档SpringDataMongoDB添加依赖配置文件新建实体映射插入文档修改文档删除参考 SpringBoot 整合 MongoDB 实战解说 …

[附源码]java毕业设计濒危物种科普系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…