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, …...

构建知识图谱:从技术到实战的完整指南
目录 一、概述二、知识图谱的基础理论定义与分类核心组成历史与发展 三、知识获取与预处理数据源选择数据清洗实体识别 四、知识表示方法知识表示模型RDFOWL属性图模型 本体构建关系提取与表示 五、知识图谱构建技术图数据库选择Neo4jArangoDB 构建流程数据预处理实体关系识别图…...

STM32的分类和选型
F系列(主要用于普通应用) STM32F0xx:低成本、低功耗,适用于成本敏感和低功耗的应用。STM32F1xx:中低端微控制器,具有丰富的外设和良好的性能。STM32F2xx:高性能微控制器,适用于要求…...

python使用read_sql与to_sql读写数据库
文章目录 详细说明示例程序 详细说明 使用pandas读写数据库的方法(以Mysql为例)如下: 首先是打包一个工具函数: import pandas as pd import numpy as np from sqlalchemy import create_engine, textdef get_sql_engine():# 数据…...

【ArcGIS微课1000例】0096:dem三维块状表达(层次地形模型)
文章目录 一、DEM表达方式二、层次模型表达三、注意事项一、DEM表达方式 DEM数字高程模型的表达方式通常有以下4种: 1. 规则格网 2. 不规则三角网 3. 等高线 4. 层次地形模型 作为栅格地理数据,DEM 数据具有2.5维的特征,能够以三维表面的形式进行三维空间表达。但受其数…...

OJ_糖果分享游戏
题干 c实现 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<vector> using namespace std;void ShareCandy(vector<int>& student) {int size student.size();vector<int> share(size); //保存每个同学交换前,糖果数量…...

sqli-lbs靶场搭建
目录 环境小皮源码下载 1.源码解压: 2.搭建网站 2.1点击创建网站 2.2修改sql-connections\db-creds.inc 2.3重新启动 3.访问你设置的域名 3.1点击启动数据库配置 3.2返回第一个页面(开启题目) sqlilbs靶场搭建 环境小皮源码下载 下载地址&am…...

SharedPreferences卡顿分析
SP的使用及存在的问题 SharedPreferences(以下简称SP)是Android本地存储的一种方式,是以key-value的形式存储在/data/data/项目包名/shared_prefs/sp_name.xml里,SP的使用示例及源码解析参见:Android本地存储之SharedPreferences源码解析。以…...

64、ubuntu使用c++/python调用alliedvisio工业相机
基本思想:需要使用linux系统调用alliedvisio工业相机完成业务,这里只做驱动相机调用,具体不涉及业务开发 Alvium 相机选型 - Allied Vision 一、先用软件调用一下用于机器视觉和嵌入式视觉的Vimba X 软件开发包 - Allied Vision VimbaX_Set…...

网络端口与 IP 地址有什么区别?
网络端口和IP地址是计算机网络中两个非常重要的概念,它们在实现网络通信和数据传输中扮演着不同的角色。 IP地址 IP地址(Internet Protocol Address)是用于标识网络上设备的唯一地址。它是一个由数字组成的标识符,用于在网络中准…...

C语言标准的输入输出
目录 1. 格式化输入输出 2. 控制字符串长度 3. 混合格式化输出 4. 格式化浮点数 5. 格式化日期和时间 在C语言编程中,输入输出格式非常重要,它决定了程序如何向用户展示数据以及如何从用户接收数据。本篇博客将介绍C语言输入输出格式的一些基本概念…...