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

news2024/11/18 11:19:37

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四.结语

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

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

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

相关文章

JavaWeb后端登录校验功能(JWT令牌技术,Cookie技术,Session,拦截技术,过滤器)

目录 一.登录校验功能&#xff08;解决直接通过路径访问&#xff09; 1.实现思路 二.会话技术 ​编辑 1.Cookie技术 2.Session 3.令牌技术 1.简介 2.如何生成和解析 3.令牌的使用 三.Filter过滤器 1.什么是过滤器 2.实现步骤&#xff1a; 3.过滤器执行流程 4.拦截路径 5.过…

349. 两个数组的交集(力扣LeetCode)

文章目录 349. 两个数组的交集题目描述数组解题set容器解题该思路数组版解题 349. 两个数组的交集 题目描述 给定两个数组 nums1 和 nums2 &#xff0c;返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。 示例 1&#xff1a; 输入&a…

【Linux】Linux下多线程

需要云服务器等云产品来学习Linux的同学可以移步/–>腾讯云<–/官网&#xff0c;轻量型云服务器低至112元/年&#xff0c;优惠多多。&#xff08;联系我有折扣哦&#xff09; 文章目录 1. 前置&#xff1a;进程地址空间和页表1.1 如何看待进程地址空间和页表1.2 虚拟地址…

练习12.6_横向射击_Python编程:从入门到实践(第3版)

编写一个游戏&#xff0c;将一艘飞船放在屏幕左侧&#xff0c;并允许玩家上下移动飞船。在玩家按空格键时&#xff0c; 让飞船发射一颗在屏幕中向右飞行的子弹&#xff0c;并在子弹从屏幕中消失后将其删除。 ship_shooting.py import pygame import sys from leftship impor…

​ArcGIS Pro 如何批量删除字段

在某些时候&#xff0c;我们得到的图层属性表内可能会有很多不需要的字段&#xff0c;如果挨个去删除会十分的麻烦&#xff0c;对于这种情况&#xff0c;我们可以使用工具箱内的字段删除工具批量删除&#xff0c;这里为大家介绍一下使用方法&#xff0c;希望能对你有所帮助。 …

[C++历练之路]C++中的继承小学问

W...Y的主页 &#x1f60a; 代码仓库分享&#x1f495; &#x1f354;前言&#xff1a; C中&#xff0c;继承是一种面向对象编程的重要概念&#xff0c;它允许一个类&#xff08;子类/派生类&#xff09;从另一个类&#xff08;父类/基类&#xff09;继承属性和方法。继承是…

C语言系列-整数在内存中的存储大小端字节序

&#x1f308;个人主页: 会编程的果子君 ​&#x1f4ab;个人格言:“成为自己未来的主人~” 目录 整数在内存中的存储 大小端字节序和字节序判断 什么是大小端 为什么会有大小端 练习 整数在内存中的存储 在讲解操作符的时候&#xff0c;我们就讲过了下面的内容 整数的2…

HashMap基本使用

特点&#xff1a; ①HashMap是Map里面的一个实现类。②没有额外需要学习的特有方法&#xff0c;直接使用Map里面的方法就可以了。③特点都是由键决定的&#xff1a;无序、不重复、无索引④HashMap跟HashSet底层原理是一模一样的&#xff0c;从名字可以看出来&#xff0c;都是哈…

MySQL原理(一)架构组成(2)逻辑模块组成

总的来说&#xff0c;MySQL可以看成是二层架构&#xff0c;第一层我们通常叫做SQL Layer&#xff0c;在MySQL数据库系统处理底层数据之前的所有工作都是在这一层完成的&#xff0c;包括权限判断&#xff0c;sql解析&#xff0c;执行计划优化&#xff0c;query cache的处理等等&…

麒麟系统—— openKylin 安装 Nacos

麒麟系统—— openKylin 安装 Nacos 一、准备工作1. 确保麒麟系统 openKylin 已经安装完毕。2. 确保 java 已经安装完毕3. 确保 Maven 已经安装完毕 二、下载 nacos三、解压与运行解压 关于 nacos 配置 本文将分享如何在麒麟系统 openKylin 上安装 Nacos。 一、准备工作 1. …

深度学习之卷积神经网络

卷积神经网络简称为CNN 首先我们来回顾一下&#xff0c;我们之前学到的全连接的神经网络&#xff1a; 上面我们通过线性层串行连接起来的神经网络&#xff0c;我们叫做全链接的网络&#xff0c;在线性层里面&#xff0c;我们的输入值和任意的输出值之间都存在权重&#xff0c;…

《HTML 简易速速上手小册》第10章:HTML 的维护与优化(2024 最新版)

文章目录 10.1 网页性能优化10.1.1 基础知识10.1.2 案例 1&#xff1a;优化网页图像10.1.3 案例 2&#xff1a;使用延迟加载优化性能10.1.4 案例 3&#xff1a;优化 CSS 和 JavaScript 的加载 10.2 SEO 最佳实践10.2.1 基础知识10.2.2 案例 1&#xff1a;创建一个 SEO 友好的博…

伊恩·斯图尔特《改变世界的17个方程》毕达哥拉斯定理笔记

它告诉我们什么&#xff1f; 直角三角形的三个边之间有什么关系。 为什么重要&#xff1f; 它提供了几何和代数之间的重要联系&#xff0c;使我们能够根据坐标计算距离。它也催生出了三角学。 它带来了什么&#xff1f; 测绘、导航&#xff0c;以及较近代出现的狭义和广义相对论…

深入了解Matplotlib中的子图创建方法

深入了解Matplotlib中的子图创建方法 一 add_axes( **kwargs):1.1 函数介绍1.2 示例一 创建第一张子图1.2 示例二 polar参数的运用1.3 示例三 创建多张子图 二 add_subplot(*args, **kwargs):2.1 函数介绍2.2 示例一 三 两种方法的区别3.1 参数形式3.2 布局灵活性3.3 适用场景3…

基于YOLOv8的摄像头吸烟行为检测系统(Python源码+Pyqt6界面+数据集)

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文主要内容:详细介绍了摄像头下吸烟行为检测系统&#xff0c;在介绍算法原理的同时&#xff0c;给出Pytorch的源码、训练数据集以及PyQt6的UI界面。在界面中可以选择各种图片、视频进行检测识别&#xff0c;可进行置信度、Iou阈值设定…

【linux】磁盘空间不足-常用排查和处理命令

【linux】磁盘空间不足-常用排查和处理命令 1.通查一下 df -h #查看服务器磁盘空间情况 du -hs * 2>/dev/null #列出各目录所占空间大小 或 du -h -d 1 2>/dev/null #列出各目录所占空间大小 1.1情况一 df 磁盘空间和du 目录空间占用相等&#xff0c…

离线安装nginx_银河麒麟系统_nginx报错_503_500 Internal Server Error----nginx工作笔记007

如果报这个错误,意思就是,对于nginx.conf文件中指定的,文件夹没有权限 那么这个是去给对应的文件夹赋权限: chmod 777 /opt/module/test_web 就可以了,然后再去访问就不会报错了,还有 503的错误都可以这样解决 然后关于离线安装nginx,尝试了一下如果把之前安装过的nginx,直接…

app逆向-frida定位签名校验

文章目录 一、前言二、如何实现签名校验三、案例&#xff1a;定位签名校验 一、前言 当我们说应用签名校验时&#xff0c;实际上是一种安全机制&#xff0c;用于确保移动应用在被安装和运行时没有被篡改或修改。这个机制通过在应用程序文件上附加一种数字签名的方式来实现。 …

2023年算法GWCA -CNN-BiLSTM-ATTENTION回归预测(matlab)

2023年算法GWCA -CNN-BiLSTM-ATTENTION回归预测&#xff08;matlab&#xff09; GWCA -CNN-BiLSTM-Attention长城建造算法优化卷积-长短期记忆神经网络结合注意力机制的数据回归预测 Matlab语言。 长城建造算法&#xff08;Great Wall Construction Algorithm&#xff0c;GWC…

Centos Cron设置定时任务

这本是很简单的问题&#xff0c;但是我服务器重装系统两次&#xff0c;遇到的问题都不一样&#xff0c;所以记录一下 1.首先要确保服务器上有 cron 服务 sudo systemctl status crond2.设置时区 sudo timedatectl set-timezone Asia/Shanghai3.重启crond 服务使crond服务的时…