深入理解指针2:数组名理解、一维数组传参本质、二级指针、指针数组和数组指针、函数中指针变量

目录

1、数组名理解

2、一维数组传参本质

3、二级指针

4、指针数组和数组指针

5、函数指针变量


1、数组名理解

首先来看一段代码:

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中单独放数组名,这里的数组表示整个数组,计算的是整个数组的大小,单位是字节
  • &数组名。这里的数组名表示整个数组,取出的是整个数组的地址(不是首元素的地址)。

除此之外,任何地方使用数组名,数组名都表示首元素的地址。

再来看一段代码:

#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[0]+1 = %p\n", &arr[0] + 1);
	printf("arr = %p\n", arr);
	printf("arr+1 = %p\n", arr + 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就是跳过1个元素。

但是&arr 和 &arr + 1 相差40个字节,这就是因为&arr是数组的地址,+1 操作是跳过整个数组的。

2、一维数组传参本质

先看一段代码:

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

输出结果

在数组传参的时候,传递的是数组名,也就是说本质上数组传参本质上传递的是数组首元素的地址。所以函数形参的部分理论上应该使用指针变量来接受首元素的地址。

3、二级指针

假设有下面的声明:

int zippo[4][2];//内含int数组的数组
  • 因为zippo是数组首元素的地址,所以zippo的值和&zippo[0]相同。而zippo[0]本身是一个内含两个整数的数组,所以zippo[0]的值和它首元素(一个整数)的地址(即&zippo[0][0]的值相同。简而言之,zippo[0]是一个占用一个int大小对象的地址。所以zippo和zippo[0]的值相同。
  • 给指针或地址加1,其值会增加对应类型大小的值。在这方面,zippo和zippo[0]不同,因为zippo指向的对象占用了两个int大小,而zippo[0]指向的对象只占用一个int大小。因此,zippo+1和zippo[0]+1的值不同。
  • zippo[0]是该数组首元素(zippo[0][0])的地址,所以 *(zippo[0])表示存储在zippo[0][0]上的值。与此类似,*zippo代表该数组首元素(zippo[0])的值,但是zippo[0]本身是一个int类型值的地址。该值的地址是&zippo[0][0]。所以*zippo就是&zippo[0][0]。**zippo与*&zippo[0][0]等价。

#include <stdio.h>
int main()
{
	int zippo[4][2] = { {2,4},{6,8},{1,3},{5,7} };

	printf("   zippo = %p,      zippo + 1 = %p\n", zippo, zippo + 1);
	printf("zippo[0] = %p,   zippo[0] + 1 = %p\n", zippo[0], zippo[0] + 1);
	printf("  *zippo = %p,     *zippo + 1 = %p\n", *zippo, *zippo + 1);
	printf("zippo[0][0] = %d\n", zippo[0][0]);
	printf("  *zippo[0] = %d\n", *zippo[0]);
	printf("    **zippo = %d\n", **zippo);
	printf("zippo[2][1] = %d\n", zippo[2][1]);
	printf("*(*(zippo+2) + 1) = %d\n", *(*(zippo + 2) + 1));

	return 0;
}

运行结果:

zippo 二维数组首元素的地址(每个元素都是内含两个 int 类型元素的一维数组)

zippo+2  二维数组的第 3个元素 (即一维数组)的地址

*(zippo+2) 二维数组的第 3个元素(即一维数组)的首元素(一个 int 类型的值)地址

*(zippo+2) + 1  二维数组的第 3个元素(即一维数组)的第 2个元素(也是一个 int 类型的值)地址

*(*(zippo+2) + 1)  二维数组的第 3个一维数组元素的第 2个int 类型元素的值,即数组的第3行第2
列的值 (zippo[2][1])

4、指针数组和数组指针

指针数组的本质其实是数组,它里面存放的是指针,数组中每个元素的类型是指针类型(int*或char*)。

指针数组的创建方式:

int * arr[10] = {0};

数组指针的本质其实是指针变量,存放的是数组的地址,能够指向数组的指针变量。

数组指针的创建方式:

int (*pt)[10];

 数组指针的初始化:

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

字符指针变量:

char* p = "hello";

这里本质上是把字符串“hello”,首字符h的地址存放在p中。

5、函数指针变量

我们先来看一段代码:

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

运行结果:

这说明函数是有地址的,和数组一样,函数名就是函数的地址。当然也可以通过&函数名的方式后的函数的地址。

如果我们要将函数的地址存放起来,就得创建函数指针变量。

void test()
{
	printf("hehe\n");
}
void (*pf1)() = &test;
void (*pf2)() = test;

int Add(int x, int y)
{
	return x + y;
}
int(*pf3)(int, int) = Add;
int(*pf3)(int x, int y) = &Add;//x和y写上或者省略都是可以的

函数指针变量的使用:通过函数指针调用指针指向的函数。

#include <stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int(*pf3)(int, int) = Add;
	printf("%d\n", (*pf3)(2, 3));
	printf("%d\n", pf3(3, 5));
	return 0;
}

结果是  5      8

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

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

相关文章

Astra深度相机在Ubuntu18.04系统下实现相机标定

问题&#xff1a; 当使用Astra相机的启动的指令启动相机后&#xff0c;使用rviz查看相机所发布的rgb数据时&#xff0c;在终端会出现如下的提示信息&#xff1a; Camera calibration file /home/car/.ros/camera_info/rgb_Astra_Orbbec.yaml not found. Camera calibration fil…

深度学习基础之一:机器学习

文章目录 深度学习基本概念(Basic concepts of deep learning)机器学习典型任务机器学习分类 模型训练的基本概念基本名词机器学习任务流程模型训练详细流程正、反向传播学习率Batch size激活函数激活函数 sigmoid 损失函数MSE & M交叉熵损失 优化器优化器 — 梯度下降优化…

10.枚举

1.背景及定义 枚举是在JDK1.5以后引入的。 主要用途是&#xff1a; 将一组常量组织起来&#xff0c; 在这之前表示一组常量通常使用定义常量的方式&#xff1a; public static final int RED 1; public static final int GREEN 2; public static final int BLACK 3; 但是…

Java中线程安全集合类

Java中线程安全类可以分为三大类 遗留的线程安全集合如Hashtable、vectorJava.util.concurrent.*&#xff08;包含三类关键词&#xff1a;Blocking、CopyOnWrite、Concurrent&#xff09;使用Collections装饰的线程安全集合&#xff0c;如&#xff1a; Collections.synchroniz…

lottery-攻防世界

题目 flag在这里要用钱买&#xff0c;这是个赌博网站。注册个账号&#xff0c;然后输入七位数字&#xff0c;中奖会得到相应奖励。 githacker获取网站源码 &#xff0c;但是找到了flag文件但是没用。 bp 抓包发现api.php&#xff0c;并且出现我们的输入数字。 根据题目给的附…

搭建Flutter开发环境、从零基础到精通(文末送书【北大出版社】)

目录 搭建开发环境 1. 下载Flutter SDK 2. 设置镜像地址及环境变量 3. 安装与设置Android Studio 4. 安装Visual Studio Code与Flutter开发插件 5. IDE的使用和配置 6. 安装Xcode 7. 检查Flutter开发环境 好书推荐 内容简介 作者简介 搭建开发环境 Flutter可以跨平…

机器人客户端如何配置同步消息至多个群中

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。 前言 由于微信群的人数&#xff0c;最多是500人&#xff0c;如果有人的业务做的大&#xff0c;可能会同步创建好多个群&#xff0c;但是资料的不想多个群一起发&#xff0c;发给某个群&a…

服务注册自治,降低 ASP.NET Core Web API 依赖注入的耦合度和复杂度

前言 在软件的实际开发中&#xff0c;一个软件通常由多个项目组成&#xff0c;这些项目都会直接或者间接被主 ASP.NET Core 项目引用。 这些项目中通常都会用到若干个被注入的服务&#xff0c;因此我们需要在主 ASP.NET Core 项目的 Program.cs 中注册这些服务。这样不仅会增…

【深入理解Java IO流0x03】解读Java最基本的IO流之字节流InputStream、OutputStream

在开始前&#xff0c;我们再来回顾一下这张图&#xff1a; 本篇博客主要为大家讲解字节流。 我们都知道&#xff0c;一切文件&#xff08;文本、视频、图片&#xff09;的数据都是以二进制的形式存储的&#xff0c;传输时也是。所以&#xff0c;字节流可以传输任意类型的文件数…

【数据结构】复杂度(长期维护)

本篇博客主要是浅谈数据结构概念及时间复杂度&#xff0c;并做长期的维护更新&#xff0c;有需要借鉴即可。 复杂度目录 一、初识数据结构1.基础概念2.如何学好数据结构 二、复杂度1.复杂度2.时间复杂度①有限数的时间复杂度②函数的时间复杂度③二分查找时间复杂度④递归拓展练…

转让名称带中国的金融控股集团公司要多少钱

随着公司的发展和市场竞争的影响&#xff0c;越来越多的创业者希望注册一家好名称的公司&#xff0c;以提高企业知名度和竞争力。但是&#xff0c;注册中字头无地域公司需要满足一定的条件和流程。本文将对中字头无地域公司注册条件及流程进行详细的介绍。可以致电咨询我或者来…

动态支付策略:Go 语言中策略模式的妙用

关注公众号【爱发白日梦的后端】分享技术干货、读书笔记、开源项目、实战经验、高效开发工具等&#xff0c;您的关注将是我的更新动力&#xff01; 在现代软件架构中&#xff0c;支付功能是不可或缺的一环。无论是在线购物还是虚拟服务&#xff0c;支付策略的选择直接影响用户体…

transform 模型常见问题

目录 transform 模型常见问题 transform 模型常见问题 1.Transformer为何使用多头注意力机制?(为什么不使用一个头) 答:多头可以使参数矩阵形成多个子空间,矩阵整体的size不变,只是改变了每个head对应的维度大小,这样做使矩阵对多方面信息进行学习,但是计算量和单个h…

JWT/JWS/JWE

JWT(JSON Web Token)&#xff1a;一种基于JSON格式&#xff0c;用于在Web应用中安全传递用户身份验证和授权信息的标准令牌&#xff0c;可以包含签名(JWS)和加密(JWE)的信息 MacAlgorithm(Message Authentication Code Algorithm)&#xff1a;消息认证码算法 HMAC(Hash-based…

【C++】详解 Unique 函数 (小白一看就懂!!!)

目录 一、前言 二、去重函数 Unique() ✨头文件 ✨用法与作用 ✨注意点 三、常考面试题 四、共勉 一、前言 经常刷算法题的朋友&#xff0c;肯定会经常看到题目中提到 去重 这样的字眼&#xff0c;或者需要我们通过 去重 来解题&#xff0c;由于之前对 去重 了解的不太清楚…

2024/4/1—力扣—删除字符使频率相同

代码实现&#xff1a; 思路&#xff1a; 步骤一&#xff1a;统计各字母出现频率 步骤二&#xff1a;频率从高到低排序&#xff0c;形成频率数组 步骤三&#xff1a;频率数组只有如下组合符合要求&#xff1a; 1, 0...0n 1, n...n (, 0)n...n, 1(, 0) bool equalFrequency(char…

【C++学习】哈希的应用—位图与布隆过滤器

目录 1.位图1.1位图的概念1.2位图的实现3.位图的应用 2.布隆过滤器2.1 布隆过滤器提出2.2布隆过滤器概念2.3如何选择哈希函数个数和布隆过滤器长度2.4布隆过滤器的实现2.4.1布隆过滤器插入操作2.4.2布隆过滤器查找操作2.4.3 布隆过滤器删除 2.5 布隆过滤器优点2.6布隆过滤器缺陷…

UE4_动画基础_角色的缩放

以第三人称模板进行制作。 一、首先为角色缩放新建粒子效果 1、新建niagara system&#xff0c;重命名为NS_Shrink。 2、双击打开设置参数&#xff1a; 发射器重命名&#xff1a; Emitter State&#xff1a; 发射器一次喷发数量&#xff1a; 粒子初始大小&#xff0c;生命周…

PANet网络

PANet&#xff08;Path Aggregation Network&#xff09;是一种用于语义分割任务的神经网络结构&#xff0c;旨在解决多尺度特征融合的问题。该网络结构由中国科学院计算技术研究所提出&#xff0c;在2018年的论文中首次提出。 PANet的主要目标是解决语义分割任务中多尺度信息…

第8章 数据集成和互操作

思维导图 8.1 引言 数据集成和互操作(DII)描述了数据在不同数据存储、应用程序和组织这三者内部和之间进行移动和整合的相关过程。数据集成是将数据整合成物理的或虚拟的一致格式。数据互操作是多个系统之间进行通信的能力。数据集成和互操作的解决方案提供了大多数组织所依赖的…