【C语言】带你完全理解指针(六)指针笔试题

目录

 

1.

 2.

 3.

4.

5.

6.

 7.

 8.


 

1.

int main()
{
	int a[5] = { 1, 2, 3, 4, 5 };
	int* ptr = (int*)(&a + 1);
	printf("%d,%d", *(a + 1), *(ptr - 1));
	return 0;
}

【答案】

2,5

【解析】

定义了一个指向整数的指针ptr,并将其初始化为&a + 1。这里&a表示整个数组a的地址,而&a + 1表示数组a之后的位置,也就是数组a的末尾之后的位置。

由于ptr指向a的末尾之后的位置,所以*(ptr - 1)表示ptr指向的位置向前偏移一个int的大小,即指向了数组a的最后一个元素。

然后在printf语句中使用*(a + 1)等价于a[1]表示数组a中的第2个元素,也就是2。使用*(ptr - 1)表示数组a中的最后一个元素,也就是5。

因此,最终输出结果为2,5

图解:



 

 2.

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;
}
printf("%p\n", p + 0x1);

 这个表达式中,p是一个指针,+ 0x1表示将指针向后偏移1个Test结构体的大小。由于Test结构体的大小是20字节,所以偏移后的地址为0x100014。在printf中使用%p格式化字符串输出这个地址


printf("%p\n", (unsigned long)p + 0x1);

这个表达式中,首先将指针p强制转换为unsigned long类型,然后再加上0x1。这样会将指针的值视为无符号长整型进行计算。假设在你的系统中,unsigned long类型和指针类型都占用4个字节,那么指针的值将会被当做无符号长整型的值来处理。也就是说现在是无符号数了不是指针了。在此基础上,再加上0x1。结果为0x100001。在printf中使用%p格式化字符串输出这个地址。


printf("%p\n", (unsigned int*)p + 0x1);

这个表达式中,首先将指针p强制转换为unsigned int*类型,这里假设unsigned int类型占用4个字节,然后再加上0x1。这样做会将指针的值视为unsigned int类型的指针,并对指针值进行偏移。由于指针类型是unsigned int*,每次偏移一个字节,所以结果为0x100004。在printf中使用%p格式化字符串输出这个地址。



 3.

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

 

ptr1[-1]等价于*(ptr1-1),表示ptr1向前挪动一个整型指向4,结果就为4

*ptr2整型指针访问四个字节(即红框所框处),此时为内存存放的图解,需要转成真实值即0x02000000。



4.

int main()
{
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };
	int* p;
	p = a[0];
	printf("%d", p[0]);
	return 0;
}

题中的是小括号,( , )即逗号表达式。a[3][2]中实际存储的是{1,3,5}。将a[0]赋值给p指针,即p指向数组第一行地址,再通过p[0]找到第一行第一个元素即1。 

 



5.

int main()
{
	int a[5][5];
	int(*p)[4];
	p = a;
	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
	return 0;
}

 【答案】

FFFFFFFC,-4

a的类型是int (*) [5] ——— 即a指针指向一个大小为5的数组

p的类型是int (*) [4] ——— 即p指针指向一个大小为4的数组

当把a所指的地址赋值给指针p之后,a p指向同一地址,但是由于a认为自己指向的是5个元素的数组,而p认为自己指向的是4个元素的数组,这就会导致它们就算下标相同时访问到的内容也是不一样的,如图所示。

printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);

随着数组下标的增长,地址是由低到高的变化,并且指针和指针相减的绝对值得到的是指针之间的元素个数,而当取出p[4][2]和a[4][2]的地址之后相减,就是小地址减去大地址,得到一个负数,就是-4。

%p是打印地址,会认为内存中存储的补码就是地址,所以就是打印-4的补码

原码:10000000 00000000 00000000 00000100

反码:11111111  11111111  11111111   11111011

补码:11111111  11111111  11111111   11111100

以%p地址的形式打印补码转成16进制表示:FF FF FF FC

原码反码补码的转换在之前的博客有涉及原码、反码、补码。有符号整数的表示和运算。-CSDN博客

而-4以%d形式打印就是-4



6.

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

 【答案】10,5

【解析】

  1. 首先,int aa[2][5] 声明了一个2行5列的二维数组,并初始化了其中的元素。

  2. int* ptr1 = (int*)(&aa + 1); 中的 &aa 是取二维数组 aa 的地址,表示整个二维数组的起始地址。然后通过 + 1 将指针向后移动,指向数组的下一个位置。最后将结果强制转换为 int* 类型,赋值给 ptr1

  3. int* ptr2 = (int*)(*(aa + 1)); 中的 *(aa + 1) 表示取二维数组 aa 中的第2行的地址。然后将结果强制转换为 int* 类型,赋值给 ptr2

  4. *(ptr1 - 1) 表示 ptr1 所指向的元素的前一个元素的值。在这里,它指向二维数组 aa 的最后一个元素。

  5. *(ptr2 - 1) 表示 ptr2 所指向的元素的前一个元素的值。在这里,它指向二维数组 aa 的第一行的最后一个元素。

所以,最后的输出结果为:
10, 5,其中 *(ptr1 - 1) 的值为二维数组 aa 的最后一个元素 10,*(ptr2 - 1) 的值为二维数组 aa 的第一行的最后一个元素 5。

 



7.

int main()
{
	char* a[] = { "work","at","alibaba" };
	char** pa = a;
	pa++;
	printf("%s\n", *pa);
	return 0;
}

【答案】at

【解析】

 a的地址赋值给pa。然后通过pa++pa指向了a数组中的第二个指针元素



 8.

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

【答案】

POINT

ER

ST

EW 

【解析】

最初状态:


printf("%s\n", **++cpp);

** ++cpp,cpp先自增1,跳过一个char**类型指向下一个元素的地址,如图。

此时再对cpp进行解引用,找到cp[1],再解引用,找到c[2]即P的地址。此时%s打印出来就是POINT


printf("%s\n", *-- * ++cpp + 3);

【* -- * ++cpp + 3】,加号优先级是最低的,所以最后算。cpp先自增1跳过一个char**大小指向下一个元素的地址,如图。

此时再对cpp进行解引用,找到的是cp[2]即指向了c+1这块空间,再自减1则指向了c这块空间,再解引用找到了c[0]即ENTER中第一个E的地址,此时+3跳过三个char,最后指向ENTER中第二个E的地址,此时用%s打印出来就是ER

第一步++cpp也就是指向cp中的第三个元素c+1这块空间

第二步*++cpp,也就是得到cp[2]

第三步-- * ++cpp,c+1变为c

第四步* -- * ++cpp,找到了c中的第一个元素所指向的ENTER的空间指针指向E

第五步* -- * ++cpp + 3,从指向E变为指向E。


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

*cpp[-2] + 3】等价于【* *(cpp-2)+3】,与前面的cpp自增自减不同,cpp-2只是表达式并不会改变cpp的指向。

第一步cpp指向的位置向前移动两位之后解引用找到cp中的第一个元素也就是c+3,

第二步解引用找到c中的第4个元素也就是c[3]指向的是first。

第三步指针加三也就是从指向F变为指向S

所以答案是ST


printf("%s\n", cpp[-1][-1] + 1);
	return 0;

【 cpp[-1][-1] + 1】等价于【*(*(cpp-1) -1) + 1】

*(cpp-1)找到c+2,c+2再-1就是c+1的地址,再进行解引用找到NEW中N的地址,再+1找到E的地址,此时用%s打印出来就是EW

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

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

相关文章

FactoryMethod工厂方法模式详解

目录 模式定义实现方式简单工厂工厂方法主要优点 应用场景源码中的应用 模式定义 定义一个用于创建对象的接口,让子类决定实例化哪一个类。 Factory Method 使得一个类的实例化延迟到子类。 实现方式 简单工厂 以下示例非设计模式,仅为编码的一种规…

在centos8.5上迁移深度学习环境的时候碰到的一下问题(需要运维人员解决的)

我负责的是将开发服务器上的深度学习环境进行打包并将该环境迁移到生产服务器上,这些操作可以在其他博客中搜到 本文主要介绍我把环境包上传至生产服务器中的anaconda/envs/路径下,解压之后,运行测试代码时遇到的问题 IT部门是如何处理的&am…

mfc 带有复选框的ListBox

mfc 带有复选框的 ListBox 效果: 添加 ListBox 控件 从工具箱拖拽 ListBox 控件到窗口上,并设置属性: 包含字符串:true所有者描述:Fixed 给ListBox添加控制变量 添加完后,将m_list_box的类型使用CC…

Rustdesk如何编译代码实现安装后,不会在右下角出现托盘图标

环境: Rustdesk1.1.9 问题描述: Rustdesk如何编译代码实现安装后,不会在右下角出现托盘图标 解决方案: 安装后只有自定义进程图标 详细方案,有需要私聊

L2-1 堆宝塔分数

本题链接:PTA | 程序设计类实验辅助教学平台 题目: 样例: 输入 11 10 8 9 5 12 11 4 3 1 9 15 输出 4 5 思路: 这是一道模拟题,需要有耐心读题,跟着题目走一遍,就可以了。 代码详解如下&…

中霖教育:没有计量相关工作经验可以考注册计量师吗?

不可以,报考注册计量师需要满足相关工作年限要求,如果没有相关工作经验是不能报考的。 具体要求如下: 一级计量师; 1.取得理学类或工学类专业大学专科学历,工作满6年,其中从事计量技术工作满4年; 2.取得理学类或工…

每日OJ题_完全背包②_力扣322. 零钱兑换

目录 力扣322. 零钱兑换 问题解析 解析代码 优化代码(滚动数组) 力扣322. 零钱兑换 322. 零钱兑换 难度 中等 给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。 计算并返回可以…

外观模式:简化复杂系统的统一接口

在面向对象的软件开发中,外观模式是一种常用的结构型设计模式,旨在为复杂的系统提供一个简化的接口。通过创建一个统一的高级接口,这个模式帮助客户端通过一个简单的方式与复杂的子系统交互。本文将详细介绍外观模式的定义、实现、应用场景以…

链表拓展之双向链表

前言 在前面已经总结了单链表,有了单链表的基础会很好理解双链表的实现,忘记了可以跳转——>http://t.csdnimg.cn/GFPk9 接下来就由我带着各位看官来认识今天的主角吧~ 什么是双向链表 在单链表的基础上,它有两个方向的链接,一…

加强fou循环的坑

今天遇到了一个有趣的事情,使用加强fou循环操作list时,会报错并发操作异常。 直到看了编译类,才发现,加强fou循环其实就是通过迭代器操作: 这里就会出现一个问题,迭代器在取出值时,就回去检测这…

分析ARP解析过程

一、实验环境 主机A和主机B连接到交换机,并与一台路由器互连,如图7.17所示,路由器充当网关。 图7.17 二、需求描述 查看 ARP 相关信息,熟悉在PC 和 Cisco 设备上的常用命令,设置主机A和主机B为同一个网段网关设置为路由接口地址。 三、推…

基于Python的景区票务人脸识别系统(V2.0)

博主介绍:✌IT徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇&#x1f3…

排列特征重要性(Permutation Feature Importance)

5个条件判断一件事情是否发生,每个条件可能性只有2种(发生或者不发生),计算每个条件对这件事情发生的影响力。排列特征重要性模型的程序。 例一 在机器学习领域,排列特征重要性(Permutation Feature Impor…

QT 串口助手 学习制作记录

QT 串口助手qt 学习制作记录 参考教程:​​​​​​QT初体验:手把手带你写一个自己的串口助手_qt设计串口助手的流程图-CSDN博客 Qt之串口编程(添加QSerialPort模块)_如何安装 qt串口模块教程-CSDN博客 串口调试助手&#xff1…

2.2 @SpringBootApplication

2.2 SpringBootApplication 在前文的介绍中,读者已经了解到SpringBootApplication注解是加在项目的启动类上的。 SpringBootApplication实际上是一个组合注解,定义如下: SpringBootConfiguration EnableAutoConfiguration ComponentScan(exc…

python-常用数据结构(2)

6、某企业为职工发放奖金:如果入职超过5年,且销售业绩超过15000元的员工,奖金比例为0.2;销售业绩超过10000元的员工,奖金比例为0.15:销售业绩超过5000元的员工,奖金比例为0.1;其他奖金比例为0.05。如果是人职不超过5年,且销售业绩超过4000的员工,奖金比例为0.045;否则为0.01。输…

使用Python模仿文件行为

在Python中,你可以通过文件操作函数(如open()函数)以及模拟输入输出流的库(如io模块)来模拟文件行为。下面是一些示例,展示了如何使用这些工具在Python中模拟文件行为。 1、问题背景 在编写一个脚本时&…

深度挖掘响应式模式的潜力,从而精准优化AI与机器学习项目的运行效能,引领技术革新潮流

​🌈 个人主页:danci_ 🔥 系列专栏:《设计模式》 💪🏻 制定明确可量化的目标,坚持默默的做事。 🔥 转载自热榜文章:探索设计模式的魅力:深度挖掘响应式模式的…

基于Docker构建CI/CD工具链(六)使用Apifox进行自动化测试

添加测试接口 在Spring Boot Demo项目里实现一个简单的用户管理系统的后端功能。具体需求如下: 实现了一个RESTful API,提供了以下两个接口 : POST请求 /users:用于创建新的用户。GET请求 /users:用于获取所有用户的列…

爬取东方财富股票代码

我们打开东方财富网站&#xff1a;http://quote.eastmoney.com/stocklist.html 假如懒得爬&#xff0c;也可以用现成的股票数据源&#xff1a;https://stockapi.com.cn 这展示了所有股票信息&#xff0c;不过需要我们分页去爬取 我们可以查询具体的html代码&#xff1a; <…
最新文章