西邮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
对应于十进制的多少位呢?
十进制数取对数后再加1,就是其位数。
lg 2^10 + 1 = 10 lg 2 + 1 = 4
lg 2^10000 + 1 = 10000 lg 2 + 1 = 3011
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("")));
}
输出为:
Xiyou Linux Group - 2022
考了printf嵌套的知识,如printf(“%d\n”, printf(“Xiyou Linux Group - 2%d”, printf(“”))),要从最里面开始运行,且执行完一个printf函数后,它的返回值为“ ”内写入的字符总数,如printf(“”)的返回值就是0,以此类推,最终输出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 == p1: 0, strcmp(p0, p2): -72
sizeof(p0): 12, sizeof(p1): 8, sizeof(*p2): 1
strlen(p0): 11, strlen(p1): 11
p0是一个字符数组,以\0做结尾,而p1是一个字符指针,指向常量区的字符,p2也是一个字符数组,但长度被限制为11,所以没有了结尾的\0。
p0是该数组首元素地址,p1是该指针指向的地址,所以不相等。用strcmp函数比较大小,当读到p0的\0时,p2为随机值,刚好为72大于0,所以输出-72表示p0小于p2。
sizeof()是一个静态运算符,意义是计算变量所占内存大小,单位是字节,由于它是静态运算符,所以在其中做的运算没有用,不会生效。strlen()是计算字符串长度的函数,被包含在<string.h>库中,所以使用时头文件一定要有<string.h>。
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 = 32520
a = 2
a = 3
打印的第一个a是在test函数其中的一个作用域中定义的,但没有对a初始化,所以打印的是一个随机值。打印的第二个a是在test函数中定义的,所以是2。打印的第三个a是在全局范围内定义的a,因为在main函数里,没有定义局部变量a,所以是3。
在不同情况下定义的变量有不同的生命周期:在全局范围内定义的全局变量的生命周期是整个程序;在某个函数或某个作用域内定义的局部变量的生命周期是这个函数或这个作用域名;加了static前缀的局部变量的生命周期也会被延长为整个程序。
4. 内存对不齐
union
与struct
各有什么特点呢,你了解他们的内存分配模式吗。
//对齐系数是8
typedef union {
long l;//8字节
int i[5];//20字节
char c;//1字节
} UNION;//所以是24字节
typedef struct {
int like;//4字节
UNION coin;//24字节
double collect;//4字节
} STRUCT;//所以是40字节
int main(void) {
printf("sizeof (UNION) = %zu\n", sizeof(UNION));
printf("sizeof (STRUCT) = %zu\n", sizeof(STRUCT));
}
输出为:
sizeof (UNION) = 24
sizeof (STRUCT) = 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);
}
输出为:
a: 56, b: 0, c: 254, d: 48
e: 0x4
a=00000100|00000111=00000111=7
a<<3=00111000=56
b=00000101&00000111=00000101=5
b>>3=00000000=0
c=00000110^00000111=00000001=1
c=~c=11111110=254
d=(00000111^11111110)<<3=0000011000110000=1584
e=11000001
e<<2=00000100=4
d被强制类型转换为char类型只保留低八位,也就是后八位。
6. 英译汉
请说说下面数据类型的含义,谈谈
const
的作用。
char *const p
。char const *p
。const char *p
。
const int和int const相同,都表示变量的值不能被改变。
const int *和int const *也是相同的,都表示不能通过指针去改变变量的值。
如果const在前面,即const int * n或int const * n,说明是常量指针,如果const在后面,即int * const n,说明是指针常量。常量指针是指不能通过这个指针改变变量的值,即不能用 *指针=xxx来改变变量的值,但并不是说指针本身不能改变,常量指针可以指向其他的变量。指针常量是指指针本身是个常量,不能再指向其他的变量,但可以通过这个指针去改变变量的值,即可以用 *指针=xxx来改变变量的值。常量指针和指针常量正好是相反的。
7. 汉译英
请用变量
p
给出下面的定义:
- 含有10个指向
int
的指针的数组。- 指向含有10个
int
数组的指针。- 含有3个「指向函数的指针」的数组,被指向的函数有1个
int
参数并返回int
。
int *p[10]
int (*p)[10]
int(*p[3])(int a)
8. 混乱中建立秩序
你对排序算法了解多少呢? 请谈谈你所了解的排序算法的思想、稳定性、时间复杂度、空间复杂度。
提示:动动你的小手敲出来更好哦~
冒泡排序将相邻的两个数分别比较,一轮完毕后再比较一轮,直到排序完成:
#include<stdio.h>
void Bubble_sort(int arr[], int size)
{
int j,i,tem;
for (i = 0; i < size-1;i ++)//size-1是因为不用与自己比较,所以比的数就少一个
{
int count = 0;
for (j = 0; j < size-1 - i; j++) //size-1-i是因为每一趟就会少一个数比较
{
if (arr[j] > arr[j+1])//这是升序排法,前一个数和后一个数比较,如果前数大则与后一个数换位置
{
tem = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tem;
count = 1;
}
}
if (count == 0) //如果某一趟没有交换位置,则说明已经排好序,直接退出循环
break;
}
}
int main()
{
int arr[10];
int i;
printf("请输入10个数\n");
for (i = 0; i < 10; i++) //接收用户的数值
{
scanf("%d", &arr[i]);
}
printf("排序前的数组>");
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
printf("\n排序后的数组>");
Bubble_sort(arr, 10);
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
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);
}
char* convertAndMerge(char a[2][20]);
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);
}
char* convertAndMerge(char a[2][20])
{
char* s;//储存新字符串的新数组
strcpy(s,a[0]);//复制字符串第一行
strcat(s,a[1]);//拼接字符串第二行
for(int i=0;i<40;i++)//反转大小写
{
if(s[i]>='a'&&s[i]<='z')
{
s[i]-=32;
}
else if(s[i]>='A'&&s[i]<='Z')
{
s[i]+=32;
}
}
return s;
}
输出为:
Welcome to Xiyou
Linux Group 2022
str = wELCOME TO xIYOU lINUX gROUP 2022
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
先定义了一个temp数组,第一次循环中,使temp数组等于二维数组arr的第一行,然后使第一行的五列分别等于a的自增,0,1,2,3,4,但是这次a自增了25次,后面的二十次都浪费了,所以arr数组的第二行分别等于25,26,27,28,29,这次a自增了20次,所以第三行等于45,46,47,48,49……以此类推,最终的输出就是这样。
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 = 1
./a.out
其实main函数是有参数的,int main(int argc,char* argv[],char* envp[])才是main函数的完整形式。第一个参数argc指的的是命令行参数的个数,它至少为一,因为第一个参数始终是程序的名称第二个参数argv是一个二维的char型指针,存放命令行参数字符串,第三个参数envp也是一个二维的char型指针,存放环境变量。
在循环中,整型argc会自增到溢出,等待几秒后,打印程序路径。
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
该题先定义并初始化两个int型数组,又将其强制类型转换为字符数组并一起复制到一个新的字符数组里,又因为x86结构是小端模式,所以低位存在低地址处,先从低地址开始打印,所以对照ascii码表,就会打印Welcome to Xiyou Linux Group 2022。
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
使用#define可以用来定义标识符,标识符可以简单理解为一个能够表示一定内容的符号。
因为宏替换只是简单的替换,所以需要我们加上括号来保证不出现歧义,比如加上花括号表示作用域,加上圆括号保证运算顺序。
14. GNU/Linux命令 (选做)
你知道以下命令的含义和用法吗:
注: 嘿!你或许对Linux命令不是很熟悉,甚至你没听说过Linux。 但别担心,这是选做题,不会对你的面试产生很大的影响!
了解Linux是加分项,但不了解也不扣分哦!
ls
rm
whoami
请问你还了解哪些GNU/Linux的命令呢。
恭喜你做到这里!你的坚持战胜了绝大多数看到这份试题的同学。
或许你自己对答题的表现不满意,但别担心,请自信一点呐。
坚持到达这里已经证明了你的优秀。
还在等什么,快带上你的笔记本电脑,来FZ103面试吧!