那些 C语言指针 你不知道的小秘密 (3)

news2025/1/17 21:50:39

本篇会加入个人的所谓‘鱼式疯言’
❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言
而是理解过并总结出来通俗易懂的大白话,
我会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的.
🤭🤭🤭可能说的不是那么严谨.但小编初心是能让更多人能接受我们这个概念 !!!
在这里插入图片描述

前言

在上一篇文章中小编主要讲解了

  • 野指针:小编总结了野指针出现的情况并说明其应对 对策

  • assert 断言:小编带着大家试着怎么用 assert 并体会了 assert 防止野指针的益处

  • 传值调用与传址调用: 理解了 传值传址 调用的不同,并说明分别用于哪些 使用场景 更好

  • 二级指针:二级指针的理解,同时并知晓如何 定义解引用

在本篇文章中将继续 和友友们一起探访 指针 的小秘密, 这次咱们只要围绕着 指针数组 的关系展开更进一步的理解

那么友友们让我们开始这次美好的指针之旅吧 💕 💕 💕

目录

  1. 数组名的含义
  2. 数组名的实际运用
  3. 一维数组传参的本质
  4. 指针数组
  5. 指针数组的实际运用

一. 数组名的含义

<1>. 数组名的简介

小爱同学就有疑问了 🤔 🤔 🤔,数组名不就是个名字么,不是相当于 变量名 一样的么,怎么还有含义呢 ? ? ?
是的,是有含义的 ❤️ ❤️ ❤️

数组名啊 !一般而言,本质上的含义就是 首元素的地址
但也有 特殊情况 的出现,下面宝子们就和小编一起去发现吧

我们可以先来做个测试

<2>. 举个栗子

#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);
	return 0;
}

在这里插入图片描述
我们发现数组名和数组首元素的地址打印出的结果一模一样,数组名就是数组首元素(第一个元素)的地

这时候小爱同学就会有疑问?数组名如果是数组首元素的地址,那下面的代码怎么理解呢?

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

在这里插入图片描述
输出的结果是:40,如果arr是数组首元素的地址,那输出应该的应该是 4 / 8 才对。
其实数组名就是 数组首元素(第一个元素) 的地址是对的,但是有两种特殊情况

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

& 数组名,这里的数组名表示整个数组,取出的是 整个数组 的地址 (整个数组的地址数组首元素的地址 是有区别的) 除此之外,任何地方使用数组名,数组名都表示首元素的地址。

鱼式疯言

&sizeof 喜欢 搞特殊 ,贪心的想得到 数组的全部 ,其他都很知足,只需要数组 首元素的地址

#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);
	
	return 0;
}

在这里插入图片描述
三个打印结果一模一样,那小爱同学又纳闷了,那 arr&arr 有啥区别呢 ? 😣 😣 😣

最后小小的证明一下小编的说的到底有没有依据吧

#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	
	//取地址首元素并 +1
	printf("&arr[0] = %p\n", &arr[0]);
	printf("&arr[0]+1 = %p\n", &arr[0] + 1);
	
	//数组名并 +1
	printf("arr = %p\n", arr);
	printf("arr+1 = %p\n", arr + 1);
	
	//取地址数组名并 +1
	printf("&arr = %p\n", &arr);
	printf("&arr+1 = %p\n", &arr + 1);
	return 0;
}

在这里插入图片描述
这里我们发现

&arr[0]&arr[0]+1 相差 4 个字节

arr和 arr+1 相差 4 个字节

是因为 &arr[0]arr 都是首元素的地址,+1 就是跳过 4个字节(一个元素)。

但是 &arr&arr+1 相差 40(十六进制的地址就相差28) 个字节

这就是因为 &arr是数组的地址,+1 操作是跳过整个数组的。

到这里大家应该搞清楚 数组名 的意义了吧。
数组名是 数组首元素 的地址,但是有 2种 特殊情况

二. 数组名的实际运用

当我们需要输入输出数组时 , 我猜小爱同学可能会这样写

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	//输入

	int i = 0;
	
	int sz = sizeof(arr) / sizeof(arr[0]);
	//输入

	for (i = 0; i < sz; i++)
	{

		scanf("%d", &arr[i]);

	}

	//输出
	for (i = 0; i < sz; i++)
	{

		printf("%d ",arr[i]);

	}
	return 0;
}

在这里插入图片描述
当我们好学的小爱同学,看看小编这样写是不是也可以呢 💕 💕 💕

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	//输入

	int i = 0;
	
	int sz = sizeof(arr) / sizeof(arr[0]);
	//输入

	for (i = 0; i < sz; i++)
	{
		//利用数组名是首元素地址
		//数组名在 + i 地址进行右移
		scanf("%d", arr + i);
		//scanf("%d", &arr[i]);
		//也可以这样写
	}

	//输出
	for (i = 0; i < sz; i++)
	{
		//得到每个元素的地址
		//解引用打印数据
		printf("%d ", *(arr + i));
		
		//printf("%d ",arr[i]);
		//也可以这样写
	}
	return 0;
}

在这里插入图片描述
是的,友友们这下就明白原来我们数组的输入输出还能这样玩啊 😎 😎 😎
输入时

  1. 对该下标的元素进行 &
  1. 利用数组名是 首元素 的地址,进行 指针加减偏移 来操作

输出时:

  1. 除了我们想数组的下标访问
  1. 利用数组名是 首元素的地址 ,再 指针加减偏移 ,我们也可以通过得到我们 *每一个元素的的地址 *,来 * 解引用访问

鱼式疯言

*(arr+i) <==> &arr[i]

arr+i <==> &arr[i]

三. 一维数组传参的本质

数组我们学过了,之前也讲了,数组是可以传递给函数的,这个小节我们讨论一下一维数组传参的本质。
首先小伙伴可以思考一个问题 ? ? ?
我们之前都是在 函数外部 计算数组的元素个数
那我们可以把函数传给一个函数后, 函数内部 求数组的元素个数吗?
不妨让我们试一下吧 😃 😃 😃

<1>. 函数外部求数组长度

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	
	//在 test 函数外部求数组长度
	int sz1 = sizeof(arr) / sizeof(arr[0]);
	
	// 验证其大小
	printf("数组长度为 : %d\n", sz1);
	
	//test(arr);
	
	return 0;
}

在这里插入图片描述
在函数外部是完全正确的,没有问题 。

<2>. 函数内部

void test(int arr[10])
{
	int sz1 = sizeof(arr) / sizeof(arr[0]);
	printf("数组长度为 : %d\n", sz1);
}

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

在这里插入图片描述
我们发现在函数内部是没有正确获得数组的元素个数。
小爱同学就深思了,这到底是什么原因呢 ? ? ?
竟然我们今天要学习 数组传参的本质 ,那这个 内在的原因 肯定 和 数组传参 脱不了干系

友友们可以思考下
我们上一节提过数组名本质上是啥,是首元素的地址吧
那么有没有一种可能数组名在传参的时候根本没有把整个数组传过去,而是传了 首元素的地址
居然我们提出了猜想,不妨我们用代码来验证下吧

#include<stdio.h>

// 参数写成数组形式,本质上还是指针
void test(int arr[])
{
	//验证一下
	//在 X86 的环境下 
	//一个指针大小是4个字节
	printf("%d\n", sizeof(arr));
}


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

	test(arr);
	return 0;
}

在这里插入图片描述
猜想验证成功,宝子们真聪明 💖 💖 💖

实践证明,我们本质上数组传参本质上传递的是数组首元素的地址

如果是这样子的话,那么我们的代码以后是否可以这样写呢 ! ! !

#include<stdio.h>
// 参数写成指针形式
void test1(int* arr)
{
	printf("%d\n", sizeof(arr));
	//计算一个指针变量的大小

	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *(arr + i));
	}

}


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

	test1(arr);
	return 0;
}

在这里插入图片描述
小伙伴快看这样写是不是也是种思路呢! ! !

但是,我们知道了本质,

这不禁也知道sizeof(arr) 计算的是一个地址的大小(单位字节)而不是数组的大小(单位字节)。

正是因为函数的参数部分是本质是 指针,所以在函数内部是没办法求的 数组元素个数

那我们该怎么解决呢,如果是这样的 😣 😣 😣

<3>. 传参方案

唯一的解决办法就是把对应的数组长度现在函数外部求好,利用 参数 传过去。

#include<stdio.h>

// 参数写成指针形式
// sz 来接收数组长度
void test1(int* arr,int sz)
{
	//利用最大长度来输出打印我们的数组元素
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", *(arr + i));
	}

}


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

	// 函数外部求数组长度
	int slz = sizeof(arr) / sizeof(arr[0]);
	
	// 传入 slz 长度
	test1(arr, slz);
	
	return 0;
}

在这里插入图片描述

鱼式疯言

一维数组传参还是 首元素地址 ,数组长度先算好,额外加一份 参数再传

四. 指针数组

<1>. 指针数组的简介

首先我们来思考一个问题

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

友友们可以类比一下

整型数组 ,是存放 整型 的数组

字符数组 是存放 字符 的数组

指针数组 呢?是存放 指针 的数组。

C语言中,指针数组是指一个数组,它的每个元素都是一个指针变量。每个指针变量可以指向不同的数据类型或对象
指针数组可以声明为以下形式:

dataType *arrayName[size];

dataType 表示指针变量所指向的 数据类型

arrayName 是数组的 名称

size 是数组的 大小

在这里插入图片描述
小爱同学是不是还是有点困惑呢 😣 😣 😣
没关系 没关系
不妨我们画个图理解一下吧 ! ! !
在这里插入图片描述

  • 指针数组的每个元素都是用来存放 地址(指针) 的。
    如下图 :
    在这里插入图片描述

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

鱼式疯言

很多个 相同类型指针 哪里放,指针数组 管饭吃

五. 指针数组的实际运用

想必大家都学过二维数组吧,如果让宝子们写个二维数组,你可能会这样写 ! ! !

<1>. 二维数组

 #include<stdio.h>

int main()
{
	//二维数组初始化
	int a[3][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} };
	
	int i = 0,j=0;

	//外循环为行数
	for ( i = 0; i < 3; i++)
	{
		//内循环为列数
		for ( j = 0; j < 4; j++)
		{
			printf("%d ", a[i][j]);
		}
		printf("\n");
	}

	return 0;
}

在这里插入图片描述
但今天小编的主要想法还是带着小伙伴一起用指针数组 来模拟咱们的 二维数组
小伙伴都眼睛睁大咯,可不要眨眼哦 😁 😁 😁

<2>. 模拟二维数组

宝子们请先看代码哦


//指针数组模拟二维数组
#include<stdio.h>

int main()
{
	int a1[5] = {1,2,3,4,5};
	int a2[5] = {2,3,4,5,6};
	int a3[5] = {3,4,6,7,8};
	
	//将每个一维数组的首地址放入指针数组中
	int* a[3] = { {a1},{a2},{a3} };
	
	int i = 0, j = 0;
	
	//先找到第几个 一维数组
	for (i = 0; i < 3; i++)
	{
		//再找到该 一维数组中的每一个元素
		for ( j = 0; j < 5; j++)
		{
		
			printf("%d ", a[i][j]);
		}
		printf("\n");
	}

	return 0;
}

在这里插入图片描述
友友们是不是也都看到了

我们的把每个一维数组的地址都放在指针数组中,通过指针数组的下标 i 我们可以访问 哪个一维数组

再通过该一维数组的下标 j 来访问具体元素,从而达到 二维数组 的效果 。

鱼式疯言

人狠话不多
看图说话
在这里插入图片描述

先走指针,后走一维数组

总结

  • 数组名的含义 :知晓了数组名的一般含义,以及他的两种特殊情况
  • 数组名的实际运用 : 我们通过指针的形式更进一步利用数组名自身含义的特点来输入输出
  • 一维数组传参的本质 : 传参的本质还是数组名的自身含义,以及数组长度如何解决的办法
  • 指针数组 : 我们更了解了指针数组的基本概念
  • 指针数组的实际运用 : 通过我们了解的指针数组的基本概念来对实践我们的理解

如果觉得小编写的还不错的咱可支持三连下 (定有回访哦) , 不妥当的咱请评论区指正

希望我的文章能给各位宝子们带来哪怕一点点的收获就是 小编创作 的最大动力 💖 💖 💖

在这里插入图片描述

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

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

相关文章

【从0上手Cornerstone3D】如何使用CornerstoneTools中的工具之同步器

同步器&#xff08;Synchronizers&#xff09;可以使多个视图同步响应同一个工具的操作&#xff0c;例如我们在MPR视图下&#xff0c;同步操作三个视图的缩放程度、windowLevel等等 一个同步器必须需要以下几个部分才可以执行 一个监听事件&#xff08;什么情况下触发同步&…

STL之stack+queue的使用及其实现

STL之stackqueue的使用及其实现 1. stack&#xff0c;queue的介绍与使用1.1stack的介绍1.2stack的使用1.3queue的介绍1.4queue的使用 2.stack&#xff0c;queue的模拟实现2.1stack的模拟是实现2.2queue的模拟实现 3.总结 所属专栏&#xff1a;C“嘎嘎" 系统学习❤️ &…

【Spring】springmvc如何处理接受http请求

目录 ​编辑 1. 背景 2. web项目和非web项目 3. 环境准备 4. 分析链路 5. 总结 1. 背景 今天开了一篇文章“SpringMVC是如何将不同的Request路由到不同Controller中的&#xff1f;”&#xff1b;看完之后突然想到&#xff0c;在请求走到mvc 之前服务是怎么知道有请求进来…

[BUUTF]-PWN:wdb2018_guess解析

查看保护 查看ida 这道题并不复杂&#xff0c;只是要注意一点细节 完整exp&#xff1a; from pwn import* from LibcSearcher import* pprocess(./guess) premote(node5.buuoj.cn,28068) puts_got0x602020payloadba*0x128p64(puts_got) p.sendlineafter(bPlease type your gu…

基于语义解析的知识图谱问答系统

目录 前言1 背景介绍2 语义解析的核心技术2.1 自然语言处理&#xff08;NLP&#xff09;2.2 语义表示学习2.3 实体关系抽取 3 语义解析的基本步骤3.1 短语检测3.2 资源映射3.3 语义组合3.4 逻辑表达式生成 4 处理与知识图谱无关的问句4.1 Bridging技术4.2 确定谓词4.3 Paraphra…

杨中科 配置系统

1、配置系统入门 说明 1、传统Web.config配置的缺点&#xff0c;之前DI讲到过 2、为了兼容&#xff0c;仍然可以使用Web.config和ConfigurationManager类&#xff0c;但不推荐。 3、.NET 中的配置系统支持丰富的配置源&#xff0c;包括文件(json、xml、ini等)、注册表、环境变…

Redis保证数据不丢失的手段

Redis 保证数据不丢失的主要手段有两个&#xff1a; 持久化集群运行 我们分别来看它们两的具体实现细节。 # 1.Redis 持久化 持久化是指将数据从内存中存储到持久化存储介质中&#xff08;如硬盘&#xff09;的过程&#xff0c;以便在程序重启或者系统崩溃等情况下&#xf…

1898_野火FreeRTOS教程阅读笔记_链表操作

1898_野火FreeRTOS教程阅读笔记_链表操作 全部学习汇总&#xff1a; g_FreeRTOS: FreeRTOS学习笔记 (gitee.com) 新的节点的插入&#xff0c;影响到的是链表中最后一个元素的后继以及当前被插入元素的前驱、后继以及归属属性。具体的操作效果为&#xff1a;新的节点更新自己的前…

101. 对称二叉树 - 力扣(LeetCode)

题目描述 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 题目示例 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true 解题思路 首先想清楚&#xff0c;判断对称二叉树要比较的是哪两个节点&#xff0c;要比较的可不是左右节点&#xff01; 对于…

SAP-PS-001-006问题预算占用与订单实际金额不一致

前言 PS模块最复杂的业务场景主要就是ETO&#xff08;Engineering-To-Order&#xff09;&#xff0c;也就是边设计边生产边采购的三边业务。 意味着从前端设计开始的成本就已经要进行收集&#xff0c;其次对于大型非标设备的生产发货只是一个环节&#xff0c;发货后还会涉及到现…

Java项目maven打包的包名设置(finalname标签的使用)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

年-月-日的输入方法

大家对于输入的函数一定有所认识&#xff0c;比如c中位于 #include <iostream> 中的 cin 函数&#xff0c;这个函数输入单个十分好用&#xff0c;但是对于年月日这种较为复杂的就行不通了&#xff0c;就只能输入最前面的一个 那怎么输入像这样的年月日呢 答案就是用 scan…

JVM-双亲委派机制

双亲委派机制定义 双亲委派机制指的是&#xff1a;当一个类加载器接收到加载类的任务时&#xff0c;会自底向上查找是否加载过&#xff0c; 再由顶向下进行加载。 详细流程 每个类加载器都有一个父类加载器。父类加载器的关系如下&#xff0c;启动类加载器没有父类加载器&am…

C++2024寒假J312实战班2.6

错题列表&#xff1a; #1Playlist #2分数线划定 #3Made Up #4图书管理员 #1Playlist 我们来介绍滑动窗口的写法&#xff1a; 1、使用一个滑动窗口k[l,r)在歌曲列表中移动。 2、同时利用一个unordered_set S来检测窗口中的歌曲是否有重复。如果窗口右端的歌曲在窗口内没有重复…

LLM应用开发与落地:流式响应

一、背景 最近智能客服产品给到一个游戏客户那边&#xff0c;客户那边的客服负责人体验后认为我们产品回答的准确率是还是比较高的。同时&#xff0c;他反馈了几个需要改进的地方&#xff0c;其中一个就是机器人回复慢。机器人回复慢有很多原因&#xff0c;也有优化方式&#…

2019 年全国职业院校技能大赛高职组 “信息安全管理与评估”赛项任务书(笔记详解)

1. 网络拓扑图 2. IP 地址规划表 3. 设备初始化信息 阶段一 任务 1:网络平台搭建 1、根据网络拓扑图所示,按照 IP 地址参数表,对 DCFW 的名称、各接口IP 地址进行配置。 2、根据网络拓扑图所示,按照 IP 地址参数表,对 DCRS 的名称进行配置,创建 VLAN 并将相应接口划入 …

C#,雷卡曼数(Recamán Number)的算法与源代码

1 雷卡曼数&#xff08;Recamn Number&#xff09; 雷卡曼数&#xff08;Recamn Number&#xff09;&#xff0c;即Recaman序列被定义如下&#xff1a; (1) a[0]0; (2) 如果a[m-1]-m>0并且这个值在序列中不存在&#xff0c;则a[m]a[m-1]-m; (3) 否则a[m]a[m-1]m; 雷卡曼序…

[UI5 常用控件] 08.Wizard,NavContainer

文章目录 前言1. Wizard1.1 基本结构1.2 属性1.2.1 Wizard&#xff1a;complete1.2.2 Wizard&#xff1a;finishButtonText1.2.3 Wizard&#xff1a;currentStep1.2.4 Wizard&#xff1a;backgroundDesign1.2.5 Wizard&#xff1a;enableBranching1.2.6 WizardStep&#xff1a;…

【Linux】vim的基本操作与配置(下)

Hello everybody!今天我们继续讲解vim的操作与配置&#xff0c;希望大家在看过这篇文章与上篇文章后都能够轻松上手vim! 1.补充 在上一篇文章中我们说过了&#xff0c;在底行模式下set nu可以显示行号。今天补充一条&#xff1a;set nonu可以取消行号。这两条命令大家看看就可…

LeetCode-第28题-找出字符串中第一个匹配项的下标

1.题目描述 给你两个字符串 haystack 和 needle &#xff0c;请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标&#xff08;下标从 0 开始&#xff09;。如果 needle 不是 haystack 的一部分&#xff0c;则返回 -1 。 2.样例描述 3.思路描述 可以让字符串 …