西邮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对应于十进制的多少位呢?

对2^10 取log10,得3,再加1,得4,对2^10000取lg10,得3010,加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("")));
}

3+2<2为假,3+2>2为真,则if语句判断为假,执行else语句,printf函数嵌套,先执行括号里的,因而先什么也不打印,printf函数返回0,中间的printf函数打印Xiyou Llinux Group -2,并且占位符%d被替换为0;最外层的printf函数打印中间函数的返回值22,所以最后输出为Xiyou Llinux 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函数接受两地址,比较字符串本身。p2字符串长度只有11,所以没有存储\0,p0的\0与下一个地址上的某字符比较得到-72
sizeof运算符计算占空间字节数,int main(void) {
char s[] = “I love Linux\0\0\0”;
int a = sizeof(s);
int b = strlen(s);
printf(“%d %d\n”, a, b),p0数组占空间为12字节,p1指针占地址为8个字节,*p2是p2数组第一个元素的大小,为1个字节。
strlen函数计算字符串长度且不计\0,所以都为11

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=a+1,所以打印的第一个a的值未被定义。打印的第二个a的值的生命周期在test函数中,所以为2,打印的第三个a是全局变量,为3

4. 内存对不齐

unionstruct各有什么特点呢,你了解他们的内存分配模式吗。

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));
}

考察结构体和联合体的在内存空间的存在方式和内存对齐
结构体的成员可以在内存空间中同时存在,联合体的成员只能同时在内存空间中存在一个。计算机存在内存对齐,而默认对齐数为8,且联合体占空间为成员整数倍该联合体占内存空间为24个字节。结构体中最大成员union占内存为24,所以对齐数为8,所以该结构体占内存为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
,需要注意的是d被强制类型转换为char类型,所以被截断,只有后面八位。

6. 英译汉

请说说下面数据类型的含义,谈谈const的作用。

  1. char *const p
  2. char const *p
  3. const char *p

第一个是const指针,即该指针只能指向该地址,第二个和第三个都是指向const的指针,即无法通过该指针改变所指地址的值

7. 汉译英

请用变量p给出下面的定义:

  1. 含有10个指向int的指针的数组。
  2. 指向含有10个int数组的指针。
  3. 含有3个「指向函数的指针」的数组,被指向的函数有1个int参数并返回int

1.int *p[10]
2.int (*p)[10]
3.int (*p[3])(int)

8. 混乱中建立秩序

你对排序算法了解多少呢?
请谈谈你所了解的排序算法的思想、稳定性、时间复杂度、空间复杂度。

提示:动动你的小手敲出来更好哦~
冒泡排序

void bubblesort(int* arr, int n)
{
	int flag = 0;
	for (int i = 0; i < n-1; i++)
	{
		for (int j = 0; j < n - 1-i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int temp;
				temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
				flag = 1;
			}
		}
		if (flag == 0)break;
	}
}

选择排序

void selectionsort(int* arr, int n)
{
	int min = arr[0];
	int flag = 0;
	for (int i = 0; i < n - 1; i++)
	{
		for (int j = i + 1; j < n ; j++)
		{
			if (arr[j] < arr[min])
			{
				min = j;
				flag = 1;
			}
			int temp;
			if (i != min) {
				temp = arr[j];
				arr[j] = arr[min];
				arr[min] = temp;
			}
		}
		if (flag == 0)break;
	}
}

插入排序

void insertionsort(int *arr,int n)
{
    int temp;
    for(int i = 0;i <= n;i++)
    {
        int j = i - 1;
        temp = arr[i];
        while(arr[j] > temp && j >= 0)
        {
            arr[j+1] = arr[j];
            j--;
        }
        arr[j+1] = temp;
    }
 
}

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 *arr = (char *)malloc(sizeof(char) * 40);
    strcpy(arr, strs[0]);
    strcat(arr, strs[1]);
    int len = strlen(strs[0]) + strlen(strs[1]);
    for (int i = 0; i < len; i++)
    {
        if (arr[i] >= 'A' && arr[i] <= 'Z')
            arr[i] += 32;
        else if (arr[i] >= 'a' && arr[i] <= 'z')
            arr[i] -= 32;
    }
    return arr;
}
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

程序通过temp指针给二维数组赋值,for循环中temp<arr[5],使得第一次赋值时给整个数组赋值,从a[0][0]到a[4][4],而第二次a[1][0]到a[4][4],所以前面的一些赋值会被后面的数值覆盖,所以数值不连续

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是参数个数,argv是参数数组,argc的最小值为1,此时的argv是程序的启动路径/名称。程序不会死循环,argc的值会溢出,然后打印出argv的值。

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
考察大小端
大端(存储)模式:是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中;

小端(存储)模式:是指数据的低位保存在内存的低地址中,而数据的高位,,保存在内存的高地址中。

x86系统采用小端的模式,所以在int类型被转化为char类型时,从后面截断。比如data[0][0],依次被截断成57,65,6c,63。参照ASCII码表,得出结果正是输出值。

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);
}

宏定义
宏定义是预处理命令的一种,在C语言源程序用一个标识符来表示一个字符串。
宏定义在被预处理时是直接替换,所以一定要注意括号的使用。

题目中宏函数中的if(x>y)这条语句后面的swap被替换后,只有第一条语句是if条件语句里的,因为没有把三条语句用花括号括起来。最后的结果是
x = 2, y = 1, tmp = 1
x = 1, y = 2, tmp = 2
x = 2, y = 2
z = 5, w = 5, tmp = 2

恭喜你做到这里!你的坚持战胜了绝大多数看到这份试题的同学。
或许你自己对答题的表现不满意,但别担心,请自信一点呐。
坚持到达这里已经证明了你的优秀。
还在等什么,快带上你的笔记本电脑,来FZ103面试吧!

  • 24
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

谁的友人A

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值