西邮Linux兴趣小组2022纳新面试题题解
感谢 Zhilu 重新录入题目原件。好人一生平安。
- 本题目只作为
Xiyou Linux兴趣小组
2022纳新面试的有限参考。- 为节省版面,本试题的程序源码省去了
#include
指令。- 本试题中的程序源码仅用于考察C语言基础,不应当作为C语言「代码风格」的范例。
- 题目难度随机排列。
所有题目编译并运行于x86_64 GNU/Linux
环境。学长寄语:
长期以来,西邮Linux兴趣小组的面试题以难度之高名扬西邮校内。我们作为出题人也清楚的知道这份试题略有难度。请别担心。若有同学能完成一半的题目,就已经十分优秀。 其次,相比于题目的答案,我们对你的思路和过程更感兴趣,或许你的答案略有瑕疵,但你正确的思路和对知识的理解足以为你赢得绝大多数的分数。最后,做题的过程也是学习和成长的过程,相信本试题对你更加熟悉的掌握C语言的一定有所帮助。祝你好运。我们FZ103见!Copyright © 2022 西邮Linux兴趣小组, All Rights Reserved.
本试题使用采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。
0. 我的计算器坏了?!
2^10=1024
对应于十进制的4位,那么2^10000
对应于十进制的多少位呢?`
解
int main(){
int a=pow(2,10000),b=0;
while(a>0){
a/=10;
b++;
}
}
1. printf还能这么玩?
尝试着解释程序的输出。
int main(void) {
if ((3 + 2 < 2) > (3 + 2 > 2))
printf("Welcome to Xiyou Linux Group\n");
else
printf("%d\n", printf("Xiyou Linux Group - 2%d", printf("")));
}
if中条件((3 + 2 < 2) > (3 + 2 > 2))=(0>1)=(0),运行else中语句,其中printf(“”)返回值为0,不输出。printf(“Xiyou Linux Group - 2%d”, printf(“”))的返回值为22,输出Xiyou Linux Group - 20。最外层的printf输出22。
printf函数返回打印字符串的长度,如果函数执行失败,则返回一个负数。
scanf函数返回成功读入的数据项数,读入数据时遇到了“文件结束”则返回EOF(end of file)。
对参数的运算顺序为从右向左。
2. 你好你好你好呀!
- 程序的输出有点奇怪,请尝试解释一下程序的输出吧。
- 请谈谈对
sizeof()
及strlen()
的理解吧。
int main(void)
{
char p0[] = "Hello,Linux";
char *p1 = "Hello,Linux";
char p2[11] = "Hello,Linux";
printf("p0==p1: %d, strcmp(p0,p2): %d\n", p0 == p1, strcmp(p0, p2));
printf("sizeof(p0): %zu, sizeof(p1): %zu, sizeof(*p2): %zu \n",
sizeof(p0), sizeof(p1), sizeof(*p2));
printf("strlen(p0): %zu, strlen(p1): %zu\n", strlen(p0), strlen(p1));
}
第一个printf中判断的是p0,p1的地址,地址不同则p0p1返回值为0,p2字符数组少一位存储\0,strcmp返回值错误。
第二个printf中sizeof返回占字节数,p0为12(1(char)*12(数组大小)),p1 (char *)类型占8字节,*
p2为p2首地址为1(char)。
第三个printf中strlen返回字符串长度(不包括"\0"),p0,p1均返回11.
%zu为无符号十进制,需要size_t型的参数,即sizeof函数,strlen函数的返回值。
strcmp函数基本形式为strcmp(str1,str2),若str1=str2,则返回零;若str1<str2,则返回负数;若str1>str2,则返回正数。
3. 换个变量名不行吗?
请结合本题,分别谈谈你对C语言中「全局变量」和「局部变量」的「生命周期」理解。
int a = 3;//生命周期为整个程序
void test()
{
int a = 1;//生命周期为test函数中
a += 1;
{
int a = a + 1;//生命周期为6至9行的代码块
printf("a = %d\n", a);
}
printf("a = %d\n", a);
}
int main(void)
{
test();
printf("a= %d\n", a);
}
{
int a = a + 1;
printf("a = %d\n", a);
}
这一块的变量a没有定初值,输出错误值。
在test函数中a=1+1=2,输出2.
main函数取全局变量a=3,输出3。
4. 内存对不齐
union
与struct
各有什么特点呢,你了解他们的内存分配模式吗。
typedef union
{
long l;//8字节
int i[5];
char c;
} UNION;
typedef struct
{
int like;
UNION coin;
double collect;//8字节
} STRUCT;
int main(void)
{
printf("sizeof (UNION) = %zu \n", sizeof(UNION)); //24字节(4*5+4(空))
printf("sizeof (STRUCT) = %zu \n", sizeof(STRUCT));//40字节(4+4(空)+24+8)
}
结构体,联合体占内存需是内部最大类型的倍数,而联合体只存储其中类型,则需按其中字节最大的类型分配内存。
5. Bitwise
- 请使用纸笔推导出程序的输出结果。
- 请谈谈你对位运算的理解。
int main(void)
{
unsigned char a = 4 | 7;
a <<= 3;
unsigned char b = 5 & 7;
b >>= 3;
unsigned char c = 6 ^ 7;
c = ~c;
unsigned short d = (a ^ c) << 3;
signed char e = -63;
e <<= 2;
printf("a: %d, b: %d, c: %d, d: %d \n", a, b, c, (char)d);
printf("e: %#x \n", e);
}
00000010 | 00000011 | 00000110 | 00111000 | 11000001 |
---|---|---|---|---|
00000111 | 00000111 | 00000111 | 11111110 | e<<=2 |
a = 4 |7=00000111 | b = 5 & 7=00000011 | c = 6 ^ 7=00000001 | a^c=11000110 | 00000100 |
a<<=3 | b >>= 3 | c=~c | d = (a ^ c) << 3 | |
00111000 | 00000000 | 11111110 | 00110000 |
%#x:输出时加上进制0x。
6. 英译汉
请说说下面数据类型的含义,谈谈
const
的作用。
char *const p
。限定指针的地址不可改变。char const *p
。限定指针指向的内容不可改变。const char *p
。同二
const:限定一个变量不允许被改变,产生静态作用。
7. 汉译英
请用变量
p
给出下面的定义:
- 含有10个指向
int
的指针的数组。- 指向含有10个
int
数组的指针。- 含有3个「指向函数的指针」的数组,被指向的函数有1个
int
参数并返回int
。
1.int *a[10]
2.int (*a)[10]
3.int (*a)(int x)[3]
8. 混乱中建立秩序
你对排序算法了解多少呢?
请谈谈你所了解的排序算法的思想、稳定性、时间复杂度、空间复杂度。提示:动动你的小手敲出来更好哦~
时间复杂度是对处理规模量为n的数据,算法执行核心代码(即:算法中执行频度最高的代码)的次数。
空间复杂度是算法在计算机内执行时对所需存储空间的度量。
稳定性是指在排序中对于相等的两个元素a,b。如果排序前a在b的前边,排序之后a也总是在b的前边。他们的位置不会因为排序而改变称之为稳定。反之,如果排序后a,b的位置可能会发生改变,那么就称之为不稳定。稳定性的重要性体现在其属性中含多种数据时。
选择排序:遍历数组元素,找到其中最大值与首位互换,再在剩余元素中找到最大值与第二位互换。依次排序,得到由大到小的数组。
冒泡排序:从首位开始,与下一位比较,若下一位大,不变。若首位大,互换。再比较2,3位。依次到n-1,n位。
循环n次。依次排序,得到由小到大的数组。
插入排序:从第二位到第n位,与前一位比较,若前一位小,不变。若前一位大,互换,再与前一位的前一位比较。以此类推,得到由小到大的数组。
#include <stdio.h>
#include <stdlib.h>
int main() {
int a[100];
int i, n,d, b;
scanf("%d", & n);
for (i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
for (i = 1; i < n; i++) {
for (d = i ; d > 0; d--) {
if (a[d - 1] > a[d]) {
b = a[d - 1];
a[d - 1] = a[d];
a[d] = b;
} else {
break;
}
}
}
for (i = 0; i < n; i++) {
printf("%d", a[i]);
}
}
以上排序的时间复杂度均为(n^2)
9. 手脑并用
请实现ConvertAndMerge函数:
拼接输入的两个字符串,并翻转拼接后得到的新字符串中所有字母的大小写。提示:你需要为新字符串分配空间。
char* convertAndMerge(/*补全签名*/);
int main(void) {
char words[2][20] = {
"Welcome to Xiyou ", "Linux Group 2022"};
printf("%s\n", words[0]);
printf("%s\n", words[1]);
char *str = convertAndMerge(words);
printf("str = %s\n", str);
free(str);
}
解
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *convertAndMerge(char x[2][20]) {
char *a = (char *)malloc(sizeof(char) * 40);
char b[40];
a = strcat(x[0], x[1]);
for (int i = 0; i < 40; i++) {
if (a[i] >= 'A' && a[i] <= 'Z') {
a[i] += 32;
}else{
if (a[i] >= 'a' && a[i] <= 'z') {
a[i] -= 32;
}
}
}
return a;
}
int main() {
char words[2][20] = {
"Welcome to Xiyou ", "Linux Group 2022"};
printf("%s\n", words[0]);
printf("%s\n", words[1]);
char *str = convertAndMerge(words);
printf("str=%s\n", str);
free(str);
}
10. 给你我的指针,访问我的心声
程序的输出有点奇怪,请尝试解释一下程序的输出吧。
int main(int argc, char **argv) {
int arr[5][5];
int a = 0;
for (int i = 0; i < 5; i++) {
int *temp = *(arr + i);
for (; temp < arr[5]; temp++) *temp = a++;
}
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
printf("%d\t", arr[i][j]);
}
}
}
第一次循环,temp指向数组首地址,内层循环25次,给数组,依次赋值0-24。外层循环i=1,temp指向a[1],内层循环20次,依次赋值25-44。再循环三次分别指向a[2],a[3],a[4],内层循环次数依次-5,最后输出
0 1 2 3 4 25 26 27 28 29 45 46 47 48 49 60 61 62 63 64 70 71 72 73 74
11. 奇怪的参数
你了解argc和argv吗?
直接运行程序argc的值为什么是1?
程序会出现死循环吗?
#include <stdio.h>
int main(int argc, char **argv)
{
printf("argc = %d\n", argc);
while (1)
{
argc++;
if (argc < 0)
{
printf("%s\n", (char *)argv[0]);
break;
}
}
}
argc 表示传入main函数的参数个数;2这个文件
argv 表示传入main函数的参数序列或指针,并且第一个参数argv[0]一定是程序的名称,并且包含了程序所在的完整路径。
2.因为只传入了一个参数(即执行这个程序的命令)。
3.不会。因为argc是int型的范围为-231到231-1。所以当argc=231-1时,+1会超范围变成-231,break结束。
12. 奇怪的字符
程序的输出有点奇怪,请尝试解释一下程序的输出吧。
int main(int argc, char **argv)
{
int data1[2][3] = {
{
0x636c6557, 0x20656d6f, 0x58206f74},
{
0x756f7969, 0x6e694c20, 0x00000000}};
int data2[] = {
0x47207875, 0x70756f72, 0x32303220, 0x00000a32};
char *a = (char *)data1;
char *b = (char *)data2;
char buf[1024];
strcpy(buf, a);
strcat(buf, b);
printf("%s \n", buf);
}
大小端:对于一个由2个字节组成的16位整数,在内存中存储这两个字节有两种方法:一种是将低序字节存储在起始地址,这称为小端字节序;另一种方法是将高序字节存储在起始地址,这称为大端字节序。
即:
大端是高字节存放到内存的低地址。
小端是高字节存放到内存的高地址。
上题为小端。
13. 小试宏刀
- 请谈谈你对
#define
的理解。- 请尝试着解释程序的输出。
#include <stdio.h>
#define SWAP(a, b, t) t = a; a = b; b = t
#define SQUARE(a) a *a
#define SWAPWHEN(a, b, t, cond) if (cond) SWAP(a, b, t)
int main() {
int tmp;
int x = 1;
int y = 2;
int z = 3;
int w = 3;
SWAP(x, y, tmp);
printf("x = %d, y = %d, tmp = %d\n", x, y, tmp);
if (x > y) SWAP(x, y, tmp);
printf("x = %d, y = %d, tmp = %d\n", x, y, tmp);
SWAPWHEN(x, y, tmp, SQUARE(1 + 2 + z++ + ++w) == 100);
printf("x = %d, y = %d,tmp=%d\n", x, y, tmp);
printf("z = %d, w = %d ,tmp = %d\n", z, w, tmp);
}
#define是预处理指令,称为宏定义。它将标识符定义为其后的常量。在预编译时所执行的操作就是简单的“文本”替换,将程序中的标识符替换为常量。
第一个swap,将x,y互换。此时x>y,再互换。swapwhen ()替换为
if(1+2+ z++ + ++w*1+2+ z++ + ++w)
tmp=x;
x=y;
y=tmp;
则x=2,y=2,ymp=2,z=5,w=5。
14. GNU/Linux命令 (选做)
你知道以下命令的含义和用法吗:
注:
嘿!你或许对Linux命令不是很熟悉,甚至你没听说过Linux。
但别担心,这是选做题,不会对你的面试产生很大的影响!
了解Linux是加分项,但不了解也不扣分哦!
ls
//打印出当前目录的列表rm
//删除一个文件或者目录。whoami
//查看当前用户名的命令请问你还了解哪些GNU/Linux的命令呢。