C语言——小知识和小细节15

一、二维数组与指针

例一

下面的程序运行结果是什么:

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

运行结果:

实际上这里有个小细节,就是二维数组的初始化实际上是不完全初始换,因为,内部用的是小括号,所以这里的 (1,2) (3,4) (5,6) 都是逗号表达式,逗号表达式的结果是最后一个表达式的结果,所以这里的初始化相当于:

int arr[3][2] = { 2,4,6 };

 所以具体的初始化情况:

arr[0] 就代表二位数组第一个子数组的第一个元素的地址,所以 *p 就能访问到二位数组第一个子数组的第一个元素的地址,即为 arr[0][0] ,所以运行结果是2。

例二

下面的程序在32位平台运行的结果是什么:

#include <stdio.h>int main()
{int arr[5][5];int(*p)[4];p = (int(*)[4])arr;printf("%p\n%d\n", &p[4][2] - &arr[4][2], &p[4][2] - &arr[4][2]);return 0;
}

运行结果:

下面我们来详细分析:

我们知道对于二维数组在内存中的存储依旧是连续的,就像一维数组那样,所以这里我们将其简化为这样(这里一个格子代表一个整型数据):

中间的空格为了方便看出每一个子数组。

在这一步:

int(*p)[4];
p = (int(*)[4])arr;

对于 p 是一个数组指针,类型是 int(*)[4] 然后对于 arr 就是二维数组首个子数组的地址,其类型是 int(*)[5] ,然后被强制转换为了 int(*)[4] 类型,然后赋给了 p ,假设这里的 p 中的数值是0x00ff1200:

但是由于 p 的类型是 int(*)[4] ,所以 p 一次是访问四个整型,对于 &p[4][2] 就相当于 &*(*(p + 4) + 2) ,前面的&与*可以相互抵消,所以还可以表示为 (*(p + 4) + 2) ,具体在图中就是:

然后对于 &arr[4][2] 就是二维数组第五行第三列的元素的地址,在途中就是这样:

我们可以看到这两个指针之间相差了四个整型数据,对于指针减指针,会返回指针之间的元素个数,较大的指针减较小的指针,会返回正数,反之,就返回负数,所以这里会返回-4。

这就可以解释第二个打印结果。

然而第一个结果为什么是0xFFFFFFFC呢?

这是因为%p是打印指针形式的数据,由于指针形式的数据都是非负的,就是打印十六进制非负数,而这里的-4又是负数,所以这里就被转换为正数了:

所以第一个打印结果是FFFFFFFC。

二、指针数组与指针

例一

下面的程序运行结果是什么:

#include <stdio.h>int main()
{const char* str[] = { "hello","world","abc" };const char** p = str;p++;printf("%s\n", *p);return 0;
}

运行结果:

对于str数组是字符指针数组,只不过这里的这里的字符指针是指向只读段的字符串的指针,然后及将数组的首元素地址付给 p ,然后 p 自增一,对于某种指针自增一,就会跳过这个指针指向的数据类型的大小,所以这里会跳过一个指针的大小,所以是指向数组第二个元素的指针,所以*p就是访问数组的第二个元素,第二个元素是指向 “world” 字符串的第一个字符 'w' 的指针,所以会打印 world 。

例二

下面的代码运行结果是什么:

#include <stdio.h>int main()
{const char* ps[] = { "hello","fgh","abcde","world"};const char** pps[] = { ps + 3,ps + 2,ps + 1,ps };const char*** ppps = pps;printf("%s\n", **++ppps );//abcdeprintf("%s\n", *-- * ++ppps + 3);//loprintf("%s\n", *ppps[-2] +  3);//ldprintf("%s\n", ppps[-1][-1] + 1); //ghreturn 0;
}

运行结果:

下面我们来具体分析:

具体的对应情况(这里的一个格子代表一个指针元素):

首先是第一句:

printf("%s\n", **++ppps );//abcde

++的优先级是大于*的,所以 ppps 先加加,最开始 ppps 指向 pps 的首元素,即 ppps 中是 pps 的首元素地址,然后加加后,就指向了 pps 的第二个元素,然后解引用就是 pps 的第二个元素 ps + 2,然后 ps + 2 就是 ps 的第三个元素的地址,所以 ps + 2 指向 ps 的第三个元素,然后解引用,就是 ps 的第三个元素,ps 的第三个元素是指向 "abcde" 这个字符串的第一个字符 'a' 的指针,所以会打印 abcde 这个字符串。

然后第二句:

printf("%s\n", *-- * ++ppps + 3);//lo

在上一句时因为加加,ppps 已经变成指向 pps 第二个元素的指针了,

然后这一句又有++,所以 ppps 指向的位置又向后面一了一个元素,所以这时 ppps 就指向 pps 的第三个元素了,

然后解引用,就访问到了 pps[2] ,然后就得到了 ps + 1 ,然后--,就变成了 ps ,这里的 pps[2] 中的 ps + 1 也变成了 ps ,

 ps 是数组 ps 的首元素的地址,所以 ps 指向数组 ps 的第一个元素,然后解引用,得到数组 ps 的第一个元素,即 ps[0] ,ps 的第一个元素是指向字符串 "hello" 的指针,然后因为 + 加操作符优先级在这里是最低的,所以最后进行,这时是 "hello" 字符串中第一个字符 'h' 的指针,然后进行加三操作,实际上会跳过三个字符,下图中每个格子代表一个字符数据:

这时指针指向 "hello" 中的第二个 'l' ,所以这里会打印 lo 。

然后第三句:

printf("%s\n", *ppps[-2] +  3);//ld

对于 ppps ,其在第二句完成后,就指向了 pps 的第三个元素了,我们知道 ppps[-2] 就相当于 *(ppps - 2) ,但这个操作不会使 ppps 指向的内容发生改变,因为没有对 ppps 造成改动,所以进行这个操作后,就得到了 pps 的第一个元素 ps + 3,ps + 3 指向数组 ps 的第四个元素,然后再解引用,就得到了 ps 的第四个元素,也就是 ps[3] ,ps[3] 是一个字符指针,指向字符串 "world" 第一个字符 'w' 的指针,然后对其进行加三操作,就会得到指向 "world" 的第四个字符 'l' 的指针,

所以会打印 ld 。

最后第四句:

printf("%s\n", ppps[-1][-1] + 1); //gh

第三句中没有改变 ppps 指向的内容,所以 ppps 还是指向 pps 的第三个元素,然后进行 ppps[-1][-1] 操作,这个操作就相当于 *(*(ppps - 1) - 1) ,里面的 *(ppps - 1) 的进行后就得到了 pps 的第二个元素了,即 pps[1] ,就是 ps + 2 ,然后外层减一,就变成了 ps + 1 ,ps + 1 指向数组 ps 的第二个元素,解引用后就得到了数组 ps 的第二个元素了,即 ps[1] ,而ps[1] 是一个指向字符串 "fgh" 第一个字符 'f' 的指针,然后进行加一操作,就指向了 "fgh" 的第二个字符 'g' ,

所以会打印 gh 。

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

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

相关文章

教师编制可以跨市调动吗

在教育的广阔天地中&#xff0c;我们常常面临各种职业发展的选择。作为一名教师&#xff0c;是否能够实现跨市调动&#xff0c;这不仅是一个职业发展的问题&#xff0c;更关系到个人生活和职业规划的诸多方面。今天&#xff0c;我们就来探讨一下&#xff0c;拥有编制身份的教师…

Linux 目录操作函数

目录操作函数 ls -l 可以查看目录中文件的信息&#xff0c;比如&#xff1a; petriXX:~/lesson01/05_io/目录操作函数$ ls -l a.txt -rw-r--r-- 1 petri petri 0 Apr 22 18:51 a.txtLinux系统中的目录操作函数&#xff1a; int rename(const char *oldpath, const char *new…

电磁兼容(EMC):静电放电(ESD)抗扰度试验深度解读(五)

静电放电过程是一个很复杂的过程&#xff0c;下面比对人体持金属对产品放电和静电发生器对产品进行接触放电过程的详细分解说明。 1. 人持金属对产品放电过程 人对产品所产生的静电放电&#xff0c;会发生下面一系列的事件&#xff1a; 1&#xff09;当手持金属片接近产品的…

算法题解记录20+++

题目描述&#xff1a; 难度&#xff1a;简单 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来…

学术论文中常见的拉丁语及其缩写词汇解析

诸神缄默不语-个人CSDN博文目录 在学术写作中&#xff0c;作者经常会使用一系列的拉丁语及其缩写词。 1. et al. 全称&#xff1a; et alii 或 et alia意义&#xff1a; 和其他人使用场景&#xff1a; 当引用的文献有多个作者时&#xff0c;通常在第一作者后使用“et al.”表…

华为路由器基于接口限速

一、背景 ISP与企业内网通过华为路由器接入Internet时,当大量流量进入路由器时,可能会因为带宽不足产生拥塞,导致丢包,严重影响用户上网体验。对于此需要对网络流量进行限制,其方式通常有防火墙带宽策略、路由器基于接口限速等。 二、华为路由器基于接口限速方式 在路由…

卸载并升级pytorch安装torcheval

Requires Python > 3.8 and PyTorch > 1.11conda list torch 查看包 pip uninstall torch 及各个包&#xff0c;nvidia-smi查看cuda11.4&#xff0c;安装对应11.3去官网找cuda11.3和torch1.11.0并粘贴命令&#xff0c;再安装torcheval pip install torch1.11.0cu113 tor…

C#各大版本特性

总目录 C# 语法总目录 C#各大版本特性目录 C#各大版本特性C#10.0C#9.0C#8.0C#7.0C#6.0C#5.0C#4.0C#3.0C#2.0 C#各大版本特性 C#10.0 支持全局using语句&#xff1a;现在可以在整个项目中使用全局using语句&#xff0c;在所有文件中自动引用命名空间&#xff0c;不需要在每个文…

【看不懂命令行、.yaml?】Hydra 库极速入门

Hydra 是一个开源的 Python 框架&#xff0c;可以简化研究和其他复杂应用程序的开发。其核心功能是通过组合动态创建层次化的配置&#xff0c;并可以通过配置文件和命令行进行覆盖。Hydra 的名字来源于它能够运行多个类似的作业 - 就像一个多头的水怪一样。 主要特性: 从多个…

汇编语言——从键盘接收一个小写字母,找出它的前导字符和后续字符,再按顺序显示这三个字符

注意的点&#xff1a; 判断输入合法性a无前导字符z无后续字符 data segment data ends stack segment stackdw 100 dup (?)top label word stack ends code segmentassume cs:code,ds:data,ss:stack main proc farmov ax,datamov ds,axmov ax,stackmov ss,axlea sp,top L1: …

T31开发笔记: 移动侦测

若该文为原创文章&#xff0c;转载请注明原文出处。 最近在测试创安源IPC时发现摄像头的视频流有移动侦测功能 &#xff0c;拆解后发现使用的是T31,刚好手头上有淘宝买50多点的T31摄像头&#xff0c;就自己现在了个简易DEMO测试一下。 一、硬件和开发环境 1、硬件&#xff1a;…

RTT设备驱动框架学习(UART)

在serial.h中 struct rt_serial_device {struct rt_device parent;const struct rt_uart_ops *ops;struct serial_configure config;void *serial_rx;void *serial_tx;struct rt_spinlock spinlock;struct rt_device_notify rx_notify; } typedef struct rt_serial_device rt_…

C语言 分支控制语句之 if

然后 我们来说 流程控制语句之 if 选择控制结构 是通过 分支语句来实现的 其中 包括 单分支选择语句通过 (if) 来实现 双分支语句通过 (if) 和 (else) 实现 多分支语句通过 (if) (else if) (else) 实现 对于单分支来讲 它控制的语句就是 要嘛做 要嘛不做 好比如 放假了 你是…

【极速前进】20240422:预训练RHO-1、合成数据CodecLM、网页到HTML数据集、MLLM消融实验MM1、Branch-Train-Mix

一、RHO-1&#xff1a;不是所有的token都是必须的 论文地址&#xff1a;https://arxiv.org/pdf/2404.07965.pdf 1. 不是所有token均相等&#xff1a;token损失值的训练动态。 ​ 使用来自OpenWebMath的15B token来持续预训练Tinyllama-1B&#xff0c;每1B token保存一个che…

夜鸦国际服账号验证怎么办 夜鸦国际服账号认证的详细教程

夜鸦国际服账号验证怎么办 夜鸦国际服账号认证的详细教程 今天为大家带来的是《夜鸦》这款游戏&#xff0c;游戏背景是基于13世纪欧洲背景的MMORPG游戏&#xff0c;这款游戏以其沉浸式的游戏体验和流畅的打斗为特色。玩家可以选择战士、剑士、猎人或女巫等角色&#xff0c;体验…

AQS(AbstractQueuedSynchronizer)队列同步器源码解读

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 目录 1. 前言 2. AOS、AQS、AQLS的区别 3. AQS的底层原理 3.1. 核心思想 3.2. 数…

李廉洋:4.23黄金休市之后大幅下跌,原油小幅度上涨。走势分析!

今年以来推动金价上涨的因素是亚洲的需求&#xff0c;很可能来自各国央行。最近又有零售买盘和一些金融买盘作为补充。目前的问题是&#xff0c;不断上升的债券收益率正在争夺资金。美国2年期国债的收益率接近5%&#xff0c;在美联储降息导致收益率开始下降之前&#xff0c;这仍…

JavaScript权威指南(第7版) 笔记 - 第 7 章 数组

能用代码说清楚的&#xff0c;绝不多废话&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; Linux创始人Linus的名言&#xff1a;Talk is cheap&#xff0c;show me the code ! &#xff0c;博主技术博文会精心给出能说明问题的范例代码&#xff01;…

JMeter--监听器--聚合报告

聚合报告&#xff08;Aggregate Report&#xff09; 可以查看事务或者取样器在某个时间范围内执行的汇总结果 右键 >>> 添加 >>> 监听器 >>> 聚合报告&#xff08;Aggregate Report&#xff09; Label 样本平均值中位数90% 百分位95% 百分位99% …

Qt实现XYModem协议(五)

1 概述 XMODEM协议是一种使用拨号调制解调器的个人计算机通信中广泛使用的异步文件运输协议。这种协议以128字节块的形式传输数据&#xff0c;并且每个块都使用一个校验和过程来进行错误检测。使用循环冗余校验的与XMODEM相应的一种协议称为XMODEM-CRC。还有一种是XMODEM-1K&am…