C语言——指针进阶(四)
目录
一.前言
二.指针和数组笔试题解析
2.1 二维数组
2.2 指针笔试题
三.全部代码
四.结语
一.前言
本文我们将迎来指针的结尾,包含了二维数组与指针的试题解析。码字不易,希望大家多多支持我呀!(三连+关注,你是我滴神!)
二.指针和数组笔试题解析
2.1 二维数组
请逐个分析下面代码最终的打印结果:
int main()
{int a[3][4] = { 0 };printf("%zd\n", sizeof(a));printf("%zd\n", sizeof(a[0][0]));printf("%zd\n", sizeof(a[0]));printf("%zd\n", sizeof(a[0] + 1));printf("%zd\n", sizeof(*(a[0] + 1)));printf("%zd\n", sizeof(a + 1));printf("%zd\n", sizeof(*(a + 1)));printf("%zd\n", sizeof(&a[0] + 1));printf("%zd\n", sizeof(*(&a[0] + 1)));printf("%zd\n", sizeof(*a));printf("%zd\n", sizeof(a[3]));return 0;
}
这是我们假想中的3行4列:
这是真正的3行4列
数组名单独放在sizeof()里面时,我们计算的就是整个数组的大小,即3*4*4==48字节
下面的a[0][0]即代表数组里面的第一个元素,即计算第0行0列的大小,元素类型是整型所以大小为:4字节
二维数组其实可以理解为是一维数组的数组,就相当于这个二维数组中每行相当于一个元素,只不过这个元素里存放的是一维数组。既然如此我们就可以认为第一行的一维数组它的数组名是a[0],它作为二维数组的第一个元素(数组)
同理如果你想访问一维数组的话先指定该一维数组在二维数组中的下标,然后再用a[j]进行访问,这样就可以访问到一维数组中的元素(数据)。
那么我们可以看到a[0]是作为二维数组中一维数组的数组名存在的,所以计算整个一维数组得到:4*4==16字节
这一次的a[0]并没有单独放在sizeof()内部,也没有&,所以不能把它看作是数组名(整个一维数组)。所以它只能表示数组首元素的地址,也就是第一行第一个元素的地址。而a[0]+1就代表是下一个的地址即第一行第二个元素的地址。那么地址的大小一律是4(x32)或8(x86)字节。
我们接着对上面第一行第二个元素的地址解引用,那就会得到第一行第二个元素。大小为4字节。
a没有单独放在sizeof()里面,也没有&,说明a就是作为二维数组的数组名,那就代表是二维数组中首元素的地址,即作为第一个元素(第一行)的整个一维数组的地址。而a+1就代表是第二行的地址。那么地址的大小一律是4(x32)或8(x86)字节。
我们接下来对第二行的一维数组地址解引用,那就是计算第二行的整个一维数组的大小,大小为4*4==16字节。
a[0]代表的是第一行的一维数组的数组名,而&代表的是取出整个一维数组的地址,最后+1就是取出的是第二行的一维数组的地址。那么地址的大小一律是4(x32)或8(x86)字节。
对第二行的一维数组地址解引用就是计算第二行的整个一维数组的大小,大小为4*4==16字节。
a表示二维数组的数组名,即首元素的地址(第一行的一维数组),而对其解引用就是计算整个一维数组的大小,大小为4*4==16字节。
在3行4列的二维数组中并没有第4行,但在sizeof()眼中并不会判定为越界,因为不管怎么写它们的类型是注定的,所以在sizeof()的判定中a[3]与a[0]是没有区别的,都是相同类型。a[0]单独放在sizeof()里代表第一行元素的整个一维数组,大小为4*4==16字节。
总结:数组名的意义:
- sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
- &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
- 除此之外所有的数组名都表示首元素的地址
2.2 指针笔试题
我们先标明ptr的指向
这道题很简单,ptr-1向前一步指向5的地址再*解引用指向元素5。a代表首元素地址,+1指向下一位,*解引用指向元素2.
这里我们需要知道,int*类型的指针+1跳过4个字节,char*指针+1跳过1个字节。而里面的p是结构体指针,它+1肯定跳过的就是结构体大小的字节也就是20字节。在p原基础上+20也就0x100014.
第二个打印中我们把结构体指针类型的p强制转换为(unsigned long)整型,而整型+1就是数字运算的+1。0x10001
第三个打印中p被强制类型转换为int*型指针,那么+1就是代表跳过4个字节,0x100004
在我们标明ptr1的指向后只需要注意ptr1[-1]代表*(ptr1-1)即向前挪动一位指向4.
而对于a而言被强制类型转换为整型后+1就是数学运算的+1,而一个元素有4个字节,a指向首个元素的地址,就算+1也还是指向元素1,并不会跳出指向2.
假设我们是用小端存储
重点来了,因为我们ptr2是int*类型的指针,而*就代表我们要访问4个字节,所以ptr2访问的4个字节是00 00 00 02
但又因为是小端存储,所以真实值应该是0x 02 00 00 00按%x(省略2前面0)的话就是2000000
这道题是有坑的,稍不注意就会写错。如果在二维数组中想把0,1放在第一行,那用的应该是{},可题目用的却是(),那就代表(0,1)它是一个逗号表达式。 而a[0]代表的是第一行的一维数组的地址<==>&a[0][0],而p[0]<==>*(p+0)<==>*p,最后打印结果为1.
我们先把a[4][2]与p最开始的指向标明
由于p是数组指针,它一次+1要跳过4个整型,而p认为它指向的数组是4个元素,所以我们把这4个元素框起来,再从里面找到第3个元素。
随着数组下标的增长,地址的大小是由低到高的。所以我们这里是小地址减大地址得到的是一个负数。两地址之间相隔4个元素,所以最终由%d打印出来的是-4。
而在我们以%p的形式打印时,它是以-4的补码形式进行打印的,列出-4的原码与反码最终求出补码,然后把补码转换为16进制时应该是0xFFFFFFFC
&aa表示取出整个二维数组的地址,而ptr1-1就是向前挪动一位指向10
*(aa+1)表示取出的是二维数组中的第2个元素,即一维数组<==>aa[1],而aa[1]没有放在sizeof()内部,就表示它代表的是首元素的地址<==>&a[1][0]。ptr2-1就是向前挪动一位,指向5
其实真正理解来说应该是在a数组里面存放了3个char*指针(3个元素),这3个char*指针分别指向了这3个字符串。
而我们还有一个char**类型的指针pa则是指向这个数组a
而接下来pa++(跳过一个char*的元素),我们就指向a数组的第2个元素,*pa就是取出a数组的第2个元素,这个元素是char*类型的指针,里面存储的是字符串"at/0"的地址,所以我们最终取出的是字符串"at/0"的地址。以%s打印为at
首先把各个指针指向标明
cpp+1表示它要跳过一个char**字节,*(cpp+1)对指向cp数组里的c+2解引用,指向元素c+2,即*(cpp+1)<==>c+2,而*c+2则表示指向c数组里面的第3个元素,POINT/0;
+3的优先级最低,所以要最后算。我们再一次对cpp++,让cpp指向cp数组里面第3个元素的地址,而后面通过*(cpp+1)解引用得到cp数组里面的元素c+1,后面我们再对c+1这个元素进行--变成c,这样它就指向了c数组里面的首个元素的地址了,再对其解引用*c拿到c数组的元素ENTER。而再对指向字符串的指针+3,则拿到的就是ER。
下面的cpp[-2]可以转换成*(cpp-2),所以最终要求的其实是**(cpp-2) + 3.
cpp-2最后的指向其实是cp数组的首元素地址(cpp-2的指向是首元素,但cpp指向是不会变的,还是原来的指向),对其解引用后得到元素c+3,再对c+3解引用得到c数组的最后一个元素PITSR,最后指针+3打印为SR。
最后一个数据中cpp[-1]可以解析为*(cpp-1),那么cpp[-1][-1]也可以解析为*(*(cpp-1)-1)。所以最后我们要求的是*(*(cpp-1)-1)+1
首先cpp-1代表指向cp数组中第2个元素的地址,解引用后为c+2,c+2-1变为c+1,指向了c数组中第2个元素的地址,再次解引用后指向c数组的第2个元素,最后指针+1打印出EW.
三.全部代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>int main()
{int a[3][4] = { 0 };printf("%zd\n", sizeof(a));//48-数组名a单独放在了sizeof内存,表示整个数组,sizeof(a)计算的是数组的大小,单位是字节printf("%zd\n", sizeof(a[0][0]));//4-a[0][0]是数组的第一行第一个元素,这里计算的就是一个元素的大小,单位是字节printf("%zd\n", sizeof(a[0]));//16 - a[0]是第一行这个一维数组的数组名,数组名单独放在了sizeof内部//a[0]就表示整个第一行这个一维数组,sizeof(a[0])计算的整个第一行这个一维数组的大小printf("%zd\n", sizeof(a[0] + 1));//4/8 - a[0]并非单独放在sizeof内部,也没有&,所以a[0]表示第一行这个一维数组首元素的地址//也就是第一行第一个元素的地址//a[0] <---> &a[0][0]//a[0]+1 ---> &a[0][1]printf("%zd\n", sizeof(*(a[0] + 1)));//4 - a[0] + 1是第一行第二个元素的地址,*(a[0] + 1))就是第一行第二个元素//printf("%zd\n", sizeof(a + 1));//4/8//a 作为二维数组的数组名,并没有单独放在sizeof内部,也没有&,a就是数组首元素的地址,也就是第一行的地址, a 的类型是 int(*)[4]//a+1 就是第二行的地址,类型是:int(*)[4]//printf("%zd\n", sizeof(*(a + 1)));//16 a+1是第二行的地址,*(a+1)就是第二行,计算的就是第二行的大小//另外一个角度理解:*(a+1) -- a[1]//sizeof(a[1]) - a[1]这个第二行的数组名,单独放在了sizeof内部,计算的是第二行的大小printf("%zd\n", sizeof(&a[0] + 1));//4/8//a[0]是第一行的数组名,&a[0]取出的是数组的地址,取出的是第一行这个一维数组的地址,类型就是int(*)[4]//&a[0]+1 就是第二行的地址,类型就是int(*)[4]printf("%zd\n", sizeof(*(&a[0] + 1)));//*(&a[0] + 1)得到的就是第二行,计算的就是第二行的大小printf("%zd\n", sizeof(*a));//16//a表示数组首元素的地址,也就是第一行的地址//*a 就是第一行,也就相当于是第一行的数组名//*a--> *(a+0) -- a[0]//printf("%zd\n", sizeof(a[3]));//16-不会越界,//a[3] -- arr[0]//int [4] int [4]return 0;
}int main()
{int a[5] = { 1, 2, 3, 4, 5 };int* ptr = (int*)(&a + 1);printf("%d, %d", *(a + 1), *(ptr - 1));return 0;
}//由于还没学习结构体,这里告知结构体的大小是20个字节
//X86 环境下演示
struct Test
{int Num;char* pcName;short sDate;char cha[2];short sBa[4];
} * p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
//0x开头的数字是16进制的数字
int main()
{p = (struct Test*)0x100000;printf("%p\n", p + 0x1);printf("%p\n", (unsigned long)p + 0x1);printf("%p\n", (unsigned int*)p + 0x1);return 0;
}int main()
{int a[4] = { 1, 2, 3, 4 };int* ptr1 = (int*)(&a + 1);int* ptr2 = (int*)((int)a + 1);printf("%x,%x", ptr1[-1], *ptr2);return 0;
}#include <stdio.h>
int main()
{int a[3][2] = { (0, 1), (2, 3), (4, 5) };int* p;p = a[0];printf("%d", p[0]);return 0;
}int main()
{int a[5][5];int(*p)[4];//数组指针p = a;//a - int(*)[5]//p - int(*)[4]printf("%p, %d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);////%p是打印地址,认为内存中存储的补码就是地址//return 0;
}int main()
{int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };int* ptr1 = (int*)(&aa + 1);int* ptr2 = (int*)(*(aa + 1));printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));return 0;
}#include <stdio.h>
//阿里的笔试题
int main()
{char* a[] = { "work","at","alibaba" };char** pa = a;pa++;printf("%s\n", *pa);return 0;
}int main()
{char* c[] = { "ENTER","NEW","POINT","FIRST" };char** cp[] = { c + 3,c + 2,c + 1,c };char*** cpp = cp;printf("%s\n", **++cpp);printf("%s\n", *-- * ++cpp + 3);printf("%s\n", *cpp[-2] + 3);printf("%s\n", cpp[-1][-1] + 1);return 0;
}
四.结语
数组和指针的笔试题解析就此谢幕。最后感谢大家的观看,友友们能够学习到新的知识是额滴荣幸,期待我们下次相见~
相关文章:

C语言——指针进阶(四)
目录 一.前言 二.指针和数组笔试题解析 2.1 二维数组 2.2 指针笔试题 三.全部代码 四.结语 一.前言 本文我们将迎来指针的结尾,包含了二维数组与指针的试题解析。码字不易,希望大家多多支持我呀!(三连+关注&…...

Django介绍
一、介绍 Django是Python语言中的一个Web框架,Python语言中主流的web框架有Django、Tornado、Flask 等多种 优势:大而全,框架本身集成了ORM、模型绑定、模板引擎、缓存、Session等功能,是一个全能型框架,拥有自己的A…...

【idea】几个不错的idea插件让我码速又快了
目录 前言 Gradianto插件 jclasslib Bytecode viewer插件 Grep Console 插件 GenerateAllSetter 插件 GsonFormat 插件 JRebel and XRebel 插件 leetcode editor 插件 maven helper 插件 SequenceDiagram 插件 Statistic 插件 Translation 插件 前言 idea可以说是j…...

LabVIEW直流电机转速检测与控制
研究了使用LabVIEW软件和ELVIS实验平台来检测和控制直流电机的转速。通过集成光电传感器和霍尔传感器,实现了对电机转速的精确测量和调节。 系统组成:系统由NI ELVIS实验平台、光电传感器、霍尔传感器和直流电机组成。通过这些硬件元件,系统…...

༺༽༾ཊ—Unity之-05-抽象工厂模式—ཏ༿༼༻
首先创建一个项目, 在这个初始界面我们需要做一些准备工作, 建基础通用文件夹, 创建一个Plane 重置后 缩放100倍 加一个颜色, 任务:使用 抽象工厂模式 创建 人物与宠物 模型, 首先资源商店下载 人物与宠物…...
力扣面试题02.07-链表相交
链表相交 题目链接 解题思路: 题目可以确定如果相交,那么相交的部分一定是在链表的结尾部分第一步求得两条链表的长度第二步长度做差,将长的那条链表与短的那条链表后部分对其第三步遍历后面的部分,如果当前节点相等,…...
Java集合-ArrayList
集合就是一个保存数据的容器。在计算机中引入集合,是为了便于处理一组类似的数据。Java标准库自带的java.util包提供了集合相关的接口和实现类:Collection接口,它是除了Map接口外所有其他集合类的根接口。 Java的Java.uitl包主要提供以下三种…...

数据结构·单链表经典例题
1. 移除链表元素 OJ链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 本题是说给出一个链表的头节点head和一个整数val,如果发现节点中存的数据有val就删掉它,最后返回修改后的链表头节点地址 如果题目中没有明确…...
Linux常用指令的整合
之前面试被问到了Linux相关的指令,所以我整理的一份常用的Linux指令列表,适用于大多数Linux发行版,现分享给大家: 文件操作 ls:列出目录内容。cd [目录]:更改当前目录。pwd:显示当前目录路径。m…...
阿里云centos安装mysql,并修改初始密码
阿里云centos安装mysql,并修改初始密码 安装数据库、修改初始密码、并测试建立自己的数据库步骤1:创建数据库和用户步骤2:配置Nginx1. 创建新的站点配置文件2. 编辑配置文件3. 保存并退出编辑器4. 测试配置文件是否正确5. 重新加载 Nginx 以应…...

【JavaScript基础入门】04 JavaScript基础语法(二)
JavaScript基础语法(二) 目录 JavaScript基础语法(二)变量变量是什么声明变量变量类型动态类型注释 数字与运算符数字类型算术运算符操作运算符比较运算符逻辑运算符运算符的优先级 变量 变量是什么 在计算机中,数据…...

标准库中的string类(下)——“C++”
各位CSDN的uu们你们好呀,这段时间小雅兰的内容仍然是Cstring类的使用的内容,下面,让我们进入string类的世界吧!!! string类的常用接口说明 string - C Reference string类的常用接口说明 string类对象的修…...

如何使用Docker部署火狐浏览器并实现无公网ip远程访问
文章目录 1. 部署Firefox2. 本地访问Firefox3. Linux安装Cpolar4. 配置Firefox公网地址5. 远程访问Firefox6. 固定Firefox公网地址7. 固定地址访问Firefox Firefox是一款免费开源的网页浏览器,由Mozilla基金会开发和维护。它是第一个成功挑战微软Internet Explorer浏…...

瑞_数据结构与算法_AVL树
文章目录 1 什么是AVL树1.1 AVL树的背景及定义1.2 判断失衡1.2.1 平衡因子1.2.2 失衡的四种情况1.2.2.1 LL1.2.2.2 LR1.2.2.3 RL1.2.2.4 RR 1.3 解决失衡1.3.1 左旋(RR)1.3.2 右旋(LL)1.3.3 先左旋再右旋(LR࿰…...

BGP同步规则
BGP同步规则:开启同步下,从IBGP收到一条路由不会传给任何EBGP邻居(实验效果IBGP邻居和EBGP邻居都不传),除非从自身的IGP中也学到这条路由。目的是防止AS内部出现路由黑洞,向外部通告了一个本AS不可达的虚假的路由。 同步规则只影响从IBGP邻居收到的路由,不影响从EBGP邻居收…...
Linux命令-apt-key命令(管理Debian Linux系统中的软件包密钥)
补充说明 apt-key命令 用于管理Debian Linux系统中的软件包密钥。每个发布的deb包,都是通过密钥认证 的,apt-key用来管理密钥。 语法 apt-key(参数)参数 操作指令:APT密钥操作指令。 实例 apt-key list # 列出已保存在系统中key。 apt-…...

Python根据Excel表进行文件重命名
一、问题背景 在日常办公过程中,批量重命名是经常使用的操作。之前我们已经进行了初步探索,主要是通过批处理文件、renamer软件或者Python中的pathlib等模块对当前目录下的文件进行批量重命名。 而今天我们要使用的是PythonExcel的方法对指定目录下的文…...

【UVM源码】UVM Config_db机制使用总结与源码解析
UVM Config_db机制使用总结与源码解析 UVM Config_db机制介绍UVM Config_db 机制引入的背景基本介绍使用方法优缺点: UVM Config_db机制使用示例:UVM Config_db使用高阶规则Config_db资源优先级 UVM Config_db 源码解析 UVM Config_db机制介绍 UVM Conf…...

群辉开启WebDav服务+cpolar内网穿透实现移动端ES文件浏览器远程访问本地NAS文件
文章目录 1. 安装启用WebDAV2. 安装cpolar3. 配置公网访问地址4. 公网测试连接5. 固定连接公网地址6. 使用固定地址测试连接 本文主要介绍如何在群辉中开启WebDav服务,并结合cpolar内网穿透工具生成的公网地址,通过移动客户端ES文件浏览器即可实现移动设…...
通过mybatis拦截器给sql执行加一个耗时监控
代码没什么内容,直接贴上来吧,其中costTimeUtil可以看我的另一篇博文:java实现一个不带次数变量的加权平均值算法-CSDN博客 Slf4j Intercepts({Signature(type StatementHandler.class,method "query",args {Statement.class, …...

Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...

elementUI点击浏览table所选行数据查看文档
项目场景: table按照要求特定的数据变成按钮可以点击 解决方案: <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...
【SpringBoot自动化部署】
SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一,能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时,需要添加Git仓库地址和凭证,设置构建触发器(如GitHub…...
前端高频面试题2:浏览器/计算机网络
本专栏相关链接 前端高频面试题1:HTML/CSS 前端高频面试题2:浏览器/计算机网络 前端高频面试题3:JavaScript 1.什么是强缓存、协商缓存? 强缓存: 当浏览器请求资源时,首先检查本地缓存是否命中。如果命…...

jdbc查询mysql数据库时,出现id顺序错误的情况
我在repository中的查询语句如下所示,即传入一个List<intager>的数据,返回这些id的问题列表。但是由于数据库查询时ID列表的顺序与预期不一致,会导致返回的id是从小到大排列的,但我不希望这样。 Query("SELECT NEW com…...
在RK3588上搭建ROS1环境:创建节点与数据可视化实战指南
在RK3588上搭建ROS1环境:创建节点与数据可视化实战指南 背景介绍完整操作步骤1. 创建Docker容器环境2. 验证GUI显示功能3. 安装ROS Noetic4. 配置环境变量5. 创建ROS节点(小球运动模拟)6. 配置RVIZ默认视图7. 创建启动脚本8. 运行可视化系统效果展示与交互技术解析ROS节点通…...