C语言学习笔记之指针(二)

指针基础知识:C语言学习笔记之指针(一)-CSDN博客

目录

字符指针

 代码分析

指针数组

数组指针

函数指针

代码分析(出自《C陷阱和缺陷》)

函数指针数组

指向函数指针数组的指针

回调函数

qsort()


字符指针

一般用法:

特殊用法:

        上述代码的本质是把字符串 "hello bit." 的首字符的地址放到了pstr中。因为"hello bit."是一个常量字符串,是存放在代码段的数据,所以不能被修改,因此要用const 修饰来防止权限的放大。

 代码分析

        这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当几个指针指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。

指针数组

        指针数组是一种数组,数组内的每个元素的类型是指针类型。

用指针数组模拟实现一个二维数组:

#include <stdio.h>

void print(int** arr, int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			printf("%d ", *(*(arr + i) + j)); //等价于 printf("%d ", arr[i][j]);
			//printf("%d ", arr[i][j]);
		}
		printf("\n");
	}

}
int main()
{
	int arr1[] = { 1, 2, 3, 4, 5 };
	int arr2[] = { 2, 3, 4, 5, 6 };
	int arr3[] = { 3, 4, 5, 6, 7 };
	int* arr[] = { arr1, arr2, arr3 };

	print(arr, 3, 5);
	return 0;
}

注:模拟的二位数组在内存中并不一定连续存放,因此它不是真的二维数组。

数组指针

        我们已经熟悉:

  •   整形指针: int * pint; 能够指向整形数据的指针。
  •   浮点型指针: float * pf; 能够指向浮点型数据的指针。

        同理,数组指针就是指向数组的指针,即存放数组地址的指针变量。

写一个打印二维数组的函数,形参是数组指针:

#include <stdio.h>
void print_arr1(int arr[3][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 ", arr[i][j]);
		}
		printf("\n");
	}
}
void print_arr2(int(*arr)[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 ", *(*(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,其实相当于第一行的地址,是一维数组的地址
	//可以数组指针来接收
	printf("\n");
	print_arr2(arr, 3, 5);
	return 0;
}

类型分析:

函数指针

        函数也是有地址的,顾名思义,函数指针就是指向函数的指针,即存放函数地址的指针变量。

        函数指针类型:函数返回值类型 (*指针变量名)(函数参数)

#include <stdio.h>
void test()
{
	printf("hehe\n");
}
int main()
{
	printf("%p\n", test);
	printf("%p\n", &test);
	return 0;
}

        根据上述代码的执行结果我们可以看出,函数名表示函数地址,&地址函数名也表示函数地址,即 函数名 == &函数名,所以 *(&函数名)== * 函数名 ,而*(&函数名)== 函数名,因此 函数名 == &函数名 == * 函数名

        首先,能存储地址,就要求pfun1或者pfun2是指针,那哪个是指针? 答案是:pfun1可以存放。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void。pfun2先和()结合,表示pfun2是一个没有参数,返回类型是void* 的函数。

代码分析(出自《C陷阱和缺陷》)

(*(void (*)())0)();

void (*signal(int , void(*)(int)))(int);

利用 typedef 简化上述代码:

函数指针数组

        存放函数指针的数组就叫做函数指针数组。

        parr1 先和 [] 结合,说明 parr1是数组,数组的内容是什么呢? 是 int (*)() 类型的函数指针。
        函数指针数组的用途:转移表

示例(计算器):

#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 }; //转移表
	while (input)
	{
		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);
		}
		else if (input == 0)
		{
			printf("程序退出\n");
			break;
		}
		else
			printf("输入有误\n");

		printf("ret = %d\n", ret);
	}
	return 0;
}

指向函数指针数组的指针

        顾名思义,指针指向的是一个每个元素是函数指针的数组。

回调函数

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

qsort()

        qsort()可以给任意类型(整型,浮点型,结构体等)的数据排序,但是需要我们按要求提供一个比较函数

用冒泡排序模拟实现一个类似 qsort() 的函数:

#include <stdio.h>

void swap(char* p1, char* p2, size_t size) //交换arr[j],arr[j+1]这两个元素
{
	int i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *p1;
		*p1 = *p2;
		*p2 = tmp;
		p1++;
		p2++;
	}
}

void bubble_sort(void* base, size_t num, size_t size, int (*cmp)(const void*, const void*))
{
	int i = 0;
	//趟数
	for (i = 0; i < num - 1; i++)
	{
		int j = 0;
		//一趟内部比较的对数
		for (j = 0; j < num - 1 - i; j++)
		{
			//假设需要升序cmp返回>0,交换
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0) //两个元素比较,需要将arr[j],arr[j+1]的地址要传给cmp
			{
				//交换
				swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}
	}
}

int cmp_int(const void* p1, const void* p2) //回调函数
{
	return *(int*)p1 - *(int*)p2;
}

//测试bubble_sort 排序整型数据
void test1()
{
	int arr[10] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz, sizeof(int), cmp_int);
}

struct Stu
{
	char name[20];
	int age;
};

int cmp_stu_by_age(const void* p1, const void* p2) //回调函数
{
	return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}

//测试bubble_sort 排序结构体数据(比较年龄)
void test2()
{
	struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 50},{"wangwu", 15} };
	int sz = sizeof(arr) / sizeof(arr[0]); 
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
}

int cmp_stu_by_name(const void* p1, const void* p2) //回调函数
{
	return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}

//测试bubble_sort 排序结构体数据(比较名字)
void test3()
{
	struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 50},{"wangwu", 15} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	printf("%d\n", sizeof(struct Stu));
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}

int main()
{
	test1();
	test2();
	test3();
	return 0;
}

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

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

相关文章

试用模方时,系统一直提示“未找到有效配置文件” ,是需要安装3dsmax吗 ?

问题如图 把文件放在认证管理服务安装目录下即可。&#xff08;注&#xff1a;因平台限制&#xff0c;需要文件的直接后台私信即可哦&#xff09; 模方是一款针对实景三维模型的冗余碎片、水面残缺、道路不平、标牌破损、纹理拉伸模糊等共性问题研发的实景三维模型修复编辑软件…

软考 - 系统架构设计师 - 数据架构真题

问题 1&#xff1a; (相当于根据题目中提到的 4 点&#xff0c;说一下关系型数据库的缺点) &#xff08;1&#xff09;.用户数量的剧增导致并发负载非常高&#xff0c;往往会达到每秒上万次读写请求。关系数据库应付每秒上万次的 SQL 查询还勉强可以&#xff0c;但是应付上万…

车载摄像头夜景增强技术解决方案,解锁高质量夜间视觉体验

随着汽车智能化的快速发展&#xff0c;车载摄像头已成为驾驶辅助系统的核心组件。尤其在夜间行驶时&#xff0c;摄像头所捕捉的画面质量直接关系到驾驶者的安全感知和行车决策。然而&#xff0c;传统的车载摄像头在夜间往往面临噪声多、画质差等挑战&#xff0c;难以满足用户对…

goland2024安装包(亲测可用)

目录 一、软件简介 二、软件下载 一、软件简介 Goland 是一款由 JetBrains 公司开发的集成开发环境&#xff08;IDE&#xff09;&#xff0c;专门用于 Go 语言的开发。它提供了丰富的功能和工具&#xff0c;帮助开发者更高效地编写、调试和管理 Go 语言项目。 功能特点&#x…

什么是Rust语言?探索安全系统编程的未来

&#x1f680; 什么是Rust语言&#xff1f;探索安全系统编程的未来 文章目录 &#x1f680; 什么是Rust语言&#xff1f;探索安全系统编程的未来摘要引言正文&#x1f4d8; Rust语言简介&#x1f31f; 发展历程&#x1f3af; Rust的技术意义和优势&#x1f4e6; Rust解决的问题…

基于逐笔数据合成高频订单簿:DolphinDB 订单簿引擎

订单簿是交易市场上买卖双方正在报价的不同价格的列表。订单簿快照反应了特定时刻市场上的交易意图&#xff0c;比如交易活跃的证券标的往往有着密集的订单簿。订单簿快照对量化金融的交易策略、风险管理和市场分析等方面都具有重要意义。 通常交易所可以提供实时和历史的行情…

无界系统实战课:全体系落地无界改版后选择、出价、高投产做付费引流-38节

课程内容&#xff1a; 001.01、如何快速学习无界推广(新学员先听).mp4 002.02、如何快速上手和适应无界(老学员先听).mp4 003.03、无界推广在运营中的作用(必听).mp4 004.04、无界多工具如何选择(必听).mp4 005.05、自定义出价、控成本、最大化底层逻辑和选择(必听).mp4 …

postgres插件部署+函数开发 - pl/java安装(centos7)

一、安装postgres sudo yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm sudo yum install -y postgresql11-server sudo /usr/pgsql-11/bin/postgresql-11-setup initdb sudo systemctl enable postg…

stable diffusion--小白学习步骤

1.看一下Unet网络的讲解_哔哩哔哩_bilibili&#xff0c;了解Unet网络 2.看一下【生成式AI】Diffusion Model 原理剖析 (1/4)_哔哩哔哩_bilibili&#xff0c;起码要看前3/6个视频 3.看一下超详细的扩散模型&#xff08;Diffusion Models&#xff09;原理代码 - 知乎 (zhihu.co…

前端-vue项目debugger调试

一、前言 有的时候接受同事一个项目&#xff0c;用框架不一样&#xff0c;写的也不太规范&#xff0c;那么就需要打断点去学习改项目的流程了。 那么vue项目是如何debugger调试呢&#xff1f; 二、操作 大概理解一下&#xff0c;vue项目启动&#xff0c;大概是先启动框架&am…

nginx 卸载和安装超详细教程

一、前言 由于现在nginx有版本漏洞&#xff0c;所以很多安装过nginx的需要卸载重新安装&#xff0c;没安装过的&#xff0c;切记不要乱安装版本。 OK以上版本切记不能再用了&#xff01; 废话不多说&#xff0c;直接上干货。 二、卸载 1、停止Nginx进程 命令行停止&#xf…

【C++成长记】C++入门 | 类和对象(上) |面向过程和面向对象初步认识、类的引入、类的定义、类的访问限定符及封装

&#x1f40c;博主主页&#xff1a;&#x1f40c;​倔强的大蜗牛&#x1f40c;​ &#x1f4da;专栏分类&#xff1a;C❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 一、面向过程和面向对象初步认识 C语言是面向过程的&#xff0c;关注的是过程&#xff0c;分析出求解问题的步…

【日常记录】【CSS】利用动画延迟实现复杂动画

文章目录 1、介绍2、原理3、代码4、参考链接 1、介绍 对于这个效果而言&#xff0c;最先想到的就是 监听滑块的input事件来做一些操作 ,但是会发现&#xff0c;对于某一个节点的时候&#xff0c;这个样式操作起来比较麻烦 只看这个代码的话&#xff0c;发现他用的是动画&#x…

第47期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以找…

通过超分辨率像素引导的Scribble Walking和逐类对比正则化的弱监督医学图像分割(SC-Ne)论文速读

目录 Weakly Supervised Medical Image Segmentation via Superpixel-Guided Scribble Walking and Class-Wise Contrastive Regularization摘要方法实验结果 Weakly Supervised Medical Image Segmentation via Superpixel-Guided Scribble Walking and Class-Wise Contrastiv…

召唤新版「数据库 GitOps 」体验官,赢取新款 Bytebase 限量周边!

距上一次「产品体验官&#xff5c;基于 GitHub 的数据库 CI/CD」已有一年半了⌛️ Bytebase 于上周发布了 Bytebase 2.15.0 - GitOps 整体升级 &#x1f38a; 全新的 GitOps 体验&#xff0c;更易上手&#xff0c;更简洁&#xff01;&#x1f929; 不管你是否使用过 Byteb…

【SpringBoot实战篇】登录认证

&#x1f340;&#x1f338;明确需求--接口文档--思路分析--开发--测试&#x1f338;&#x1f340;&#x1f495; 1 明确需求 2 接口文档 登录 3 思路分析 UserServic、UserMapper在注册的时候已经实现 现在我们重点看UserController 控制器 4 开发&#xff08;实现&#xff0…

一篇安装配置ubuntu22.04(步骤详细,配置成功)

一篇配置ubuntu22.04(步骤详细&#xff0c;配置成功) 官网下载相应的镜像 vitualbox安装ubuntu 新建虚拟机 第一步 第二步 第三步、按需分配内存、处理器个数、磁盘大小 第四步、一直下一步直至完成 配置虚拟机网络 第一步、先停止虚拟机 第二步、设置虚拟机网络 正常启…

Hudi-IDEA编程

项目 一、HudiSparkKafka&#xff08;Scala&#xff09; 配置详见【1.Scala配置】 依赖详见【1.HudiSparkKafka依赖】 1-1 构建SparkSession对象 def main(args: Array[String]): Unit {//1.构建SparkSession对象val spark: SparkSession SparkUtils.createSparkSession(…

7.C++:多态

一、 virtual关键字 //1.可以修饰原函数&#xff0c;为了完成虚函数的重写&#xff0c;满足多态的条件之一&#xff1b; //2.可以在菱形继承中&#xff0c;完成虚继承&#xff0c;解决数据冗余和二义性&#xff1b; 两个地方使用同一关键字&#xff0c;但二者间没有一点关联 二…