C语言学习NO.10-指针(二)数组名的理解,使用指针访问数组,一维数组传参的本质,冒泡排序,二级指针,指针数组,指针数组模拟二维数组

一、数组名的理解

//使用指针访问数组的内容
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int* p = &arr[0];

这里我们使用&arr[0]的方式拿到了数组第一个元素的地址,但是其实数组名本来就是地址,而且是数组首元素的地址

#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("&arr[0] = %p\n", &arr[0]);
	printf("arr     = %p\n", arr);
	printf("arr     = %p\n", arr+1);
	printf("arr     = %p\n", &arr);
	printf("arr     = %p\n", &arr+1);
	return 0;
}

我们能发现数组名和数组首元素的地址是一致的,数组名就是数组首元素(第一个元素)的地址

arr是数组首元素的地址,arr+1与arr的地址相差4个字节(一个整型四个字节);

&arr是整个数组的地址,&arr+1与&arr相差了40个字节,正好是一个数组所占的字节。

sizeof(数组名),sizeof中单独放数组名,这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节。

二、使用指针访问数组

使用指针访问数组

#include <stdio.h>
int main()
{
     int arr[10] = {0};//创建一个数组
     int i = 0;
     int sz = sizeof(arr)/sizeof(arr[0]);//计算数组中元素个数
        
     //输⼊
     int* p = arr;
     for(i=0; i<sz; i++)
     {
         scanf("%d", p+i);
         //scanf("%d", arr+i);//也可以这样写
     }
        
     //输出
     for(i=0; i<sz; i++)
     {
     	printf("%d ", *(p+i));
     }
     return 0; 
}

这个代码搞明白后,我们再试一下,如果我们再分析一下,数组名arr是数组⾸元素的地址,可以赋值给p,其实数组名arr和p在这里是等价的。那我们可以使用arr[i]可以访问数组的元素,那p[i]是否也可以访问数组呢?

#include <stdio.h>
int main()
{
     int arr[10] = {0};
     int i = 0;
     int sz = sizeof(arr)/sizeof(arr[0]);
        
     //输⼊
     int* p = arr;
     for(i=0; i<sz; i++)
     {
         scanf("%d", p+i);
         //scanf("%d", arr+i);//也可以这样写
     }
        
     //输出
     for(i=0; i<sz; i++)
     {
     	printf("%d ", p[i]);
     }
     return 0; 
}

在第18行的地方,将*(p+i)换成p[i]也是能够正常打印的,所以本质上p[i] 是等价于 *(p+i)。同理arr[i] 应该等价于 *(arr+i),数组元素的访问在编译器处理的时候,也是转换成首元素的地址+偏移量求出元素的地址,然后解引用来访问的。

三、一维数组传参的本质

现在在函数内部计算数组的元素个数,我们可以吧函数传给一个函数后,在函数的内部求数组的元素个数。

#include <stdio.h>
void test(int arr[])
{
	int sz2 = sizeof(arr) / sizeof(arr[0]);
	printf("sz2 = %d\n", sz2);
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz1 = sizeof(arr) / sizeof(arr[0]);
	printf("sz1 = %d\n", sz1);
	test(arr);
	return 0;
}

可以看到,在函数内部并没有得到正确的数组元素个数。

为什么呢?

因为数组名是数组首元素的地址;那么在数组传参的时候,传递的是数组名,也就是说本质上数组传参本质上传递的是数组首元素的地址

所以函数形参的部分理论上应该使用指针变量来接受首元素的地址。那么在函数内部我们写sizeof(arr)计算的是一个地址的大小(单位字节)而不是数组的大小(单位字节)。正式因为函数的参数部分是本质是指针,所以在函数内部是没办法求的数组元素个数的。

#include <stdio.h>

void test1(int arr[])//参数写成数组形式,本质上还是指针 
{
	printf("%d\n", sizeof(arr));
}

void test2(int* arr)//参数写成指针形式
{
	printf("%d\n", sizeof(arr));//计算⼀个指针变量的⼤⼩ 
}

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	test1(arr);
	test2(arr);
	return 0;
}

结果为:指针变量在x64环境下大小为8。

在x86环境下:

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

四、二级指针

指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里?这就是二级指针

对于二级指针的运算有:

  • *ppa通过对ppa中的地址进行解引用,这样找到的是pa,*ppa其实访问的就是pa。
int b = 20;
*ppa = &b;//等价于 pa = &b;
  • **ppa先通过*ppa找到pa,然后对pa进行解引用操作:*pa,那找到的是a。
**ppa = 30;
//等价于*pa = 30;
//等价于a = 30;

五、指针数组

指针数组是指针还是数组?

类比一下,整型数组是存放整型的数组,字符数组是存放字符的数组。

那么指针数组时存放指针的数组吗?

指针数组的每个元素都是用来存放地址(指针)的。

指针数组的每个元素都是地址,也可以指向一块区域。

六、指针数组模拟二维数组

#include <stdio.h>
int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };
	//数组名是数组⾸元素的地址,类型是int*的,就可以存放在parr数组中
	int* parr[3] = { arr1, arr2, arr3 };
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 5; j++)
		{
			printf("%d ", parr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

parr[i]是访问parr数组的元素,parr[i]找到的数组元素指向了整型一维数组,parr[i][j]就是整型一维数组中的元素。

上述的代码模拟出二维数组的效果,实际上并非完全是二维数组,因为每一行并非是连续的。

七、二维数组传参的本质

二维数组的需要传参给一个函数的时候,我们是这样写的:

#include <stdio.h>
void test(int a[3][5], int r, int c)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < r; i++)
	{
		for (j = 0; j < c; j++)
		{
			printf("%d ", a[i][j]);
		}
		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;
}

这里的实参是二维数组,形参也是二维数组,如果使用指针形式呢?

将二维数组的数组名看作为第一行的地址,相当于一维数组名是数组首元素地址。

第一行的一维数组的类型是int[5],所以第一行的地址的类型就是数组指针类型int(*)[5]。

那就意味着二维数组传参本质上也是传递了地址,传递的是第一行这个一位数组的地址那么形参也是可以携程指针形式的。

#include <stdio.h>

void test(int* str, int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			printf("%d ", *(*(str + i) + j)));
		}
		printf("\n");
	}
}

int main()
{
	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	//int sz = sizeof(arr)/sizeof(arr[])
	test(arr, 3, 5);
	return 0;
}

总结:二维数组传参,形参的部分可以写成数组,也可以次而成指针形式。

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

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

相关文章

全国“兽医”专业大学生知识竞赛策划方案

一、初赛&#xff1a;参赛者通过网络平台进行答题。 各校大学生均可在“大赛官网”登录&#xff0c;填写真实姓名、学校、联系方式、身份证号、学号等信息&#xff0c;即可答题参赛。 系统将随机从题库中抽取50道题&#xff0c;满分100分&#xff0c;其中&#xff0c;单选题20…

Prometheus API 使用介绍|收藏

​ &#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试…

通过windows cng api 实现rsa非对称加密

参考&#xff1a; 1,使用 CNG 加密数据 - Win32 apps | Microsoft Learn 2,不记得了 &#xff08;下文通过cng api演示rsa加密&#xff0c;不做原理性介绍&#xff09; 相对于aes等对称加密算法&#xff0c;rsa加密算法不可逆性更强。非对称加密在通常情况下&#xff0c;使…

STM32烧录口锁死问题解决

前言 我在给STM32单片机下载程序时&#xff0c;出现下载成功一次后&#xff0c;后面就下载不了了&#xff0c;识别不到下载器设备&#xff0c;经过排查&#xff0c;最终确认是代码配置有误&#xff0c;导致板子烧录口锁死&#xff0c;下面我将把问题的出现到问题的解决进行复现…

LZ码基本概念

LZ码是一种无损压缩算法&#xff0c;由Lempel和Ziv两位计算机科学家提出并命名。它是一种基于字典的压缩方法&#xff0c;可以将数据有效地压缩存储&#xff0c;同时实现高效的解压缩。 LZ码的基本概念是利用字典来存储先前遇到的字符串&#xff0c;然后用较短的代表符号来表示…

R软件包ConsensusCluster进行共识聚类(Consensus Clustering)

从下面论文看到这个方法&#xff1a; Wang, Xin, et al. "Deep learning using bulk RNA-seq data expands cell landscape identification in tumor microenvironment." Oncoimmunology 11.1 (2022): 2043662. 这篇论文基于 AI 方法对 bulk RNA-seq 数据识别肿瘤微环…

2023年12月GESP Python五级编程题真题解析

【五级编程题1】 【试题名称】&#xff1a;小杨的幸运数 【问题描述】 小杨认为&#xff0c;所有大于等于a的完全平方数都是他的超级幸运数。 小杨还认为&#xff0c;所有超级幸运数的倍数都是他的幸运数。自然地&#xff0c;小杨的所有超级幸运数也都是幸运数。 对于一个…

AI 视频 | 又一款 AI 视频工具火爆全网!DomoAI 实测体验如何?

一、引言 前几期介绍了几款常用的 AI 视频工具&#xff1a;Moonvalley、Runway Gen-2、Stable Video Diffusion&#xff0c;NeverEnds&#xff0c;对 AI 视频工具感兴趣的小伙伴可以移步之前的几篇文章。 程序员X小鹿&#xff1a;【AI视频】免费的 AI 视频生成工具 Moonvalley…

纯搬运 solidworks 2021卸载方法,怎么完全彻底卸载删除清理干净solidworks 2021各种残留注册表和文件?

纯搬运 solidworks 2021卸载方法&#xff0c;怎么完全彻底卸载删除清理干净solidworks 2021各种残留注册表和文件&#xff1f; 网址&#xff1a; solidworks 2021卸载方法&#xff0c;怎么完全彻底卸载删除清理干净solidworks 2021各种残留注册表和文件&#xff1f; solidworks…

js显示实时时间

文章目录 一、效果二、思路三、最后 一、效果 用JS实现XXXX年XX月XX日 星期X XX时XX分XX秒 效果 效果 &#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>time</title><script t…

基于Java SSM框架实现在线课程教育资源考试管理系统项目【项目源码+论文说明】

基于java的SSM框架实现在线课程教育资源考试管理系统演示 摘要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线教育资源管理系统&#xff0c;主要的模块包括管理员&#xff1b;个人中心、学生…

Android wifi基础知识点

1、什么是 CSMA/CA &#xff1f; 以太网用 CSMA/CD 进行传输控制&#xff0c;而 IEEE 802.11 的 WLAN 采用的是 CSMA/CA 。 CSMA/CD &#xff0c;全称 Carrier Sense Multiple Access with Collision Detection &#xff0c;即 载波侦听多路访问/冲突检测协议。 载波侦听(Ca…

Python课程设计-图书管理系统

Python课程设计-图书管理系统 摘要第一章 绪论1.1 开发环境及技术1.2 系统实现功能描述 第二章 功能详细设计与实现2.1 系统框架各层次实现2.1.1 可视页面设计2 数据库设计3 逻辑流程设计 2.2 主要功能的设计与实现1 功能 1用户登录2 功能 2展示图书3 功能 3添加图书4 功能 4删…

个性化邮件营销策略:提升销售额的有效方法

事实上&#xff0c;电子邮件营销人员一直将个性化视为让受众产生强烈参与感的最佳方式之一。对于很多营销人员来说&#xff0c;实施个性化甚至不再是一种选择&#xff0c;而是培养和吸引潜在客户和联系人的必要条件。因此&#xff0c;今天我们将一起来讨论一些成功电子邮件营销…

YZ系列工具之YZ03:高版本Excel的自定义菜单

我给VBA下的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套一部VBA手册&#xff0c;教程分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的…

整数规划-分支定界法

分支定界法 分支定界法由来分支定界法原理分支定界法思想疑惑or改进&#xff1f; 分支定界法由来 谨以此博客作为学习期间的记录 在生活中&#xff0c;整数规划(IP)或者混合整数规划(MIP)往往要比单纯的线性规划(LP)应用更为广泛。生产计划、库存规划等&#xff0c;都有着变量…

STL中优先队列的模拟实现与仿函数的介绍

文章目录 仿函数优先队列的模拟实现 仿函数 上回我们说到&#xff0c;优先队列的实现需要用到仿函数的特性 让我们再回到这里 这里我们发现他传入的用于比较的东西竟然是一个类模板&#xff0c;而不是我们所见到的函数 我们可以先创建一个类&#xff0c;用于比较大小 struc…

【toolschain algorithm cpp ros】cpp工厂模式实现--后续填充具体规划算法,控制器版的已填充了算法接入了仿真器

写在前面 现在局势危机&#xff0c;于是想复习一下之前写的设计模式&#xff0c;之前提到&#xff0c;做过一个闭环仿真器&#xff08;借用ros&#xff09;&#xff0c;见https://blog.csdn.net/weixin_46479223/article/details/134864123我的控制器的建立遵循了工厂模式&…

Excel 获取当前行的行数

ROW() 获取当前行 ROW()1 获取当前行然后支持二次开发

视频号小店一件代发怎么做?

我是电商珠珠 视频号团队于22年7月开始发展自己的电商平台-视频号小店。由于是新平台&#xff0c;并在今年开始有很多人关注。 所以平台相对来说并没有什么很严格的规则&#xff0c;特别是对于无货源一件代发这一块&#xff0c;没有什么成文的规定。 对于商家来说同样可以依…