C语言进阶之笔试题详解(2)

前言

这里的内容包括二维数组笔试题和指针笔试题,供给读者对这部分知识进行加深和巩固。

158c3f50b199454985017a51dbef9841.png               ✨ 猪巴戒:个人主页✨

               所属专栏:《C语言进阶》

        🎈跟着猪巴戒,一起学习C语言🎈

目录

前言

笔试题

二维数组

题目

解析:

一维数组

二维数组

总结:

指针笔试题

题目1

解析:

题目2

解析:

总结:

题目3

解析

题目4

解析:

题目5

解析: 

总结:

题目6:

解析:

题目7: 

解析:

总结:

题目8:

解析:

最后


笔试题

二维数组

题目

以下代码的打印结果是什么?

int main()
{int a[3][4] = { 0 };printf("%d\n", sizeof(a));printf("%d\n", sizeof(a[0][0]));printf("%d\n", sizeof(a[0]));printf("%d\n", sizeof(a[0]+1));printf("%d\n", sizeof(*(a[0] + 1)));printf("%d\n", sizeof(a + 1));printf("%d\n", sizeof(*(a + 1)));printf("%d\n", sizeof(&a[0] + 1));printf("%d\n", sizeof(*a));printf("%d\n", sizeof(a[3]));return 0;
}

解析:

一维数组

一维数组的数组名表示的是首元素的地址,通过数组名加下标的形式可以访问数组的每个元素。

arr[1]这里的1就是元素的下标,arr[1]访问的是数组的第二个元素。

二维数组

arr[3][4]是一个二维数组,二维数组可以看作是3个一维数组组成,每个一维数组就是二维数组的一个元素,因此二维数组的首元素其实是第一行的所有元素。

二维数组的数组名arr,表示的是第一行的地址,也就是第一个一维数组的地址。一共有多少行就有多少个一维数组。

arr[0]其实就是第一个一维数组的数组名。

arr[1]就是第二个一维数组的数组名。

arr[2]就是第三个一维数组的数组名。

一共是3行,就是3个一维数组。

想要访问二维数组的每个元素:一维数组的数组名加下标,arr[0][0],访问的是第一行第一列的元素。

449142bd904342adb372616a9323ed0a.png

1|printf (" %d\n ", sizeof ( a ) )  48

这里a是数组名,sizeof(数组名),就代表整个数组的大小,数组一共12个元素,每个元素4个字节,整个数组的大小就是48个字节。

2|printf (" %d\n ", sizeof ( a[0][0] ) )  4

a[0][0]就是第一行第一列的元素,一个整形元素,就是4个字节的大小。

3|printf (" %d\n ", sizeof ( a[0] ) )   16

a[0]就是第一个一维数组,a[0]就是第一个一维数组的数组名,sizeof(数组名)表示的就是整个数组的大小,这个一维数组一共有4个元素,每个元素的大小是4个字节。

4|printf (" %d\n ", sizeof ( a[0]+1 ) )  4 / 8

a[0]就是第一个一维数组的数组名,因为a[0]并不是单独放在sizeof内部的,所以这里的a[0]表示的就是首元素的地址,a[0]是一维数组,首元素地址就是第一行第一列的元素的地址。那a[0]+1表示的就是第一行第二个的元素的地址,地址为4个字节或者8个字节。

5|printf (" %d\n ", sizeof ( *( a[0] + 1) ) )  4

在4|中,我们就分析了a[0]+1是第一行第二个元素的地址,现在把它解引用。*(a[0]+1)得到的就是第一行第二个元素,一个整形元素的大小是4个字节。

6|printf (" %d\n ", sizeof ( a + 1 ) )  4 / 8

a是一个二维数组,a不是单独放在sizeof内部,a表示的就是首元素的地址,而二维数组首元素的地址表示的是第一个一维数组的地址,也就是第一行的地址,a+1表示的就是第二个一维数组的地址,也就是第二行的地址,即使是一维数组的地址,大小也是4个字节或者8个字节。

7|printf (" %d\n ", sizeof ( *(a + 1) ) )  16

在6|中,我们分析到a+1表示的就是第二个一维数组的地址,也就是第二行的地址,这里对第二个一维数组进行解引用操作,*(a+1)得到的就是第二个一维数组的元素,一共有4个元素,每个元素的大小是4个字节。

8|printf (" %d\n ", sizeof ( &a[0] + 1 ) )  4 / 8

&a[0],&数组名表示就是取整个数组的地址,a[0]就是第一个一维数组,取的就是第一行的地址,,因为这个地址的类型是一行的地址,&a[0]+1跳过一行,得到的就是第二行的地址。地址的大小是4个字节或者8个字节。

9|printf (" %d\n ", sizeof ( *a ) )  16

a是一个二维数组,a并没有单独放在sizeof的内部,a表示的就是首元素的地址,二维数组首元素的地址,就是第一行的地址。*a对第一行的地址解引用得到的就是第一行的所有元素,一个4个元素,每个元素的大小是4个字节。

10|printf (" %d\n ", sizeof ( a[3] ) )   16

sizeof()只关心内部类型的大小,如果有这个二维数组第4行,同样是是4个元素,sizeof(数组名),依旧是向后访问4个元素,每个元素是整形的大小,所以是16个字节,sizeof不会真的去访问,程序会返回16。


总结:

数组名的意义:

1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。

2.&数组名,这里的数组名表示整个数组,去除掉是整个数组的地址。

3.除此之外所以的数组名都表示首元素的地址。

4.二维数组可以看作有多个一维数组组成,一维数组可以看作二维数组的每个元素,行数代表制二维数组的意思个数,列数代表一维数组的元素个数。


指针笔试题

题目1

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

解析:

*(a+1):  2

a是数组名,表示的是首元素的地址,一个元素的地址,+1就跳过一个元素,所以(a+1)表示就是第二个元素的地址,解引用得到的就是第二个元素,等于2.

db48ce2b09304072b2f0f6f0df08f191.png

 *(ptr-1):  5

&a,a是数组名,&a取的是整个数组的地址,+1跳过整个数组,指向的位置:

735a314caa0a42739dfe248ea6169ab3.png

将(&a+1)强制类型转化为(int*),就是由数组指针转成整形指针的类型, 原来是数组的地址,所以是数组指针,现在是整形指针的地址,就意味着,+1或-1的操作只能跳过一个整形元素的大小。

ptr-1跳过4个字节,位置如下图所示,对ptr-1解引用就得到5.

495143f5b89d4a63a9682e247a4c872e.png


题目2

假设p的值为0x100000.如下表表达式的值分别为多少?

已知,结构体Test类型的变量大小是20个字节

struct Test
{int Num;char* pcName;short sDate;char cha[2];short sBa[4];
}*p;int main()
{printf("%p\n",p+0x1);printf("%p\n",(unsigned long)p +0x1);printf("%p\n",(unsigned int*)p +0x1);return 0;
}

解析:

1.    printf (" %p\n ", p+0x1 )

p是结构体指针,结构体变量的大小是20个字节,

指针+1,跳过一个指针变量的大小,0x100000这是16进制,我们加上20个字节

十进制中的20转化为十六进制就是14.

结果为0x100014.

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

将p一个结构体指针强制类型转化为unsigned long类型,也就是整数,那结果就为

0x100001

因为指针+1-1需要考虑指针类型,整数直接加减就可以了

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

p是结构体指针,将p强制类型转化为unsigned int* 类型,无符号整形指针,整形指针+1就会跳过一个整形的大小,也就是4个字节。结果:

0x100004


总结:

这道题考察的是

指针+1-1的操作,什么类型的指针就会跳过一个多大的元素。

比如整形指针+1就会跳过4个字节,字符指针+1就会跳过1个字节。

-1也类比如此。


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

解析

1.    int* ptr1 = (int*)(&a + 1)

&a+1,&数组名,取的是整个数组的地址,所以加1就会跳过整个数组。

(int*)(&a+1),将(&a+1)强制类型转化成整形指针,ptr1就是一个整形指针

ptr1[-1],ptr1[-1]其实就等同于*(ptr1-1),ptr1是整形指针,所以-1跳过一个整形元素,4个字节。

ptr1[-1]的打印结果:

0x00 00 00 04 打印会省略前面的0,所以结果为4

%x是打印十六进制的数字

7af11eabfee04ea49ef60eb75d1fc6f3.png

5544c9edcc2144958cd1c9ce7fdcc884.png

 2.    int* ptr2 = (int*) ( ( int ) a + 1)  0x02 00 00 00

a是数组名,表示首元素的地址

(int) a 对首元素的地址强制类型转化为(int)

(int)a+1,a已经强制类型转化为整数,+1直接在a的地址加1就可以了。

(int*) ( ( int ) a + 1),强制类型转化会整形指针,那整形指针ptr2指向的就是a+1指向的后面4个字节。

msvc采用的是小端存储模式,所以读出来的要再次进行倒置,结果:

0x02 00 00 00

%x是打印十六进制

c5c7ff19ead143ab89b4b64ea8d0fd7c.png


题目4

#include <stdio.h>
int main()
{int arr[3][2] = { (0,1),(2,3),(4,5) };int* p;p = arr[0];printf("%d", p[0]);return 0;
}

解析:

如果我们想要对一个三行二列的数组进行初始化,我们可以用这种形式。

int arr[3][2] = { {0,1}, {2,3}, {4,5} };

07b08596205643e38deef172d55ace25.png

但是用()就不是这个初始化了,这是逗号表达式,逗号表达式取最后的一位,依次取,剩余不够就会初始化为0。

int arr[3][2] = { (0,1),(2,3),(4,5) };
//实际上
int arr[3][2] = {   1  ,  3  ,  5   };

2fd41f3a0b174644a6ba2b8b0cff041f.png

arr[0]是第一行的数组名,arr[0]表示首元素的地址,a[0][0]的地址,&a[0][0],第一行的地址和第一个元素的地址表示形式是一样的

int*p,p是整形指针, 所以存放的是arr[0][0]的地址

p[0] -> *(p+0) -> *p

结果: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;
}

解析: 

a是每行有5个元素,一共5行的二维数组。

p是数组指针,一个指向4个整形的数组。

p = a, 数组名a表示首元素的地址,即第一行的地址,地址的表示形式就是第一个元素的地址,p只有4个元素,但是地址的位置传给了p。

&p[4][2]-->*(*(p+4)+2),p是一个4个整形元素的数组指针,所以p+1就跳过4个字节,

*(p+4)就是一个4个元素的整形数组的数组名,表示这个数组首元素的地址,即整形指针,整形指针+2,跳过2个整形元素,实际上是8个字节。

&a[4][2],找到数组a第5行第3列的元素地址位置

指针-指针,指针之间相减,等于指针之间的元素个数。

&p[4][2] - &a[4][2],由于是低地址-高地址,结果为:-4 

2df109d7d46e43b1b9d45a0dc6b08718.png

%p的打印结果

%p是打印地址,地址是没有负数概念的,所以这里的-4是将其存放在内存中的补码打印出来,-4的补码是:11111111111111111111111111111100 

将它变成十六进制,0xFFFF FFFC

10eb87872aa94816bb8e4609063a6cee.png

%d的打印结果

-4


总结:

1.指针相减得到的是指针之间的元素个数。

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

解析:

*(ptr1-1):  10

&aa取的是二维数组的起始地址,&aa+1就会跳过整个二维数组。

然后传给整形指针ptr1,整形指针ptr1-1,向前跳过一个元素。

ptr1-1指向的数字就是10

5b03b17c2e8146c4b31ce55ced6ff20e.png

 *(ptr2-1):  5

aa为首元素的地址,即第一行的地址,aa+1跳过一个一维数组,就是第二行的地址

*(aa+1)对第二行的地址解引用得到的就是第二行的数组名,数组名表示首元素的地址,ptr2表示的是6的地址。

ptr2-1,ptr2是整形指针,-1向前跳过1个元素,得到的就是5的地址。

*(ptr2-1),解引用得到5.

题目7: 

#include <stdio.h>
int main()
{char* a[] = { "work","at","alibaba" };char** pa = a;pa++;printf("%s\n", *pa);return 0;
}

解析:

pa是一个char**类型,一个二级字符指针,指向的是a,a表示数字首元素的地址,就是“work”的地址。

因为pa是char**类型,所以pa+1,跳过一个char*元素,pa++也就是跳到“at”。

对pa解引用,得到的结果:

at

247a4779f3df4897bb011eccb80515c1.png


总结:

 1.整形指针int*,+1-1跳过一个整形元素

2.二级字符指针char**,+1-1跳过一个char*类型的元素。


题目8:

int main()
{char* c[] = { "ENTER","NEW","POITER","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;
}

解析:

e01456759e90464f9ca84dcf59ab6aa4.png

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

cpp = cp,cpp指向的是数组cp的首元素,就是c+3.

++cpp,cpp指向的位置发生了改变,cpp指向c+2

*++cpp,解引用后,得到的就是c+2.

**++cpp,再次解引用,得到的就是c+2所指向的"POINT".

  打印结果:POINT

65d62babadf84cd68b5db0ef38d4044f.png

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

上一轮的操作中,cpp所指向的位置已经发生了改变,cpp指向的对象是c+2

++cpp,cpp所指向的位置改变,指向c+1.

*++cpp,解引用,就是c+1

--*++cpp,前置减减,得到的c.

*--*++cpp,得到c所指向的对象,“ENTER”

*--*++cpp+3,这个*--*++cpp得到的是c所指向的对象,即字符串首个字符的地址,就是字符指针。

字符指针+3,跳过3个字符,得到的结果是:

“ER”

在字符数组中的字符串存放的其实是这个常量字符串首个字符的地址,

47ab00d2559e47e68518fe53dd075d89.png

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

经过上一轮,cpp所指向的对象变成了cp数组的第三个元素,第三个元素本来是c+1,但是--之后变成了c。

cpp[-2],等同于*(cpp-2),cpp向前跳过2个元素,指向的对象变成了c+3,然后解引用得到c+3.

*cpp[-2],对c+2进行解引用,c+3所指向的对象是“FIRST”,得到“FIRST”.得到的“FIRST”其实是F的地址,是一个字符指针

*cpp[-2]+3,字符指针+3,跳过3个字符,得到的结果就是“ST”.

7e45326ee7734f04979dbe33b0841efc.png

4.    printf (" %s\n ", cpp[-1][-1] + 1)

cpp指向cp的第三个元素,第三个元素已经变成了c。

cpp[-1][-1],等同于*(*(cpp-1)-1)

cpp-1,cpp指向的是cp第三个元素,向前移动一位,得到c+2的地址.

*(cpp-1),对c+2的地址解引用,就会得到c+2.

*(cpp-1)-1,c+2-1,得到的就是c+1。

*(*(cpp-1)-1),对c+1进行解引用,得到的就是c所指向的对象,“NEW”.“NEW”得到的是N的地址,是一个字符指针。

以上对cpp[-1][-1]的解析,

cpp[-1][-1] + 1,我们已经得到“ENTER”,也就是字符指针,字符指针+1-1跳过一个字符,+1跳过N,得到的结果是“EW”.

e9c7a741f6f640efbc142f91fc7da7d5.png

最后

 如果你能够走到这里,那么恭喜你,请给自己比个大拇指吧!

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

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

相关文章

VR Interaction Framework2.0使用

1 按键 &#xff0c;比如按压下手柄的B键 if (InputBridge.Instance.BButtonDown){print("kkkkkkbbbbb456");} 2抓取某个物体&#xff0c;那么就在要抓取的那个物体上加一些组件&#xff0c;特别是Grabble Unity Events 其中UIExAmple为XR Demo自带的 UI wer:我挂的…

中国毫米波雷达产业分析4——毫米波雷达企业介绍

一、矽典微 &#xff08;一&#xff09;公司简介 矽典微致力于实现射频技术的智能化&#xff0c;专注于研发高性能无线技术相关芯片&#xff0c;产品广泛适用于毫米波传感器、下一代移动通信、卫星通信等无线领域。 整合自身在芯片、系统、软件、算法等领域的专业能力&#xf…

【JavaEE】多线程 -- 死锁问题

目录 1. 问题引入 2.死锁问题的概念和原因 3. 解决死锁问题 1. 问题引入 在学习死锁之前, 我们先观察下面的代码能否输出正确的结果: 运行程序, 能正常输出结果: 这个代码只管上看起来, 好像是有锁冲突的, 此时的 locker 对象已经是加锁的状态, 在尝试对 locker 加锁, 不应该…

Ubuntu下载离线安装包

旧版Ubuntu下载地址 https://old-releases.ubuntu.com/releases/ 下载离线包 sudo apt-get --download-only -odir::cache/ncayu install net-tools下载snmp离线安装包 sudo apt-get --download-only -odir::cache/root/snmp install snmp snmpd snmp-mibs-downloadersudo a…

unity Terrain 性能问题

在实践过程中unity发生进入场景GPU爆显存的情况&#xff0c;经过调查发现是使用Terrain造成的问题&#xff0c;这个问题在使用一个Terrain的时候并不会发生&#xff0c;但是在使用多个时会发生。 似乎在使用过程中Terrain会直接把Terrain的整个地图加载&#xff0c;造成移动设…

Ubuntu+Tesla V100环境配置

系统基本信息 nvidia-smi’ nvidia-smi 470.182.03 driver version:470.182.03 cuda version: 11.4 查看系统体系结构 uname -aUTC 2023 x86_64 x86_64 x86_64 GNU/Linux 下载miniconda https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/?CM&OA https://mi…

Dockerfile讲解

Dockerfile 1. 构建过程解析2. Dockerfile常用保留字指令3. 案例3.1. 自定义镜像mycentosjava83.2. 虚悬镜像 4. Docker微服务实战 dockerfile是用来构建docker镜像的文本文件&#xff0c;是由一条条构建镜像所需的指令和参数构成的脚本。 dockerfile定义了进程需要的一切东西&…

[蓝桥杯习题]———位运算、判断二进制1个数

⭐Hello!这里是欧_aita的博客。 ⭐今日语录&#xff1a;行动胜过一切。 ⭐个人主页&#xff1a;欧_aita ψ(._. )>⭐个人专栏&#xff1a; 数据结构与算法&#xff08;内含蓝桥杯习题&#xff09; MySQL数据库 位运算 位运算位运算的定义简单运用 实战刷题题目思路代码实现声…

flutter,uni-app开发调试ios

一、申请ios开发者账号 二、ios开发者配置 ios 开发者需要配置的地方 https://developer.apple.com/account/resources/certificates/list Certificates&#xff08;证书&#xff09;: 作用&#xff1a; 证书用于对应用程序和开发者进行身份验证&#xff0c;确保安全性和可…

mac截图Snagit 中文介绍

1.超越普通的屏幕截图 TechSmith Snagit 是唯一具有内置高级图像编辑和屏幕录制功能的屏幕捕获软件。因此&#xff0c;您可以在一个程序中轻松创建高质量的图像和视频。 2.最后&#xff0c;屏幕捕获软件可以完成您所做的一切 快速解释一个过程如果您正在努力清楚地沟通&…

2023 年 IntelliJ IDEA下载、安装教程,附详细图文

大家好&#xff0c;今天为大家带来的是 2023年 IntelliJ IDEA 下载、安装教程&#xff0c;超详细的图文教程&#xff0c;亲测可用。 文章目录 1 IDEA 下载2 IDEA 安装3 IDEA 使用4 快捷键新手必须掌握&#xff1a;Ctrl&#xff1a;Alt&#xff1a;Shift&#xff1a;Ctrl Alt&a…

STM32CubeIDE(CUBE-MX hal库)----定时器

系列文章目录 STM32CubeIDE(CUBE-MX hal库)----初尝点亮小灯 STM32CubeIDE(CUBE-MX hal库)----按键控制 STM32CubeIDE(CUBE-MX hal库)----串口通信 文章目录 系列文章目录前言一、定时器二、使用步骤三、HAL库实验代码三、标准库代码 前言 STM32定时器是一种多功能外设&#…

【MATLAB】异常数据识别

基于分位数的异常点识别 首先&#xff0c;给定了一个原始数据序列x。然后&#xff0c;计算了序列x的上四分位数和下四分位数&#xff0c;并根据这两个值计算了异常点的阈值。上四分位数减去1.5倍的四分位数范围得到异常值下界&#xff0c;下四分位数加上1.5倍的四分位数范围得…

Selenium介绍及基本使用方法

Selenium是一个开源、免费、简单、灵活&#xff0c;对Web浏览器支持良好的自动化测试工具&#xff0c;在UI自动化、爬虫等场景下是十分实用的&#xff0c;能够熟练掌握并使用Selenium工具可以大大的提高效率。 Selenium简介 Selenium支持多平台、多浏览器、多语言去实现自动化…

【Qt绘图】之绘制坦克

使用绘图事件&#xff0c;绘制坦克。 效果 效果很逼真&#xff0c;想象力&#xff0c;有没有。 示例 代码像诗一样优雅&#xff0c;有没有。 包含头文件 #include <QApplication> #include <QWidget> #include <QPainter>绘制坦克类 class TankWidge…

【docker系列】docker实战之部署SpringBoot项目

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Docker篇之利用docker搭建ftp服务器可实现多用户上传

一、前言 场景&#xff1a;公司需要搭建FTP服务器&#xff0c;供内网之前可以互相传递数据&#xff0c;安全稳定&#xff0c;需要满足开通多个账号&#xff0c;每个用户上传的文件有自己对应的文件目录。 这里建议&#xff1a;用户目录Disk尽量大一点&#xff0c;避免因为空间不…

低代码平台在数字化转型过程中的定位

内容来自演讲&#xff1a;郭昊东 | 上海外服 | 流程分析工程师 摘要 本文介绍了外服集团的 IT 共享中心在低代码平台应用开发方面的实践经验。他们选择低代码平台的原因包括开发成本低、快速看到实际产品以及能够解决数据孤岛和影子 IT 等问题。他们在应用开发中面临的挑战包括…

android shape绘制半圆

<?xml version"1.0" encoding"utf-8"?><shape xmlns:android"http://schemas.android.com/apk/res/android"android:shape"rectangle"><sizeandroid:width"20dp"android:height"10dp" /><…

HBase数据模型杂谈

1.概述 HBase是一个稀疏、多维度、排序的映射表&#xff0c;这张表的索引是行键、列族、列限定符和时间戳。 每个值是一个未经解释的字符串&#xff0c;没有数据类型。用户在表中存储数据&#xff0c;每一行都有一个可排序的行键和任意多的列。表在水平方向由一个或者多个列族…