西邮Linux兴趣小组2022纳新面试题题解
- 本题目只作为
Xiyou Linux兴趣小组
2022纳新面试的有限参考。- 为节省版面,本试题的程序源码省去了
#include
指令。- 本试题中的程序源码仅用于考察C语言基础,不应当作为C语言「代码风格」的范例。
- 题目难度随机排列。
所有题目编译并运行于x86_64 GNU/Linux
环境。学长寄语:
长期以来,西邮Linux兴趣小组的面试题以难度之高名扬西邮校内。我们作为出题人也清楚的知道这份试题略有难度。请别担心。若有同学能完成一半的题目,就已经十分优秀。 其次,相比于题目的答案,我们对你的思路和过程更感兴趣,或许你的答案略有瑕疵,但你正确的思路和对知识的理解足以为你赢得绝大多数的分数。最后,做题的过程也是学习和成长的过程,相信本试题对你更加熟悉的掌握C语言的一定有所帮助。祝你好运。我们FZ103见!Copyright © 2022 西邮Linux兴趣小组, All Rights Reserved.
本试题使用采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。
文章目录
- 0. 我的计算器坏了?!
- 1. printf还能这么玩?
- 2. 你好你好你好呀!
- 3. 换个变量名不行吗?
- 4. 内存对不齐
- 5. Bitwise
- 6. 英译汉
- 7. 汉译英
- 8. 混乱中建立秩序
- 9. 手脑并用
- 10. 给你我的指针,访问我的心声
- 11. 奇怪的参数
- 12. 奇怪的字符
- 13. 小试宏刀
- 14. GNU/Linux命令 (选做)
0. 我的计算器坏了?!
2^10 = 1024
对应于十进制的4位,那么2^10000
对应于十进制的多少位呢?
对于十进制数d
,可以取其以10为底的对数,[lg d] + 1
即为所求的位数。
[lg 2^10] + 1 = [10 lg 2] + 1 = 4
[lg 2^10000] + 1 = 3011
当然,也可以使用Python,将2^10000
转换为字符串并打印其长度解决。
print(len(str(2**10000))) //3011
1. printf还能这么玩?
尝试着解释程序的输出。
int main(void) {if ((3 + 2 < 2) > (3 + 2 > 2))printf("Welcome to Xiyou Linux Group\n");elseprintf("%d\n", printf("Xiyou Linux Group - 2%d", printf("")));
}
先看if
语句,3 + 2 < 2
为假返回0
,3 + 2 > 2
为真返回1,0 > 1
为假,执行else
内的语句。
而else
语句中,printf()
的返回值为打印字符串的长度,因此最内层的printf("")
打印""
并返回其长度0
,中间层打印"Xiyou Linux Group - 20"
并返回其长度22
,外层打印"22\n"
,其结果为:
Xiyou Linux Group - 2022
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));
}
p0
是字符数组,以’\0’结尾,p1
是字符指针,指向常量区的一个字符串,p2
也是字符数组,但由于长度限制,没有结尾的标识符\0
。
直接引用数组变量名时,返回的是数组首位的地址,直接引用指针名时,返回的是指针指向的地址。由于p0
、p1
地址不同,因此p1 == p2
不为真,返回0
。strcmp()
为字符串比较函数,从传入的两个地址参数开始向后读取字符并比较,直到出现不同的字符或者\0
为止,返回正数、负数或0(相应字符的ASCII码之差有关),分别表示前者大、后者大、两者相等。当读到p0
的\0
时,此时p2
的下一个字符为内存中的随机值。因此打印的第一行为:
p0 == p1: 0, strcmp(p0, p2): -72
sizeof(p0)
是字符数组(含'\0'
以及未初始化的内容)的大小,sizeof(p1)
是指针所存地址的大小,sizeof(*p2)
是字符'H'
的大小。因此打印的第二行为:
sizeof(p0): 12, sizeof(p1): 8, sizeof(*p2): 1
strlen()
返回字符串的长度,从传入的地址参数开始读取,直到内存中的下一个'\0'
之前,返回读取的字符个数,
strlen(p0): 11, strlen(p1): 11
另外,由于p2
长度限制,数组内没有'\0'
作字符串结束的标识符,因此strlen(p2)
返回的是数组首位到内存随机内容的下一个'\0'
,其返回值应该很大,具体结果视内存而定。
3. 换个变量名不行吗?
请结合本题,分别谈谈你对C语言中「全局变量」和「局部变量」的「生命周期」理解。
int a = 3;
void test() {int a = 1;a += 1;{int a = a + 1;printf("a = %d\n", a);}printf("a = %d\n", a);
}
int main(void) {test();printf("a= %d\n", a);
}
打印第一个a的内容不符合预期,因为代码块作用域内的局部变量a在声明时遇到了Undefined behavior
,a的值视具体的编译器、系统、平台而定。
浅谈 C++ Undefined Behavior
4. 内存对不齐
union
与struct
各有什么特点呢,你了解他们的内存分配模式吗。
typedef union {long l;int i[5];char c;
} UNION;
typedef struct {int like;UNION coin;double collect;
} STRUCT;
int main(void) {printf("sizeof (UNION) = %zu\n", sizeof(UNION)); printf("sizeof (STRUCT) = %zu\n", sizeof(STRUCT));
}
sizeof (long) = 4
sizeof (int[5]) = 20
sizeof (char) = 1
union
中的所有数据成员共享同一个存储空间,其在内存中的大小为最大成员的大小,因此UNION
的大小应该为20
。
sizeof (int) = 4
sizeof (UNION) = 20
sizeof (double) = 8
struct
中的每个数据成员享有独立的存储空间,其在内存中的大小为所有成员的大小之和。因此STRUCT
的大小应该为32
。
真的是这样吗?
在计算机中,为了方便读取数据,内存中各数据的起始地址通常都是4或8的倍数。因此结果很可能如下:
sizeof (UNION) = 24
sizeof (STRUCT) = 40
C/C++内存对齐详解 - 知乎专栏
Data alignment: Straighten up and fly right - IBM Developer
// 若是4字节对齐
typedef union {long l; // 0 ~ 3int i[5]; // 0 ~ 19char c; // 0 ~ 7
} UNION; // 0 ~ 19 -> 20
typedef struct {int like; // 0 ~ 3UNION coin; // 4 ~ 23double collect; // 23 ~ 30
} STRUCT; // 0 ~ 31 -> 32
// 若是8字节对齐
typedef union {long l; // 0 ~ 3int i[5]; // 0 ~ 19char c; // 0 ~ 7
} UNION; // 0 ~ 23 -> 24
typedef struct {int like; // 0 ~ 3UNION coin; // 8 ~ 27double collect; // 32 ~ 39
} STRUCT; // 0 ~ 39 -> 40
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);
}
按步骤执行:
#include <stdio.h>
int main(void) {unsigned char a = 4 | 7;// a = 0000 0010 | 0000 0111 = 0000 0111 -> 7a <<= 3;// a = 0011 1000 = 28unsigned char b = 5 & 7;// b = 0000 0101 & 0000 0111 = 0000 0111 -> 7b >>= 3;// b = 0000 0000 = 0unsigned char c = 6 ^ 7;// c = 0000 0110 ^ 0000 0111 = 0000 0001 -> 1c = ~c;// c = 1111 1110 -> (unsigned) 254unsigned short d = (a ^ c) << 3;// d = (0011 1000 ^ 1111 1110) << 3// = 1100 0110 << 3// = 0000 0110 0011 0000 -> 1584signed char e = -63;// e = 1100 0001e <<= 2;// e = 0000 0100// 作为char类型打印d,只保留低八位// (char)d = 0011 0000 -> 48printf("a: %d, b: %d, c: %d, d: %d\n", a, b, c, (char)d);// 56, 0, 254, 48printf("e: %#x\n", e);// 0x4return 0;
}
6. 英译汉
请说说下面数据类型的含义,谈谈
const
的作用。
char *const p
。char const *p
。const char *p
。
char *const p
指*p
是个常量型字符指针,其中所存的地址不能改变。char const *p
指字符指针*p
指向的字符是个常量,所指字符的内容不能改变,但可以改变*p
指向的地址。const char *p
指字符指针*p
指向的字符是个常量,所指字符的内容不能改变,但可以改变*p
指向的地址。
7. 汉译英
请用变量
p
给出下面的定义:
- 含有10个指向
int
的指针的数组。- 指向含有10个
int
数组的指针。- 含有3个「指向函数的指针」的数组,被指向的函数有1个
int
参数并返回int
。
int *p[10];
int arr[10];
int *p = (int*)arr;
int (*p[3])(int arg);
8. 混乱中建立秩序
你对排序算法了解多少呢?
请谈谈你所了解的排序算法的思想、稳定性、时间复杂度、空间复杂度。提示:动动你的小手敲出来更好哦~
- 冒泡排序
void bubbleSort(int arr[], int len)
{int i, j, tmp;for (i = 0; i < len - 1; i++)for (j = 0; j < len - 1 - i; j++)if (arr[j] > arr[j + 1]){tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;}
}
- 选择排序
void selectionSort(int arr[], int len)
{int i, j;for (i = 0; i < len - 1; i++){int min = i;for (j = i + 1; j < len; j++)if (arr[j] < arr[min])min = j;int tmp = arr[min];arr[min] = &arr[i];&arr[i] = tmp;}
}
https://github.com/hustcc/JS-Sorting-Algorithm
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>
char *convertAndMerge(char strs[2][20])
{char *result = (char *)malloc(sizeof(char) * 40);strcpy(result, strs[0]);strcat(result, strs[1]);int len = strlen(strs[0]) + strlen(strs[1]);for (int i = 0; i < len; i++){if (result[i] >= 'A' && result[i] <= 'Z')result[i] += 32;else if (result[i] >= 'a' && result[i] <= 'z')result[i] -= 32;}return result;
}
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);
}
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]);}}
}
运行后,程序的输出为:
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
程序在运行时,a
是稳定自增的值,输出为此结果的原因是程序在每次循环时,将a
的值从arr[i]
的起始(*temp = *(arr + i)
)赋值到数组的结尾。比如在第二次循环时,a
从25
自增到44
的序列会从arr[1][0]
赋值到arr[4][4]
,覆盖了第一次循环后原先位置的5
到24
。
如果将循环条件中的temp < arr[5]
,改为temp < arr[i+1]
,程序在每次循环时就会只对arr[i]
内的元素进行操作,如下。
#include <stdio.h>
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[i+1]; temp++) *temp = a++;}for (int i = 0; i < 5; i++) {for (int j = 0; j < 5; j++) {printf("%d\t", arr[i][j]);}}
}
0 1 2 3 4
5 6 7 8 9
10 11 12 13 14
15 16 17 18 19
20 21 22 23 24
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
指argument count,即参数计数器,argv
指argument vector,即参数数组。程序在运行时传入的第一个参数就是程序的启动路径/文件名,因此argc
最小为1
。在循环中,整型argc
会自增到溢出,然后打印argv[0]
即程序路径。
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);
}
程序的输出为:
Welcome to Xiyou Linux Group 2022
文本在变量中的存储对应关系如下:
// c l e W e m o X o t
int data1[2][3] = {{0x636c6557, 0x20656d6f, 0x58206f74},// u o y i n i L \0{0x756f7969, 0x6e694c20, 0x00000000}};
// G x u p u o r 2 0 2 2
int data2[] = {0x47207875, 0x70756f72, 0x32303220, 0x00000a32};
// unsigned char buf[33] = {// 0x57, 0x65, 0x6C, 0x63, 0x6F, 0x6D, 0x65, 0x20, 0x74, 0x6F, 0x20, 0x58,
// 0x69, 0x79, 0x6F, 0x75, 0x20, 0x4C, 0x69, 0x6E, 0x75, 0x78, 0x20, 0x47,
// 0x72, 0x6F, 0x75, 0x70, 0x20, 0x32, 0x30, 0x32, 0x32};
程序运行时,会把data[1]
和data[2]
中的字符串拼接起来,然后输出。
字符之所以“反向存储”,是因为在计算机中,为了方便某些设备的读取,采用了“小端序”(Little-endian)的存储方式:低位字节排放在内存的低地址端即该值的起始地址,高位字节排放在内存的高地址端。
与此相对的自然是“大端序”(Big-endian),常见于文件存储、网络传输中。
13. 小试宏刀
- 请谈谈你对
#define
的理解。- 请尝试着解释程序的输出。
#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\n", x, y, tmp);printf("z = %d, w = %d, tmp = %d\n", z, w, tmp);
}
x = 2, y = 1, tmp = 1
x = 1, y = 2, tmp = 2
x = 2, y = 2
z = 5, w = 5, tmp = 2
宏定义只是实现了简单的文本替换,不会自动为表达式补充圆括号或者为代码块补充花括号。因此,宏定义替换后等效于如下代码:
#include <stdio.h>
int main()
{int tmp;int x = 1;int y = 2;int z = 3;int w = 3;// SWAP(x, y, tmp);tmp = x;x = y;y = tmp;printf("x = %d, y = %d, tmp = %d\n", x, y, tmp);// x = 2, y = 1, tmp = 1// if (x > y) SWAP(x, y, tmp);if (x < y)tmp = x;// 无论如何以下两行都执行x = y;y = tmp;printf("x = %d, y = %d, tmp = %d\n", x, y, tmp);// x = 1, y = 2, tmp = 2// SWAPWHEN(x, y, tmp, SQUARE(1 + 2 + z++ + ++w) == 100);// 宏替换并不会为SQUARE()函数的参数加括号if (1 + 2 + z++ + ++w * 1 + 2 + z++ + ++w == 100)tmp = x;x = y;y = tmp;printf("x = %d, y = %d", x, y, tmp);// x = 2, y = 2printf("z = %d, w = %d, tmp = %d\n", z, w, tmp);// z = 5, w = 5 ,tmp = 2
}
如此修改,便可令程序按预期执行。
#include <stdio.h>
#include <math.h>
#define SWAP(a, b, t) { t = a; a = b; b = t; }
#define SQUARE(a) pow(a, 2)
#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);SWAPWHEN(x, y, tmp, SQUARE(1 + 2 + z++ + ++w) == 100);printf("x = %d, y = %d\n", x, y, tmp);printf("z = %d, w = %d ,tmp = %d\n", z, w, tmp);
}
14. GNU/Linux命令 (选做)
你知道以下命令的含义和用法吗:
注:
嘿!你或许对Linux命令不是很熟悉,甚至你没听说过Linux。
但别担心,这是选做题,不会对你的面试产生很大的影响!
了解Linux是加分项,但不了解也不扣分哦!
ls
rm
whoami
请问你还了解哪些GNU/Linux的命令呢。
ls
即list directory contents
,列出目前工作目录所含之文件及子目录(显示指定工作目录下之内容)。rm
即remove
,用于删除一个文件或者目录,慎用。whoami
用来打印当前执行操作的用户名,与此相似的who am i
则用来打印登陆当前 Linux 系统的用户名。实用的文件工具有
mv
、touch
、mkdir
、cp
、chmod
等。实用的网络工具有
nc
、netstat
、ping
等。
恭喜你做到这里!你的坚持战胜了绝大多数看到这份试题的同学。
或许你自己对答题的表现不满意,但别担心,请自信一点呐。
坚持到达这里已经证明了你的优秀。
还在等什么,快带上你的笔记本电脑,来FZ103面试吧!
西邮Linux兴趣小组2022纳新面试题题解相关推荐
- 西邮Linux兴趣小组2021纳新面试题题解
注: 本题目仅作西邮Linux兴趣小组2021纳新面试题的有限参考. 为节省版面本试题的程序源码中省略了#include指令. 本试题中的程序源码仅用于考察C语言基础,不应当作为C语言代码风格的范例. ...
- 西邮Linux兴趣小组2021纳新面试题
#include<stdio.h> #include<string.h> int main(void) {char s[]="I love Linux\0\0\0&q ...
- 西邮linux兴趣小组网络,西邮Linux兴趣小组2012纳新笔试题
这是我们西邮Linux兴趣小组2012的纳新笔试题,对于大一的学生,出得有难度哦,个人感觉比腾讯实习生的笔试题出的有水平. 西邮Linux兴趣小组纳新试题 姓名: ...
- 西邮Linux兴趣小组2017纳新免试题揭秘
声明 今年的免试题按照关卡顺序依次是由小组15级成员何攀.楚东方.宫展京.杜肖孟.王一妃同学精心准备的(鼓掌),每个人总结了一下自己负责关卡的解法,我这里整理了一下,给出一套完整的免试题详解,免试题通 ...
- [c语言]西邮Linux兴趣小组2020纳新面试题
一.运行下面的代码,输出结果是什么,解释原因. int i; int main(int argc, char *argv[]) {i--;if (i > sizeof(i)){printf(&q ...
- 西邮linux兴趣小组2014纳新免试题(四)
[第四关] 题目 http://findakey.sinaapp.com/ Example:String1:FFFF8 5080D D0807 9CBFC E4A04 24BC6 6C840 49B5 ...
- 西邮Linux兴趣小组2021纳新试题
1.大小和长度竟然不是一个意思, sizeof()和strlen()有什么异同之处? 他们对于不同参数的结果有什么不同?请试举例子说明. int main(void) { char s[] = &qu ...
- 西邮Linux兴趣小组2020纳新试题题解
西邮Linux兴趣小组2020纳新试题题解 1. 请试着解释其输出. int main(int argc , char *argv[]) {unsigned char a = 255;char ch ...
- 西邮Linux兴趣小组2021纳新试题②
1. 结果:127 -128 signed char的范围是-128~127 unsigned char的范围是0~256 计算a-ch是把ch转换成整数,而输出ch是进行了类型转换为char类型,1 ...
最新文章
- postgresql存储过程输出参数
- Python中数字以及算数运算符的相关使用
- 618 技术特辑(一)不知不觉超预算3倍,你为何买买买停不下来?
- java对象json序列化时忽略值为null的属性
- linux C 语言的 system
- docker 安装最新mysql
- Anatomy of a Program in Memory
- 深度学习在58同城首页推荐中的应用
- 这8款黑科技APP,满足你的所有需求,你想不到的照样帮你实现!
- 质量数据分析工具软件的应用
- 微信小程序测试注意事项
- 3dmax联机分布式渲染方法技巧详解
- ppm/℃是什么单位?什么意思?
- 项目管理、Bug管理软件工具:禅道,BugFree,Redmine
- 一文搞懂MySQL体系架构!!
- Charles使用及常用功能介绍
- 安卓手机屏幕投射电脑 手机投屏到win7
- A Noise-Robust Framework for Automatic Segmentation of COVID-19 Pneumonia Lesions From CT Images论文笔记
- 免费送!!!CSDN 会员月卡!
- 打通版4.3 思维导图