西邮 Linux 兴趣小组 2024 纳新面试题
学长寄语:长期以来,西邮 Linux 兴趣小组的面试题以难度之高名扬西邮校内。我们作为出题人也清楚的知道这份试题略有难度。请你动手敲一敲代码。别担心,若有同学能完成一半的题目,就已经十分优秀。其次,相比于题目的答案,我们对你的思路和过程更感兴趣,或许你的答案略有瑕疵,但你正确的思路和对知识的理解足以为你赢得绝大多数的分数。最后,做题的过程也是学习和成长的过程,相信本试题对你更加熟悉地掌握 C 语言一定有所帮助。祝你好运。我们东区逸夫楼 FZ103 见!
- 本题目只作为西邮 Linux 兴趣小组 2024 纳新面试的有限参考。
- 为节省版面,本试题的程序源码省去了
#include指令。 - 本试题中的程序源码仅用于考察 C 语言基础,不应当作为 C 语言「代码风格」的范例。
- 所有题目编译并运行于 x86_64 GNU/Linux 环境。
0. 聪明的吗喽
一个小猴子边上有 100 根香蕉,它要走过 50 米才能到家,每次它最多搬 50 根香蕉,(多了就拿不动了),它每走 1 米就要吃掉一根,请问它最多能把多少根香蕉搬到家里。
(提示:他可以把香蕉放下往返走,但是必须保证它每走一米都能有香蕉吃。也可以走到 n 米时,放下一些香蕉,拿着 n 根香蕉走回去重新搬 50 根。)
题解:
- 代码:
1 | #include<stdio.h> |
输出结果:
18解释:
- 由于小猴子一次性最多搬运50根香蕉,而在小猴子所持香蕉数大于50根的情况下,会先带着1批香蕉向前走1步,再拿1根香蕉返回上一个位置,再带着另1批香蕉向前走回刚刚向前走那1步的所在位置,即可以等效于,走1步消耗3根香蕉。
- 通过计算得知,这样走会剩下2根香蕉,在此刻我们再以该条件走1步,此时,小猴子所持香蕉数已经不大于50根了。我们便可以用走1步消耗1根香蕉的方式走完剩下的路程。
- 考点:
通过数学工具来解决实际问题的能力
1. 西邮Linux欢迎你啊
请解释以下代码的运行结果。
1 | int main() { |
题解:
输出结果:Hi guys! Join Linux - 2024(无限循环)
解释:
a为unsigned,即为非负数,故其在自减的过程中,会在减为0时返回到int类型最大值考点:
unsigned、补码
2. 眼见不一定为实
输出为什么和想象中不太一样?
你了解 sizeof() 和 strlen() 吗?他们的区别是什么?
1 | int main() { |
题解:
输出结果:
0
0
0
1解释:
- p0和p2的本质是一样的,p1是一个字符串指针。
strcmp函数在比较两个字符串时,碰到了两个'\0',则停止比较,返回ASCII码。sizeof对指针进行计算,结果为8(64位机),对字符串进行计算,结果为字符串的长度。
- 考点:
strcmp和sizeof
3. 1.1 - 1.0 != 0.1
为什么会这样,除了下面给出的一种方法,还有什么方法可以避免这个问题?
1 | int main() { |
题解:
输出结果:
b - a == ex is falseB - A == EX is true解释:
- a,b,c均为浮点数,其实际值在无限趋近于所给值。故a+b!=c。
- 将对应数乘10,其整数部分具有实际意义,小数部分在转换为int类型时被忽略,正好可以使得相加运算正确。
- 改进方法:
- 可以使用16进制保存a,b,c这些浮点数,在确定小数后几位的情况下,16进制是精准的。
- 可以使用误差容忍度,float下为1e-6,double下为1e-12。
- 考点:
浮点数、三元运算符
4. 听说爱用位运算的人技术都不太差
解释函数的原理,并分析使用位运算求平均值的优缺点。
1 | int average(int nums[], int start, int end) { |
题解:
- 输出结果:
num[5]={1,2,3,4,5}
3
-解释:
- 利用函数递归,将整个数组分成两个部分,分别求平均值,在把结果求平均值,得到最终结果,即分治。
- 优点:
- 避免了整数溢出的问题。
- 函数整体更整顿。
缺点: - 时间复杂度为O(n)
- 空间复杂度为O(logn)
- 考点:
位运算求平均值、算法的优化思想
5. 全局还是局部!!!
先思考输出是什么,再动动小手运行下代码,看跟自己想得结果一样不一样 >-<
1 | int i = 1; |
题解:
输出结果:
i = 11, j = 15
a = 11
i = 1, j = 15解释:
- 程序内有两个函数,func和main,func函数内使用了静态变量
j,和一个自动变量i,虽然在函数外部,还有一个变量i,但函数外部的变量i为外部链接的静态变量,他会被一个自动变量所“隐藏”。因此当我们在每次调用这个函数的时候,都会有一个自动变量i,来也匆匆去也匆匆。 - 而在不调用func函数的时候,自然也不会创建这个自动变量
i去覆盖那个静态变量i。于是在第三行打印的时候,i是1,j是15。
- 考点:
变量的作用域和存储期
6. 指针的修罗场:改还是不改,这是个问题
指出以下代码中存在的问题,并帮粗心的学长改正问题。
1 | int main(int argc, char **argv) { |
题解:
- 代码:
1 | #include<stdio.h> |
- 解释:
const在限定一个指针的时候,他和*的先后顺序需要细心关注一下。当*在const的左侧时,const限定了指针的地址;当*在const的右侧时,const限定了指针地址下的值;当*在const中间时,则地址和值都被限定。可以记为”左位右值“。
- 考点:
const限定符
7. 物极必反?
你了解 argc 和 argv 吗,这个程序里的 argc 和 argv 是什么?
程序输出是什么?解释一下为什么。
1 | int main(int argc, char *argv[]) { |
题解:
输出结果:
a = 0, b = -2147483646, c = -1解释:
- argc为程序命令行的数量,*argv[]内保存着命令行的具体内容。
- 由于while循环的判断条件和终止条件在同一行内,且其执行的十一条空语句,故一定会执行到argc小于0。由于变量在计算机内以补码形式保存,故为4字节下最小值
-2147483648。 - 在之后又进行了一次自增,把值赋给b后又自增,且在if语句内判断条件时,没有出现短路的情况,故所有操作都执行。
- 考点:
argc和argv
8. 指针?数组?数组指针?指针数组?
在主函数中定义如下变量:
1 | int main() { |
说说这些输出分别是什么?
1 | a, a + 1, &a, &a + 1, *(a + 1), sizeof(a), sizeof(&a) |
题解:
| &a[0] | &a[1] | 数组a的地址 | 数组a末尾的地址 | 8 | 8 | 8 |
| &a[0] | &a[1] | 数组a的地址 | 数组a末尾的地址 | 8 | 8 | 8 |
| &&a[0] | &&a[1] | 数组c的地址 | 数组c末尾的地址 | 8 | 8 | 8 |
- 解释:
a[2]是一个数组,(*b)[2]是一个指向含有2个元素的数组的指针,*c[2]是一个包含两个指针的数组。&a的意思是整个数组的地址,而&a[0]是指数组的入口地址。sizeof是计算其所占内存的大小。
- 考点:
数组,指针数组,数组指针、sizeof
9. 嘻嘻哈哈,好玩好玩
在宏的魔法下,数字与文字交织,猜猜结果是什么?
1 | #define SQUARE(x) x *x |
题解:
输出结果:
嘻嘻,结果你猜对了吗,包11滴解释:
- CONCAT的意思是,把a和b直接拼接起来,作为一个标识符。因此
x1=5,y2=3。 - 在SQUARE中,对
x1+1的操作为x1+1*x1+1,对y2的操作为y2*y2,取最大值赋值给max,打印max。
- 考点:
宏
10. 我写的排序最快
写一个 your_sort 函数,要求不能改动 main 函数里的代码,对 arr1 和 arr2 两个数组进行升序排序并剔除相同元素,最后将排序结果放入 result 结构体中。
1 | int main() { |
题解:
- 代码:
1 | #include<stdio.h> |
输出结果:
1 2 3 4 6 7 8 9 10解释:
- 首先定义了一个结构体,并且把这个结构体命名为
result。 - 在
your_sort函数内,分别进行了拼接,排序,去重三个步骤,并且使用了动态分配内存。这里的排序我使用的是冒泡排序。
- 考点:
结构体访问、动态分配内存、拼接,排序,去重
11. 猜猜我是谁
在指针的迷宫中,五个数字化身为神秘的符号,等待被逐一揭示。
1 | int main() { |
题解:
输出结果:
0
2
2
4
2解释:
*a[]是一个指针数组。在64位机中地址为8字节,且为小端存储。- 先转换为
char*类型,后移动1个字节,解引用的结果为0。 - 先转换为
char*类型,后转换为int*类型,对其4字节下的地址解引用,结果为1,1+1=2。 - 先转换为
long long*类型,后移动8*3=24个字节,解引用的结果为4。 - 先转换为
short*类型,后移动2*4=8字节,解引用的结果为2。
- 考点:
指针的类型转换和运算
12. 结构体变小写奇遇记
计算出 Node 结构体的大小,并解释以下代码的运行结果。
1 | union data { |
题解:
输出结果:
welcome to xiyoulinux_group!解释:
- 首先进行内存对齐,data为
24字节,Node为40字节。 - 在main函数内对P进行动态内存分配,大小为
40+30=70字节。 P->change = func把change函数指针指向了func,P->change(P)调用了指向的func函数。- 在其中还有一个字符串变为小写的tolower()函数,使得结果最后均为小写。
- 考点:
结构体和共用体内存对齐、动态内存分配、字符串常量的存储方式
13. GNU/Linux (选做)
注:嘿!你或许对Linux命令不是很熟悉,甚至没听说过Linux。
但别担心,这是选做题,了解Linux是加分项,不了解也不扣分哦!
- 你知道
ls命令的用法与/.~这些符号的含义吗? - 你知道 Linux 中权限
rwx的含义吗? - 请问你还懂得哪些与 GNU/Linux 相关的知识呢?
题解:
- 输出结果:
ls:列出文件ls /:列出根目录下的所有目录ls .:列出当前目录下的所有目录ls ~:列出用户主目录下的所有目录r:读取权限w:写入权限x:执行权限cd:更改目录
touch:创建文件
clear:清除终端页面
……