C语言——指针进阶(四)

fe594ea5bf754ddbb223a54d8fb1e7bc.gif

目录

一.前言

二.指针和数组笔试题解析

2.1 二维数组

2.2 指针笔试题

三.全部代码

四.结语


8fb442646f144d8daecdd2b61ec78ecd.png

一.前言

本文我们将迎来指针的结尾,包含了二维数组与指针的试题解析。码字不易,希望大家多多支持我呀!(三连+关注,你是我滴神!)

二.指针和数组笔试题解析

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字节。

总结:数组名的意义:

  1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
  2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
  3. 除此之外所有的数组名都表示首元素的地址

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


4b12323f94834afd9ec146a3c10df229.jpeg四.结语

数组和指针的笔试题解析就此谢幕。最后感谢大家的观看,友友们能够学习到新的知识是额滴荣幸,期待我们下次相见~

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

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

相关文章

网络基础二 session、cookie、token

HTTP协议以明文方式发送内容&#xff0c;不提供任何方式的数据加密&#xff0c;如果攻击者截取了Web浏览器和网站服务器之间的传输报文&#xff0c;就可以直接读懂其中的信息&#xff0c;因此&#xff0c;HTTP协议不适合传输一些敏感信息&#xff0c;比如&#xff1a;信用卡号、…

正则表达式 文本三剑客

一 正则表达式&#xff1a; 由一类特殊字符及文本字符所编写的模式&#xff0c;其中有些字符&#xff08;元字符&#xff09;不表示字符字面意义&#xff0c;而表示控制或通配的功能&#xff0c;类似于增强版的通配符功能&#xff0c;但与通配符不同&#xff0c;通配符功能是用…

Log4j2的Appenders配置详解

官方配置文档 https://logging.apache.org/log4j/2.x/manual/appenders.html#RollingFileAppender <Appenders> 常使用的类如下&#xff1a; org.apache.log4j.ConsoleAppender&#xff08;控制台&#xff09; org.apache.log4j.FileAppender&#xff08;文件&#xff…

【Go-zero】手把手带你在goland中创建api文件并设置高亮

【Go-zero】手把手带你在goland中创建api文件并设置高亮 大家好 我是寸铁&#x1f44a; 总结了一篇手把手带你在goland中创建api文件并设置高亮解决方案的文章✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 问题复盘 在使用go-zero 框架时&#xff0c;常常需要用到goctl 一键生成…

Netty源码二:服务端创建NioEventLoopGroup

示例 还是拿之前启动源码的示例&#xff0c;来分析NioEventLoopGroup源码 NioEventLoopGroup构造函数 这里能看到会调到父类的MultiThread EventLoopGroup的构造方法 MultiThreadEventLoopGroup 这里我们能看到&#xff0c;如果传入的线程数目为0&#xff0c;那么就会设置2倍…

Vue2 props组件通信-父子组件传值

一、父组件向子组件传值 1、流程图 2、父组件代码 <template><div class"app"><UserInfo:usernameusername:ageage:isSingleisSingle:carcar:hobbyhobby></UserInfo></div> </template><script> import UserInfo from .…

汽车网络安全管理体系框架与评价-汽车网络安全管理体系框架

R155《网络安全与网络安全管理系统》法规中明确指出 &#xff0c; 汽车制造商应完成 “汽车网络安全管理体系认证” &#xff08;简称&#xff1a; CSMS认证&#xff09;以及 “车辆型式审批&#xff02; 且CSMS认证&#xff0c;是车辆型式审批的前提条件。 虽然我国相关政策尚…

【网络基础】IP

IP协议报头 4位版本号(version): 指定IP协议的版本, 对于IPv4来说, 就是4.4位头部长度(header length): IP头部的长度是多少个32bit, 也就是 length * 4 的字节数. 4bit表示最大的数字是15, 因此IP头部最大长度是60字节. 8位服务类型(Type Of Service): 3位优先权字段(已经弃用…

C++笔记之作用域解析符::和命名空间、作用域的关系

C++笔记之作用域解析符::和命名空间、作用域的关系 —— 杭州 2024-01-26 code review 文章目录 C++笔记之作用域解析符::和命名空间、作用域的关系1.`命名空间`和`作用域`两个术语的联系和区别命名空间(Namespace)作用域(Scope)联系与区别2.`作用域解析符::`和`命名空间`…

Codeforces Round 785 C. Palindrome Basis

C. Palindrome Basis 题意 定义一个正整数 a a a 是回文的&#xff08;没有前导 0 0 0&#xff09;当且仅当&#xff1a; a a a 的十进制表示形式回文 给定一个正整数 n n n &#xff0c;求出将 n n n 拆分成若干个回文数之和的方案数 思路 这是一个经典模型&#xff0…

Mybatis2:基础操作

Mybatis1 入门 目录 1 基础操作 1.1 准备数据库表 1.2 删除 1.2.1 功能实现 1.2.2 日志输入 1.2.3.1 介绍 1.2.3.2 SQL注入 1.2.3.3 参数占位符 1.3新增 1.3.1 主键返回 1.4更新 1.5 查询 1.5.1 根据ID查询 1.5.2 数据封装 1.5.3参数名说明 1 基础操作 1.1 准备…

107基于51单片机的数字温度计设计[proteus仿真]

基于51单片机的自行车测速系统设计[proteus仿真] 温度检测系统这个题目算是课程设计和毕业设计中常见的题目了&#xff0c;本期是一个基于51单片机的数字温度计设计 需要的源文件和程序的小伙伴可以关注公众号【阿目分享嵌入式】&#xff0c;赞赏任意文章 2&#xffe5;&…

linux -- 内存管理 -- 页面分配器

linux内存管理 为什么要了解linux内存管理 分配并使用内存&#xff0c;是内核程序与驱动程序中非常重要的一环。内存分配函数都依赖于内核中一个非常复杂而重要的组件 - 内存管理。 linux驱动程序不可避免要与内核中的内存管理模块打交道。 linux内存管理可以总体上分为两大…

2024年最新MacBook苹果电脑安装JDK8、JDK11教程,配置环境变量 + 快速切换JDK版本

本帖发布日期&#xff1a;2024年01月26日&#xff0c;全网最新教程整理。 1、概述 本文主要为在MacBook苹果电脑系统下安装JDK及环境变量配置。 教程并非原创&#xff0c;摘抄自互联网&#xff0c;本人作为更新整理亲测。&#xff08;也算给自己记录一贴&#xff09; 本帖分…

数据结构【初阶】--排序(归并排序和基数排序)

目录 一.归并排序的非递归写法 1.思想应用 2.代码基本实现 (1)单趟归并逻辑 (2)多趟&#xff08;循环&#xff09;的控制条件 ① 迭代条件&#xff1a;i2*gap ② 结束条件&#xff1a;i(或i<n-2*gap)<> (3)代码展示 ① 单趟逻辑 ②整体逻辑 3.优化代码…

Springboot响应数据详解

功能接口 Controller下每一个暴露在外的方法都是一个功能接口 功能接口的请求路径是RequestMapping定义的路径&#xff0c;浏览器需要请求该功能则需要发出该路径下的请求。 RestController RestControllerControllerResponseBody(响应数据的注解) ResponseBody 类型&#…

找不到d3dcompiler 43.dll如何解决,5个方法轻松解决d3dcompiler 43.dll问题

计算机系统中d3dcompiler_43.dll文件的丢失可能会引发一系列显著的问题&#xff0c;这一动态链接库文件在Windows操作系统中扮演着至关重要的角色。它主要与Direct3D图形API相关&#xff0c;对于支持和运行使用Direct3D技术开发的各种应用程序和游戏至关重要。 一旦d3dcompiler…

RabbitMQ-如何保证消息不丢失

RabbitMQ常用于 异步发送&#xff0c;mysql&#xff0c;redis&#xff0c;es之间的数据同步 &#xff0c;分布式事务&#xff0c;削峰填谷等..... 在微服务中&#xff0c;rabbitmq是我们经常用到的消息中间件。它能够异步的在各个业务之中进行消息的接受和发送&#xff0c;那么…

sqli.labs靶场(第18~22关)

18、第十八关 经过测试发现User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0加引号报错 这里我们闭合一下试试 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0,127.0.0.1,adm…

【爬坑】临时修复To connect to xxx insecurely, use `--no-check-certificate‘报错

解决方案&#xff1a;wget请求时跳过证书验证。 sudo vim /etc/wgetrc 插入一行&#xff1a; check_certificate off 重新运行wget命令即可。