2022InterviewSolution

西邮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. 内存对不齐

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

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);
}
0000001000000011000001100011100011000001
00000111000001110000011111111110e<<=2
a = 4 |7=00000111b = 5 & 7=00000011c = 6 ^ 7=00000001a^c=1100011000000100
a<<=3b >>= 3c=~cd = (a ^ c) << 3
00111000000000001111111000110000

%#x:输出时加上进制0x。

6. 英译汉

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

  1. char *const p。限定指针的地址不可改变。
  2. char const *p。限定指针指向的内容不可改变。
  3. const char *p。同二

const:限定一个变量不允许被改变,产生静态作用。

7. 汉译英

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

  1. 含有10个指向int的指针的数组。
  2. 指向含有10个int数组的指针。
  3. 含有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的命令呢。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值