本题目只作为西邮 Linux 兴趣小组 2024 纳新面试的有限参考。
为节省版面,本试题的程序源码省去了 #include 指令。
本试题中的程序源码仅用于考察 C 语言基础,不应当作为 C 语言「代码风格」的范例。
所有题目编译并运行于 x86_64 GNU/Linux 环境。
0. 聪明的吗喽
一个小猴子边上有 100 根香蕉,它要走过 50 米才能到家,每次它最多搬 50 根香蕉,(多了就拿不动了),它每走 1 米就要吃掉一根,请问它最多能把多少根香蕉搬到家里。
(提示:他可以把香蕉放下往返走,但是必须保证它每走一米都能有香蕉吃。也可以走到 n 米时,放下一些香蕉,拿着 n 根香蕉走回去重新搬 50 根。)
有一百根香蕉,因为他每次最多搬50根,所以将这100根香蕉分成a,b两组。所以把它分成前50根和后50根,在前50根的时候他每走一米,搬了a组,要吃一根,然后回去,又吃一根,然后将b组搬一米,吃一根,==所以每走一米要吃三根==,在前到16米时,它已经吃了48根,这时候要分第十七米了,它有两种选择:==1.直接拿着那个有50根香蕉的走,17米时吃了1根,剩了49根==;2。==继续像原来的方案一样,到十七米时也为52 - 3 = 49根*==;之后就每走一米然后吃一根,直到50米走完,49 - 33 = 16根。所以最后答案为*16根。**
1. 西邮Linux欢迎你啊
请解释以下代码的运行结果。
|
|
此处为printf函数嵌套,用到了printf的返回值,printf返回值为打印元素的个数.并且进入嵌套时从嵌套外进去嵌套,读取时从最内侧的开始向外侧读取,由于最里面没有东西,所以返回值是0,最外面的那个看的是里面printf的字符个数,一共有24个字符,所以最后会打印2024.unsigned int 的取值范围为==0 - 2^32 - 1==.当a等于0时,它会从0直接到最大值,不会有负数,所以==for循环会无限执行下去==。
2. 眼见不一定为实
输出为什么和想象中不太一样?
你了解 sizeof() 和 strlen() 吗?他们的区别是什么?
|
|
sizeof和strlen的区别:1.sizeof 是运算符,而strlen 是C语言库函数中的一个函数,且使用时包含==头文件<string.h>==;2.sizeof 操作符用于计算变量或类型的大小,一般单位为==字节==,通常用于计算内存大小。3.strlen是计算字符串长度的,遇到\0结束,==返回不包括\0==,即如果没有\0则会计算出随机值。
strcmp函数是C语言中的字符串比较函数,用于比较两个字符串的大小,且遇到\0或不同的字符会自动结束。==相等为0,第一个小于第二个,小于0==.这里的p1,p2,p0在\0处之前都相等,所以第一行需要打印的都为0;sizeof(第二条)会检索全部所以两个不相等,所以为0,strlen到\0结束,所以相等。
3. 1.1 - 1.0 != 0.1
为什么会这样,除了下面给出的一种方法,还有什么方法可以避免这个问题?
|
float、double为浮点数,小数位数有限,比较容易损失精度。float类型数字在计算器中以二进制类型存储,==对于一些十进制小数,不能精确地用二进制表示,所以会导致1.1 - 1.0 != 0.1.==
|
此时会计算为正确。除此之外,还可以用Decimal,Decimal 不会把小数转换为二进制,而是就用十进制。
4. 听说爱用位运算的人技术都不太差
解释函数的原理,并分析使用位运算求平均值的优缺点。
|
leftAvg & rightAvg:获取 leftAvg 和 rightAvg 的共同部分。
leftAvg ^ rightAvg:获取两个平均值不同的部分。
((leftAvg ^ rightAvg) >> 1):将不同的部分右移一位,相当于除以2。
最后,将共同部分与右移后的不同部分相加,得到最终的平均值。
以下面的例子为例
|
这个函数用递归的方式来求平均值,将数组分为左右两半,分别计算每一半的平均值。1 2 3 4 5
,第一次结束后为 1 2 3
4 5
,然后1 2
3
,4 5计算得 4(因为为int类型),1 2
计算为1,即 1 3 4
再分为1 3
4
,计算为2,4,最后算出为3
。
优点:高效,计算速度快。
缺点:误差太大。
5. 全局还是局部!!!
先思考输出是什么,再动动小手运行下代码,看跟自己想得结果一样不一样 >-<
|
|
先要搞清楚定义:局部变量:==定义在函数体内部的变量,作用域仅限于函数体内部。离开函数体就会无效。再调用就是出错。==
全局变量:==所有的函数外部定义的变量,它的作用域是整个程序,也就是所有的源文件,包括.c和.h文件。==
在 C 或 C++等语言中,如果在全局变量和局部变量(如在 main 函数中定义的变量)有相同的名称,在局部作用域(即 main 函数内部)中会优先使用局部变量,局部变量会“压制”全局变量。这是因为编译器在查找变量时,会首先在当前的局部作用域中查找,如果找到了同名变量就使用它,而不会使用全局作用域中的同名变量。所以,在func函数里,i被重新定义,优先使用i = 10,i++后为11,a = 11 % 15 = 11;==在main函数里没有i,所以会用全局变量==,i = 1;
6. 指针的修罗场:改还是不改,这是个问题
指出以下代码中存在的问题,并帮粗心的学长改正问题。
|
==第一个定义的是常量指针,它的值无法更改,但地址可以更改。
第二个定义的是指针常量,它的地址无法更改,但值可以更改。==
|
==第三个为常量指针常量,它的值无法更改,且它的地址无法更改。所以不能改变地址和值。==
7. 物极必反?
你了解 argc 和 argv 吗,这个程序里的 argc 和 argv 是什么?
程序输出是什么?解释一下为什么。
|
==argv[]是命令行参数,argc是从命令行传给程序的参数个数(至少为1)==
在没有传入参数时 argc = 1,while(argc++ > 0),会让他从1加到最大,直到溢出为-2147483648,–a是先给a减一,a = 0;c– ,先用c = 0,c = -1于是if不执行,所以执行最后。
|
8. 指针?数组?数组指针?指针数组?
在主函数中定义如下变量:
|
说说这些输出分别是什么?
|
|
由于题目可以看出来,第一个是数组,再看输出,a,a是一个数组,所以这里的a代表的是a数组的首地址,所以结果为a[0] (首地址)的地址;a + 1 是一个步长,int 类型步长是4个字节,所以为地址+4,为a[1]的地址;&a是给a取地址,所以为a整个的地址,但表示还是用a的首地址;&a 是指向整个数组的指针,当对这个指针执行 +1 操作时,它会根据整个数组的大小进行移动,所以移动8个字节;给a+1解引用,得到是a[1],即为8;sizeof是计算变量或类型的大小,一般单位为字节,此处为8字节;最后一个为指针,在64位系统中为8字节,32位为4字节。
第二个是数组指针, * b是取数组指针的第一个值,但这个值是&a,是a的地址;所以给* b+1,结果是a的地址加一,即为加了四个字节;b 代表的是一个指向数组 a 的指针,所以是整个数组的地址;b + 1的话,就是给整个数组a的地址+1,它会根据整个数组的大小进行移动,所以移动8个字节; *(*b + 1)就是给a + 1再解引用;sizeof(*b) 计算的是指针 b 指向的数组(即 a)的大小;sizeof(b) 返回的是指针 b 本身的大小,而b 是一个指向数组的指针,所以为8字节。
最后一个是指针数组,所以输出的是指向 c 数组的地址,c 的首地址是指向a的地址;c + 1为c的第二个元素的地址,即指向a + 1地址的地址;&c 的类型为数组指针,即指向包含整个a的数组的指针;&c + 1为在指向a这个数组的指针再走一步此时为16字节;解引用(c + 1)将得到 c[1],即 a + 1,这是指向 a 数组中第二个元素(值为 8)的指针,继续解引用,所以会得到a[1],就是8;sizeof的c是整个地址的字节,这里一个a的地址为4,但c指向的a的地址,所以一个指向地址的地址为8个字节,所以指针数组字节大小为16;sizeof(&c) 返回的是指向数组 c 的指针的大小,为8字节。
9. 嘻嘻哈哈,好玩好玩
在宏的魔法下,数字与文字交织,猜猜结果是什么?
|
|
这里CONCAT会使x1 = 5,y2 = 3;定义没有加括号(应该为这么定义#define SQUARE(x) ((x) * (x))),所以这里会写成5 + 1 x 5 + 1 = 11,令一个为9,max是判断谁更大,然后输出大的值即为11.
10. 我写的排序最快
写一个 your_sort 函数,要求不能改动 main 函数里的代码,对 arr1 和 arr2 两个数组进行升序排序并剔除相同元素,最后将排序结果放入 result 结构体中。
|
|
11. 猜猜我是谁
在指针的迷宫中,五个数字化身为神秘的符号,等待被逐一揭示。
|
|
****==void *a[] = {(void *)1, (void )2, (void )3, (void )4, (void )5}; 定义了一个void类型的数组 其中每个元素都是void类型的值 即每个元素都是一个十六进制的整形,且为小端储存==;
第一个这里你获取数据,但(char *)a + 1
是a的首地址强制转化为char类型,由于char只占一个字节,+1并取得第二个字节,这里指针为8字节,所以一个数字为0.5个字节,0000000000000001
-> 00000000000000
01,所以为0;
==第二个表达式先将a转换为字符指针,然后再转换为整型指针,再解引用,此时为第一个数就是1==,1 + 1就是2;
后面三个与第一个同理,3.转化为int类型,+2刚好是八字节,即2;
4.long long 为8字节,+3相当于走到数组第四个,即4;
5.short2字节,+4为8字节,所以到第二个,即2;
12. 结构体变小写奇遇记
计算出 Node 结构体的大小,并解释以下代码的运行结果。
|
Node *P = (Node *)malloc(sizeof(Node) + (strlen(s) + 1) * sizeof(char));
为柔性数组string分配内存空间。
结构体内存大小为24
;
|
13. GNU/Linux (选做)
注:嘿!你或许对Linux命令不是很熟悉,甚至没听说过Linux。
但别担心,这是选做题,了解Linux是加分项,不了解也不扣分哦!
你知道 ls 命令的用法与 / . ~ 这些符号的含义吗?
你知道 Linux 中权限 rwx 的含义吗?
请问你还懂得哪些与 GNU/Linux 相关的知识呢~
- ls 是一个用于列出目录内容的命令。常见用法包括:
ls
:列出当前目录的文件和子目录。ls -l
:以长格式显示文件和目录的详细信息,包括权限、拥有者、文件大小和修改时间。ls -a
:显示所有文件,包括以.开头的隐藏文件。
/:根目录,所有文件和目录的起始点。
.:当前目录的表示。
..:上一级目录的表示。
~:当前用户的主目录的快捷方式。
- 在 Linux 中,文件和目录的权限由三部分组成:用户、组和其他用户,每部分可以有三种权限:
r(read):读权限,允许查看文件内容。
w(write):写权限,允许修改文件内容。
x(execute):执行权限,允许执行文件(对目录来说,允许进入该目录)。
eg:rwxr-xr–