C语言:指针(二)

目录

  • 1.数组名的理解
  • 2.使用指针访问数组
  • 3.一维数组传参的本质
  • 4.二级指针
  • 5.指针数组
  • 6.字符指针变量
  • 7.数组指针变量
  • 8.二维数组传参的本质
  • 9.函数指针变量
  • 10.函数指针数组
  • 11.回调函数
  • 12.qsort函数
  • 13.使用回调函数模拟实现qsort函数

1.数组名的理解

int main() {
	int arr[] = { 1,2,3 };
	printf("%p\n", &arr[0]);
	printf("%p\n", arr);

	return 0;
}

在这里插入图片描述
从结果可以看出,&arr[0] == arr,这是用为数组名就是地址,而且是首元素的地址
但是,arr作为数组名在两种情况下不表示首元素的地址:

  • sizeof(数组名),sizeof中单独存放数组名表示求整个数组的大小,单位是字节
  • &arr,&arr是&后面直接加上一个数组名,它表示整个数组的地址。(&arr在数值上可能与&arr[0]相同,但是本质上是不一样的,即&arr[0] == arr != &arr )
    除此之外,数组名都表示首元素的地址。
    在这里插入图片描述
    其实arr与&arr的区别不在于直接打印的数值上,而在于运算上,
int main() {
	int arr[] = { 1,2,3 };
	printf("%p\n", &arr[0]);
	printf("%p\n", &arr[0]+1);

	printf("%p\n", arr);
	printf("%p\n", arr+1);

	printf("%p\n", &arr);
	printf("%p\n", &arr+1);

	return 0;
}

在这里插入图片描述
可以看出,arr和&arr[0]加一的结果都是跳过4个字节,而&arr则跳过12个字节(E8-F4 =(15 * 16 ^ 1 + 4 * 16 ^ 0)- ( 14*16^1 + 8 *16 ^0) =12).
&arr表示整个数组,加一表示跳过整个数组,数组一共3个int类型的数据,所以一次跳过12个字节。

2.使用指针访问数组

数组可以使用下标引用操作符来访问,比如:

int ret = arr[9];

既然数组名相当于地址,那么还可以这样访问:

int* p = &arr[9];
int ret = *p;

3.一维数组传参的本质

以前将数组作为参数传递给函数时需要将数组的数据个数一起传递给函数,那么,不传递数据个数可以吗?

void test(int arr[]) {
	int sz2 = sizeof(arr) / sizeof(arr[0]);
	printf("sz1 = %d\n", sz2);
}
int main() {
	int arr[] = {1,2,3,4,5};
	int sz1 = sizeof(arr) / sizeof(arr[0]);
	printf("sz1 = %d\n", sz1);
	test(arr);
	return 0;
}

在这里插入图片描述
从结果来看是不行的,
其实,将数组作为参数是将数组的首元素的地址作为参数传递给函数
所以,在函数内部使用sizeof(arr)实际上计算的是一个地址的大小。正因为参数部分的本质是指针,所以不能不传数据个数

4.二级指针

指针变量也是变量,是变量就有地址,所以二级指针是用来存放一级指针变量的地址的
在这里插入图片描述
画图说明:
在这里插入图片描述
对于二级指针的运算:

  • *ppa通过对ppa中的地址解引用,找到的是pa,*ppa访问的就是p
  • **ppa先对ppa解引用得到pa然后对pa解引用得到a

5.指针数组

存放整型的数组叫做整型数组
存放字符的数组叫做字符数组
那么存放指针的数组就叫做指针数组

在这里插入图片描述

6.字符指针变量

在指针的类型中有一种指针类型叫做字符指针char*
在这里插入图片描述
还有一种使用方法:

int main() {
	const char* p = "abcdef";
	printf("%s\n", p);

	return 0;
}

这种表示方法相当于将字符串看作为一个字符数组,指针变量p存放的不是字符串而是首字符’a‘的地址。
如果两个指针指向同一个字符串,那么不会开辟两个空间来存放,也就是说两个指针指向的是同一个内存

7.数组指针变量

存放整型数据地址的指针叫做整型指针
存放字符型数据的指针叫做字符指针
那么存放数组的地址的指针叫做数组指针

指针数组是数组,而数组指针是变量

int (*P)[5];

p先于*结合说明p是一个指针,再于【】和 int结合说明他是一个数组指针,数组元素类型是int。

数组自指针的初始化:

int main() {
	int arr[10] = { 0 };
	int(*p)[10] = &arr;

	return 0;
}

在这里插入图片描述
&arr == p

8.二维数组传参的本质

当我们将二位数组作为参数传递给函数时,其实传递的是第一行的地址(不是第一个元素的地址)

void test(int(*p)[3], int r, int c) {
	for (int i = 0; i < r; i++) {
		for (int j = 0; j < c; j++) {
			printf("%d ", *(*(p + i) + j));
		}
		printf("\n");
	}
}
int main() {
	int arr[2][3] = { {1,2,3},{2,3,4} };
	test(arr, 2, 3);

	return 0;
}

因为传递的是第一行元素的地址,那么该地址就该用函数指针来接收。
在这里插入图片描述

9.函数指针变量

函数也有地址,那么该地址就可以使用指针变量来接受,那么该指针变量就是函数指针变量。

int (*p)(int, int);

在这里插入图片描述
可以看出函数名就是函数的地址,也可以使用&函数名来获得函数的地址。
要将函数的地址存放起来,就可以使用函数指针变量
在这里插入图片描述
可以使用函数指针变量来调用函数

void test() {
	printf("haha\n");
}
int main() {
	int(*p)() = test;
	p();

	return 0;
}

在这里插入图片描述

10.函数指针数组

函数指针是用来存放函数的地址的,那么有很多函数,要将它们的地址存放在一起,那么就可以使用函数指针数组。

int (*p[10])(int, int);

p先于【】结合说明是一个数组,那么数组的内容就是int (*)().

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;
}
void Menu() {
	printf("**************************\n");
	printf("*****1.加法    2.减法*****\n");
	printf("*****3.乘法    4.除法*****\n");
	printf("*****0.退出          *****\n");
	printf("**************************\n");
}
int main() {
	int n = 0;
	int num1 = 0, num2 = 0;
	int(*p[5])(int, int) = { 0,Add,Sub,Mul,Div };
	do {
		Menu();
		printf("请输入要选择的功能:>");
		scanf("%d", &n);
		if (n >= 1 && n <= 4) {
			printf("\n请输入两个数用于计算:>");
			scanf("%d%d", &num1, &num2);
			printf("\n计算结果:%d\n", (*p[n])(num1, num2));
		}
		else if (n == 0) {
			printf("已退出\n");
			break;
		}
		else {
			printf("选择错误,重新选择:>");
		}
	} while (n);

	return 0;
}

这个代码实现了一个简单的计算器。
int( * p[5])(int, int) = { 0,Add,Sub,Mul,Div };
这段代码就是将四个函数的地址放在一个函数指针数组里面
( * p[n])(num1, num2);
这段代码函数指针数组来调用函数n表示想调用哪个函数,num1和num2就是传递给函数的参数

11.回调函数

回调函数就是一个通过函数指针调用的函数
如果将函数的指针作为一个参数传递给另一个函数,但这个指针被用来调用其所指的函数时,被调用的函数就是回调函数。

void test2() {
	printf("hahaha\n");
}
void test1(void (*test2)()) {
	printf("haha\n");
	test2();
}
int main() {

	test1(test2);

	return 0;
}

在这里插入图片描述
在main函数中,将test2的地址传递给test1,然后test1用函数指针变量来接收,然后调用test2函数,那么test2函数就是回调函数。

12.qsort函数

qsort函数的功能是将任意类型的数据排列,底层原理是快速排序。

#include<stdlib.h>

void com_arr(const void* p1, const void* p2) {
	return *(int*)p1 - *(int*)p2;
}

void print(int arr[], int sz) {
	for (int i = 0; i < sz; i++) {
		printf("%d ", arr[i]);
	}
	printf("\n");
}
int main() {
	int arr[] = { 5,2,1,7,3,4,6,14,8 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	
	print(arr, sz);
	qsort(arr, sz, 4, com_arr);
	print(arr, sz);

	return 0;
}

在这里插入图片描述
qsort函数需要4个参数,第一个参数是数组首元素的地址,第二个参数是数组中元素的个数,第三个参数是数组中数据类型的大小,第四个参数是一个函数,作用是实现排序这种数据的方法。
在上述代码中排序整型数组中的数据,那么第四个参数就可以让两个整数相减,返回负数表示第一个元素小于第二个元素,反之大于。

13.使用回调函数模拟实现qsort函数

我们可以采用冒泡排序的方式来实现qsort函数

int Method(const void* p1, const void* p2) {//用以判断两个数时候该交换
	return (*(int*)p1 - *(int*)p2);
}

void Swap(void* p1, void* p2 ,int sz) {
	for (int i = 0; i < sz; i++) {
		char tmp = *((char*)p1 + i);
		*((char*)p1 + i) = *((char*)p2 + i);
		*((char*)p2 + i) = tmp;
		

	}
}
void Maopao(void* arr, int sz, size_t num, int (*method)(void*, void*)) {
	for (int i = 0; i < sz - 1; i++) {
		for (int j = 0; j < sz - 1 - i; j++) {
			if (method((char*)arr + j * num, (char*)arr + (j+1) * num) > 0){
				Swap((char*)arr + j * num, (char*)arr + (j+1) * num, num);
			}
		}
	}
}
int main() {
	int arr[] = { 4,2,6,1,7,5,9,8 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < sz; i++) {
		printf("%d ", arr[i]);
	}
	printf("\n");

	Maopao(arr, sz, sizeof(arr[0]), Method);

	for (int i = 0; i < sz; i++) {
		printf("%d ", arr[i]);
	}
	return 0;
}

在这里插入图片描述

/考研势在必行/

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

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

相关文章

上帝视角看GPU(5):图形流水线里的不可编程单元

【GPU】图形流水线基础【GPU】逻辑上的模块划分【GPU】部署到硬件【GPU】完整的软件栈 前几期我们过了一遍GPU的软硬栈。这次我们将深入GPU图形流水线的一些细节&#xff0c;看看那些不可编程的模块是怎么工作的。 对于GPU的图形流水线来说&#xff0c;最核心最重要的一个组件就…

通过人工智能增强的对话建立有意义的联系

人工智能如何重塑我们的交流&#xff1f;2024年最新对话AI趋势 在技术和人类互动比以往任何时候都更加复杂地交织在一起的时代&#xff0c;人工智能增强的对话已成为建立有意义的联系的关键要素。 这种转变不仅关乎效率&#xff0c;还关乎效率。 这是为了丰富沟通的结构。 在这…

MATLAB--pie函数绘制复杂分类饼图(2)--附案例代码

MATLAB–pie函数绘制复杂分类数据的饼状图 目录 MATLAB--pie函数绘制复杂分类数据的饼状图摘要1. 问题描述2. 具体步骤&#xff1a;3. 绘制结果4. 小结 摘要 在数据可视化中&#xff0c;饼状图是一种常用的展示分类数据的方式。之前&#xff0c;文章介绍了使用MATLAB绘制饼状图…

Vue中的计算属性和方法有什么区别?

Vue.js是一款流行的JavaScript前端框架&#xff0c;提供了丰富的功能和便捷的开发方式。在Vue中&#xff0c;计算属性和方法是常用的两种方式来处理数据和逻辑。但它们之间存在一些区别&#xff0c;本文将详细介绍Vue中计算属性和方法的区别&#xff0c;并通过示例代码加深理解…

654.最大二叉树

这段Java代码实现了一个名为Solution的类&#xff0c;其中包含两个方法&#xff1a;constructMaximumBinaryTree()和constructMaximumBinaryTree1()&#xff0c;目的是从给定的整数数组nums中构建出一个最大二叉树。以下是详细的注释说明&#xff1a; class Solution {// 主方…

GitHub Copilot extension activation error: ‘No access to GitHub Copilot found‘

好不容易学生认证通过了&#xff0c;打开vscode用copilot结果一直报这个错误。我的原因是&#xff1a;还未给copilot授权&#xff0c; 通过了学生认证后要进入这里进行授权&#xff1a;

MCU设计--M3内核整体功能说明

整体架构 内核特性 CM3内核支持3级流水哈佛结构 :数据和指令隔离Blanked SP :两个堆栈,一个堆栈只允许系统操作,另一个堆栈开放给用户。Handler and Thread modes低延迟中断进入和退出支持非对齐操作嵌套中断向量 最大支持1-240个外部中断可设置3-8的优先级可动态配置优先级…

一文了解什么是园区网以及如何部署园区网

目录 一、局域网分类 二、园区网的业务部署内容 1、构建高可靠可冗余网络 2、组播业务的快速开展 3、语音业务的部署 4、网络安全的部署 5、网络管理和维护的应用 一、局域网分类 &#xff08;1&#xff09;园区网&#xff1a; 目的&#xff1a;让各种服务器提供服务 …

mysql学习笔记3——授权操作

利用select查询数据库数据时&#xff0c;可以在后面加上参数 意为限制显示条数 对数据库进行远程操作时&#xff0c;为了避免不同用户操作混乱&#xff0c;要设置不同用户的权限&#xff0c;可以使用 具体格式为 其中*代表任意均可 &#xff0c;这里用户创建采用与授权同时进…

【OJ】求和与计算日期

文章目录 1. 前言2. JZ64 求123...n2.1 题目分析2.2 代码 3. HJ73 计算日期到天数转换3.1 题目分析3.2 代码 4. KY222 打印日期4.1 题目分析4.2 代码 1. 前言 下面两个题目均来自牛客&#xff0c;使用的编程语言是c&#xff0c;分享个人的一些思路和代码。 2. JZ64 求123…n …

北京大学发布,将试错引入大模型代理学习!

引言&#xff1a;探索语言智能的新边界 在人工智能的发展历程中&#xff0c;语言智能始终是一个核心的研究领域。随着大语言模型&#xff08;LLM&#xff09;的兴起&#xff0c;我们对语言智能的理解和应用已经迈入了一个新的阶段。这些模型不仅能够理解和生成自然语言&#x…

20240304-1-操作系统

操作系统 知识体系 Questions 1.进程和线程的区别 进程是系统进行资源分配和调度的基本单位&#xff1b;线程是CPU调度和分派的基本单位。 每个进程都有独立的代码和数据空间&#xff08;程序上下文&#xff09;&#xff0c;程序之间的切换会有较大的开销&#xff1b;线程可…

Java基础 - 7 - 常用API(三)

API&#xff08;全称 Application Programming Interface&#xff1a;应用程序编程接口&#xff09; API就是Java帮我们已经写好的一些程序&#xff0c;如类、方法等&#xff0c;可以直接拿过来用 JDK8 API文档&#xff1a;Java Platform SE 8 一. JDK8之前传统的日期、时间 …

基于springboot+vue的流浪宠物管理系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

js面试 forEach ,map,for ,for in , for of

forEach ,map&#xff0c;for ,for in , for of 1 forEach 回调3个参数value&#xff0c;index&#xff0c;arr&#xff08;原数组&#xff09; 2 map 1&#xff1a;map() 不会改变原始数组 2&#xff1a;函数的作用是对数组中的每一个元素进行处理&#xff0c;返回新的元素…

如何使用生成式人工智能探索视频博客的魅力?

视频博客&#xff0c;尤其是关于旅游的视频博客&#xff0c;为观众提供了一种全新的探索世界的方式。通过图像和声音的结合&#xff0c;观众可以身临其境地体验到旅行的乐趣和发现的喜悦。而对于内容创作者来说&#xff0c;旅游视频博客不仅能分享他们的旅行故事&#xff0c;还…

YOLOv8姿态估计实战:训练自己的数据集

课程链接&#xff1a;https://edu.csdn.net/course/detail/39355 YOLOv8 基于先前 YOLO 版本的成功&#xff0c;引入了新功能和改进&#xff0c;进一步提升性能和灵活性。YOLOv8 同时支持目标检测和姿态估计任务。 本课程以熊猫姿态估计为例&#xff0c;将手把手地教大家使用C…

大模型推荐落地啦!融合知识图谱,蚂蚁集团发布!

引言&#xff1a;电商推荐系统的新突破 随着电子商务平台的蓬勃发展&#xff0c;推荐系统已成为帮助用户在信息过载时代中筛选和发现产品的关键工具。然而&#xff0c;传统的推荐系统主要依赖历史数据和用户反馈&#xff0c;这限制了它们在新商品推出和用户意图转变时的有效性…

Python使用模块和库编程

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 路在脚下&#xff0c;勇往直前&#x…

面试经典150题——简化路径

"A goal is a dream with a deadline." - Napoleon Hill 1. 题目描述 2. 题目分析与解析 2.1 思路一 这个题目开始看起来并不太容易知道该怎么写代码&#xff0c;所以不知道什么思路那就先模拟人的行为&#xff0c;比如对于如下测试用例&#xff1a; 首先 /代表根…