西邮Linux兴趣小组2023纳新题
- 本题目只作为西邮 Linux 兴趣小组 2023 纳新面试的有限参考。
- 为节省版面,本试题的程序源码省去了 #include 指令。
- 本试题中的程序源码仅用于考察 C 语言基础,不应当作为 C 语言「代码风格」的范例。
- 所有题目编译并运行于 x86_64 GNU/Linux 环境。
0. 鼠鼠我啊,要被祸害了
有1000瓶水,其中有一瓶有毒,小白鼠只要尝一点带毒的水,24小时后就会准时死亡。至少要多少只小白鼠才能在24小时内鉴别出哪瓶水有毒?
10
只即可
首先给这1000瓶水标上1~1000的二进制序号,最多需要十位二进制数就可以将1000瓶水全表示完。然后给十只标上1~10的序号,对应十个二进制位。如果某瓶水的序号中某一二进制位为1,则让对应序号的小鼠喝下这瓶水。最终根据24小时后小鼠的死亡情况可以得出一个十位二进制数,其所对应的十进制数就是有毒的水的序号。
1. 先预测一下~
按照函数要求输入自己的姓名试试~
1 | char *welcome() { |
三种可行的方法
1 | //1 |
1 | //2 |
1 | //3 |
局部变量离开生命周期后就会被销毁 不能返回局部变量的地址
所以
1 | char *welcome() { |
不可行
2. 欢迎来到Linux兴趣小组
有趣的输出,为什么会这样子呢~
1 | int main(void) { |
本题考察了printf的返回值。ptr0
指向W,ptr1
指向的也是W,所以 *ptr0==*ptr1
为真,执行if语句。 printf(“”) 成功打印了 0
个字节,返回的 0
作为另一个printf函数的参数打印出Hello, Linux Group - 20
,同时返回成功打印的字符数 24
。最后一个printf函数打印出 24
。最终打印的结果为
1 | Hello, Linux Group - 2024 |
3. 一切都翻倍了吗
请尝试解释一下程序的输出。
请谈谈对sizeof()和strlen()的理解吧。
什么是sprintf(),它的参数以及返回值又是什么呢?
1 | int main(void) { |
sizeof() 是一个运算符,而 strlen() 是一个函数。
sizeof() 计算的是变量所占用的内存字节数,而 strlen() 计算的是字符串中字符的个数。
strlen() 只能用于以 ‘\0’ 结尾的字符串。
sizeof() 括号中的语句不执行 而strlen() 括号中的语句会执行
sizeof() 计算字符串的长度,包含末尾的 ‘\0’。
strlen() 计算字符串的长度,不包含字符串末尾的 ‘\0’。
sprintf 与printf类似,不同的是printf是打印到stdout中,而sprintf是打印到一个字符串里。
关于sprintf的返回值:sprintf函数如果成功打印,则返回写入的字符总数,不包括字符串追加在字符串末尾的空字符。如果失败,则返回一个负数。
- 第一个printf里的
*&arr
其实就是arr
sizeof(arr)
求出的就是整个数组的大小 arr+0
表明它是一个右移了 0 个单位的指针sizeof(arr+0)
打印出来的就是(char*)类型指针的大小。- 因为sizeof()的结果是在编译时计算的,而strlen()的结果是在运行时计算的,所以sizeof()括号中的语句并不会执行。
sizeof(num = num2 + 4)
计算出来的就是num的大小。 - 此处的sprintf将num转换成一个十六进制数打印到str里,并且返回了成功打印的字符数。成功打印的字符的数量不等于num,所以
sprintf(str, "0x%x", num) == num
返回的是0,printf将其打印出来。 strlen(&str[0] + 1)
相当于strlen(str+1)
计算的是从str+1开始的长度,到’\0’为止。strlen(arr+0)
打印的是以’\0’终止的整个字符串str的长度。
4. 奇怪的输出
程序的输出结果是什么?解释一下为什么出现该结果吧~
1 | int main(void) { |
1 | 01000000 (64) |
1 | 01000000 (64) |
1 | 11000000 (-64) (源码) |
ch = 64 + 63 - (-1) = 128,但是char类型的大小为1个字节 范围为 -128 ~ 12701111111
(127) 再加1时会溢出为 10000000
(-128)
最终打印的结果为
1 | a = 64 b = 63 c = -1 ch = -128 |
5. 乍一看就不想看的函数
“人们常说互联网凛冬已至,要提高自己的竞争力,可我怎么卷都卷不过别人,只好用一些奇技淫巧让我的代码变得高深莫测。”
这个func()函数的功能是什么?是如何实现的?
1 | int func(int a, int b) { |
a ^ b
是按位异或运算,相当于不进位的加法。而(a & b) << 1
实现了进位。函数利用递归重复该过程,直到a = 0(停止进位)时返回b。该过程实现的其实就是a和b两个整数的相加,最终返回的就是a+b的值。
利用该函数求 1+1 的过程中 a和b的变化
1 | a b |
func(b, c)
就是 b+c
; printf()打印的其实就是 (a+(b+c)) 最终结果为6
6. 自定义过滤
请实现filter()函数:过滤满足条件的数组元素。
提示:使用函数指针作为函数参数并且你需要为新数组分配空间。
1 | typedef int (*Predicate)(int); |
代码及注释
1 |
|
7. 静…态…
如何理解关键字static?
static与变量结合后有什么作用?
static与函数结合后有什么作用?
static与指针结合后有什么作用?
static如何影响内存分配?
static
意为“静态”。static
修饰的变量具有默认的初始值0。static
修饰的局部变量的生命周期会延长至整个程序。
全局变量和函数被static
修饰会使其不能被其他文件访问,可以防止命名冲突。static
修饰的指针的生命周期会延长至整个程序。static
修饰的变量会存储在全局区。
8. 救命!指针!
数组指针是什么?指针数组是什么?函数指针呢?用自己的话说出来更好哦,下面数据类型的含义都是什么呢?
1 | int (*p)[10]; |
数组指针,是一个指向数组的指针。
指针数组,是一个存放指针的数组。
函数指针,是一个指向函数的指针。int (*p)[10];
是一个数组指针 指向一个int类型的 包含10个元素的数组。const int* p[10];
是一个常量数组指针。它指向一个常量。int (*f1(int))(int*, int);
定义了一个返回值为int(*)(int *,int),参数为int的函数。
9. 咋不循环了
程序直接运行,输出的内容是什么意思?
1 | int main(int argc, char* argv[]) { |
命令行参数相关知识argc
是 命令行参数 默认值为1。 while()语句中argc
递增 ,此过程中argc
先溢出为-2147483648,再递增到0后退出循环。 i = -1
; j = argc = 0
; k = 1
;
&&的优先级高于|| , 所以 i++ && j++
先进行。后置递增运算符在语句执行完后才进行递增,所以i++ && j++
中&&左边部分为真,继续执行&&右边部分,j递增。但是j在递增之前为0,所以i++ && j++
为假,继续执行||右边的部分,k递增。EXIT_SUCCESS
定义于 头文件<stdlib.h>
,return EXIT_SUCCESS
相当于 return 0
10. 到底是不是TWO
1 |
|
宏定义相关知识
#define定义别名是在预处理时直接在文本中替换 所以if(16 / CAL(2) == 2)
会被直接替换为
1 | if(16 / 2 * 2 * 2 == 2) |
显然if条件为假 不会打印 I'm TWO(ノ>ω<)ノ
else{}中定义的nums离开else{}就会被销毁,所以printf打印出来的就是一开始定义的nums的值 1
11. 克隆困境
试着运行一下程序,为什么会出现这样的结果?
直接将s2赋值给s1会出现哪些问题,应该如何解决?请写出相应代码。
1 | struct Student { |
将s2
直接赋值给s1
属于浅拷贝。s2
中的age
可以成功复制,但是复制使用malloc
动态开辟的name
时只能将s2.name
的地址赋给s1.name
。在使用free
释放时,会将s2.name
地址对应的内存重复释放,造成错误。
解决方法:使用strcpy()将s2.name复制到已经开辟好的s1.name的空间中即可。
代码:
1 |
|
12. 你好,我是内存
作为一名合格的C-Coder,一定对内存很敏感吧~来尝试理解这个程序吧!
1 | struct structure { |
字节序与大小端(struct structure *)arr
将arr强转为结构体指针类型访问联合体中的string成员,联合体union内的成员共用一块内存,内存大小至少为8。联合体在结构体内的对齐数为8,又因为前面还有一个int类型,所以联合体存储在结构体中偏移量为8的地址处。arr强转为结构体指针后访问联合体node中的成员string,相当于跳过了数组中的两个int类型元素,指向第三个元素0x636c6557
。
并且计算机一般采用小端存储,也就是对于arr中的第三个元素0x636c6557
57
存在低地址处,printf打印时从57
开始,将十六进制ascll码转换为字符,逐个打印,直到遇到’\0’。最终打印出来的是:
1 | Welcome to XUPT , welcome to Xiyou Linux Group [2023] |
13. GNU/Linux (选做)
注:嘿!你或许对Linux命令不是很熟悉,甚至你没听说过Linux。但别担心,这是选做题,了解Linux是加分项,但不了解也不扣分哦!
你知道cd命令的用法与 / . ~ 这些符号的含义吗?
请问你还懂得哪些与 GNU/Linux 相关的知识呢~
cd:切换工作目录
/ 根目录 . 当前目录 ~ user目录