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

news2024/11/27 0:19:16

目录

 

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.coloradmin.cn/o/1599851.html

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

相关文章

设计编程网站集:动物,昆虫,蚂蚁养殖笔记

入门指南 区分白蚁与蚂蚁 日常生活中,人们常常会把白蚁与蚂蚁搞混淆,其实这两者是有很大区别的,养殖方式差别也很大。白蚁主要食用木质纤维,会给家庭房屋带来较大危害,而蚂蚁主要采食甜食和蛋白质类食物,不…

pytorch 今日小知识3——nn.MaxPool3d 、nn.AdaptiveAvgPool3d、nn.ModuleList

MaxPool3d — PyTorch 2.2 documentation 假设输入维度(1,2,3,4,4) maxpool torch.nn.MaxPool3d(kernel_size(2, 2, 2), stride(2, 2, 2), padding(1, 0, 0))F 维的 kernel_size 为 2,说明在 F 维的覆盖的 frame 数为 2,也就是…

Buck变换电路

Buck变换电路 Buck变换电路是最基本的DC/DC拓扑电路,属于非隔离型直流变换器,其输出电压小于输入电压。Buck变换电路具有效率高、输出稳定、控制简单和成本低的优点,广泛应用于稳压电源、光伏发电、LED驱动和能量回收系统。 电路原理 Buck变…

Noisy Student(CVPR 2020)论文解读

paper:Self-training with Noisy Student improves ImageNet classification official implementation:https://github.com/google-research/noisystudent 本文的创新点 本文提出了一种新的半监督方法Noisy Student Training,主要包括三步…

每日一题 — 串联所有单词的子串

30. 串联所有单词的子串 - 力扣(LeetCode) 思路:因为words里面的每一个字符串的长度都是固定的,所以可以将题转换成字符在字符串中的所有异位词 设出哈希表定义left和right进窗口维护count判断出窗口维护count 代码: …

Langchain入门到实战-第三弹

Langchain入门到实战 Langchain中RAG入门官网地址Langchain概述代码演示调用RAG功能更新计划 Langchain中RAG入门 Retrieval Augmented Generation 翻译成中文是“检索增强生成” 官网地址 声明: 由于操作系统, 版本更新等原因, 文章所列内容不一定100%复现, 还要以官方信息…

算法打卡day35

今日任务: 1)343. 整数拆分 2)96.不同的二叉搜索树 3)复习day11 343. 整数拆分 题目链接:343. 整数拆分 - 力扣(LeetCode) 给定一个正整数 n,将其拆分为至少两个正整数的和&#xf…

Vue3 + Element-Plus 使用 Table 插槽时数据未及时更新

Vue3 Element-Plus 使用 Table 插槽时数据未及时更新 问题重现解决方法最终效果 问题重现 这里我已经通过二级分类 id 查询到一级分类和二级分类&#xff0c;但是使用插槽和 v-for 渲染出来还是之前的分类 id&#xff0c;但是一点击表格或者保存代码他又能正常刷新出来。 <…

算法设计与分析实验报告c++实现(矩阵链相乘、投资问题、完全背包问题、数字三角形、最小生成树、背包问题)

一、实验目的 1&#xff0e;加深学生对分治法算法设计方法的基本思想、基本步骤、基本方法的理解与掌握&#xff1b; 2&#xff0e;提高学生利用课堂所学知识解决实际问题的能力&#xff1b; 3&#xff0e;提高学生综合应用所学知识解决实际问题的能力。 二、实验任务 用动态…

1.2MHz,固定频率白光LED驱动器

一、产品概述 TX6216是一款升压转换器&#xff0c;设计用于通过单节锂离子电池驱动多达7个串联的白光LED。 TX6216采用电流模式&#xff0c;固定频率架构来调节LED电流&#xff0c;LED电流通过外部电流检测电阻测量。其低104mV反馈电压可降低功率损耗并提高效率。 TX6216具有…

笔试题1 -- 吃掉字符串中相邻的相同字符(点击消除_牛客网)

吃掉字符串中相邻的相同字符 文章目录 吃掉字符串中相邻的相同字符题目重现解法一&#xff1a;(基于 erase() 函数实现)解法二&#xff1a;&#xff08;利用 栈 辅助实现&#xff09;总结 题目链接&#xff1a; 点击消除_牛客网 题目重现 牛牛拿到了一个字符串。 他每次“点击…

JMeter控制器数据库获取一组数据后遍历输出

目录 1、测试计划中添加Mysql Jar包 2、添加线程组 3、添加 jdbc connection configuration 4、添加JDBC Request&#xff0c;从数据库中获取数据 5.获取数据列表&#xff0c;提取所有goodsName信息 6.通过添加控制器遍历一组数据 6.1 方式一&#xff1a;循环控制器方式 …

Vue3从入门到实战:深度掌握通信插槽slot

slot_默认插槽的概念&#xff1a; 在Vue中&#xff0c;插槽&#xff08;slot&#xff09;是一种用于在组件中插入内容的特殊技术。默认插槽是其中一种类型的插槽&#xff0c;它允许你在组件的模板中指定一个位置&#xff0c;以便在使用组件时插入自定义的内容。 想象一下你有…

Linux中安装seata

Linux中安装seata 一、准备1、环境2、下载3、上传到服务器4、解压 二、配置1、备份配置文件2、导入sql3、修改配置前4、修改配置后5、在nacos中配置 三、使用1、启动2、关闭 一、准备 1、环境 因为要在 nacos 中配置&#xff0c;要求安装并启动 nacos 。可以参考这篇博客。 …

【十一】MyBatis Plus 原理分析

MyBatis Plus 原理分析 摘要 Java EE开发中必不可少ORM框架&#xff0c;目前行业里最流行的orm框架非Mybatis莫属了&#xff0c;而Mybatis框架本身没有提供api实现&#xff0c;所以市面上推出了Mybatis plus系列框架&#xff0c;plus版是mybatis增强工具&#xff0c;用于简化My…

【JAVA基础篇教学】第十二篇:Java中多线程编程

博主打算从0-1讲解下java基础教学&#xff0c;今天教学第十二篇&#xff1a;Java中多线程编程。 多线程编程是利用多个线程同时执行任务来提高程序的效率和性能。在 Java 中&#xff0c;多线程编程可以通过继承 Thread 类或实现 Runnable 接口来实现。下面是一个简单的多线程…

IntelliJ IDEA2024 安装包(亲测可用)

目录 一、软件简介 二、软件下载 一、软件简介 IDEA&#xff08;Integrated Development Environment for Apache&#xff09; 是一款专为 Apache 开发者设计的集成开发环境。该软件提供了丰富的功能和工具&#xff0c;帮助开发者更高效地创建、调试和部署 Apache 项目。 主…

自定义类型: 结构体 (详解)

本文索引 一. 结构体类型的声明1. 结构体的声明和初始化2. 结构体的特殊声明3. 结构体的自引用 二. 结构体内存对齐1. 对齐规则2. 为啥存在对齐?3. 修改默认对齐值 三. 结构体传参四. 结构体实现位段1. 什么是位段?2. 位段的内存分配3. 位段的应用4. 位段的注意事项 ​ 前言:…

计算机系列之操作系统的系统

2、大话操作系统的启动 当按下开机键时&#xff0c;BIOS 就会开始执行 ​ BIOS 就是放在主板上 ROM 里面的一段程序。 ​ ROM Read Only Memory&#xff08;只能读取的内存&#xff09; ​ 所以 BIOS 在出厂的时候就可以直接写死在 ROM 里面。 ​ 每次开机的时候&#xff…

JavaScript 高性能编程 —— 加载和运行

JavaScript 在浏览器中的性能,可认为是开发者所要面对的最重要的可用性问题。此问题因 JavaScript 的阻塞特征而复杂,也就是说,当 JavaScript 运行时其他的事情不能被浏览器处理。 事实上,大多数浏览 器使用单进程处理 UI 更新和 JavaScript 运行等多个任务,而同一时间只能…