【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.mzph.cn/news/820988.shtml

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

相关文章

动态规划专练( 198.打家劫舍)

198.打家劫舍 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一个代表每个…

设计模式学习笔记 - 设计模式与范式 -总结:1.回顾23中设计模式的原理、背后的思想、应用场景等

1.创建型设计模式 创建型设计模式包括:单例模式、工厂模式、建造者模式、原型模式。它主要解决对象的创建问题,封装复杂的创建过程,解耦对象的创建代码和使用代码。 1.单例模式 单例模式用来创建全局唯一的对象。一个类只允许创建一个对象…

Angular 使用DomSanitizer防范跨站脚本攻击

跨站脚本Cross-site scripting 简称XSS,是代码注入的一种,是一种网站应用程序的安全漏洞攻击。它允许恶意用户将代码注入到网页上,其他用户在使用网页时就会收到影响,这类攻击通常包含了HTML和用户端脚本语言(JS&…

微博百度热搜收集

背景 大家都有使用微博、百度吧,而每天的热搜想必大家也用的不少。微博、百度的热搜有7、8种分类,每个单独查看比较耗费时间,效率极低,大概要花费3,4分钟左右。最近闲来无事,冒出个想法,是不是有…

Python编程:高效数据处理与自动化任务实践

一、引言 Python,作为一种解释型、交互式、面向对象的编程语言,凭借其简洁易懂的语法和强大的功能库,已经成为数据科学、机器学习、Web开发等多个领域的首选工具。本文将探讨Python在数据处理和自动化任务方面的应用,通过具体的代码案例展示Python的强大功能。 二、数据处理…

官宣:2024第二十届国际铸造件展12月精彩呈现!

Shanghai International Die-casting Casting Expo 2024第二十届上海国际压铸、铸造展览会 2024第二十届上海国际压铸、铸件产品展 时间:2024年12月18-20日 地点:上海新国际博览中心(浦东区龙阳路2345号) 报名参展&#xff1…

Spring ApplicationEvent 事件处理--不用引入中间件

一、背景 有一天,小王向小李请教:“我碰到了一个功能需求,用户注册完账号后,我们需要通知他们进行激活。我想实现异步处理,但不知道怎么做才更优雅。”小李听后,不假思索地说:“你可以直接开启…

【Git】初识 Git

文章目录 1. 提出问题2. 如何解决?版本控制器3. 注意事项 1. 提出问题 不知道你工作或学习时,有没有遇到这样的情况:我们在编写各种文档时,为了防止文档丢失、更改失误、失误后能恢复到原来的版本,不得不复制出一个副…

CC工具箱使用指南:【浙江省村规结构调整表(杨欢)】

一、简介 群友定制工具。 这个工具的功能简单易懂,就是根据输入的现状用地和规划用地图层,生成浙江村规的结构调整表。 村规的结构调整表格式,各个省份都不太一样,无法做一个通用的工具,实在很让人头痛。 看了之后表…

多语言vue-i18n (vue2,uniapp)

安装vue-i18n npm install vue-i18n8 --save // npm install vue-i18n–save 9版本需要vue3.0 // 在vue2环境下,默认安装 npm install vue-i18n 的版本是 vue-i18n9.2.2, // 报错信息里提示这个版本要求是vue3,所以我们安装适合vue2版本的vu…

FactoryMethod工厂方法模式详解

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

linux系统简介与环境搭建

linux系统简介与环境搭建 注:该题册一旦提交不可见不可修改,甚至不知道成绩,请谨慎提交,以下答案均为个人见解,非标准答案,如有意见,请评论!!! 一、单 选 题…

libcurl 简单使用

LibCurl是一个开源的免费的多协议数据传输开源库,该框架具备跨平台性,开源免费,并提供了包括HTTP、FTP、SMTP、POP3等协议的功能,使用libcurl可以方便地进行网络数据传输操作,如发送HTTP请求、下载文件、发送电子邮件等…

数据结构DAY4--哈希表

哈希表 概念:相当于字典,可以根据数据的关键字来寻找相关数据的查找表。 步骤:建立->插入->遍历->查找->销毁 建立 建立数据,形式随意,但一般为结构体(储存的数据量大)&#xff…

Pytorch深度学习完整GPU图像分类代码

1. CPU与GPU不同 1.输入数据 2.网络模型 3.损失函数 .cuda() 说明:下面代码中GPU版本中取消下划线的即为CPU版本 2.完成的分类代码(GPU) import torch import torchvision from torch.utils.tensorboard import SummaryWriter# from model import * # 准备数据集 …

vue3 依赖-组件tablepage-vue3说明文档,列表页快速开发,使用思路及范例(Ⅳ)其他配置项

github求⭐ vue3 依赖-组件tablepage-vue3说明文档,列表页快速开发,使用思路及范例(Ⅰ)配置项文档 vue3 依赖-组件tablepage-vue3说明文档,列表页快速开发,使用思路及范例(Ⅱ)搜索…

OpenHarmony实战开发-如何实现发布图片评论功能。

介绍 本示例将通过发布图片评论场景,介绍如何使用startAbilityForResult接口拉起相机拍照,并获取相机返回的数据。 效果图预览 使用说明 通过startAbilityForResult接口拉起相机,拍照后获取图片地址。 实现思路 1.创建CommentData类&…

进程替换execl

#include<stdio.h> #include<unistd.h> // int execl(const char *path, const char *arg, ...); int main() {printf("start:\n");execl("/usr/bin/ls","ls","-a",NULL);printf("end!\n"); }如果没有exe…

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

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

华硕ROG幻16笔记本电脑模式切换管理工具完美替代华硕奥创中心管理工具

文章目录 华硕ROG幻16笔记本电脑模式切换管理工具完美替代华硕奥创中心管理工具1. 介绍2. 下载3. 静音模式、平衡模式、增强模式配置4. 配置电源方案与模式切换绑定5. 启动Ghelper控制面板6. 目前支持的设备型号 华硕ROG幻16笔记本电脑模式切换管理工具完美替代华硕奥创中心管理…