西邮Linux兴趣小组2021纳新面试题题解

西邮Linux兴趣小组2021纳新面试题题解

注:

  • 本题目仅作 西邮Linux兴趣小组 2021 纳新面试题的有限参考。
  • 为节省版面本试题的程序源码中省略了 #include 指令。
  • 本试题中的程序源码仅用于考察C语言基础,不应当作为C语言代码风格的范例。
  • 题目难度与序号无关。
  • 所有题目均假设编译并运行 x86_64 GNU/Linux 环境。

Copyright © 2021 西邮Linux兴趣小组, All Rights Reserved.
本试题使用采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。

1. 大小和长度竟然不是一个意思

sizeof()strlen() 有什么异同之处?

他们对于不同参数的结果有什么不同?请试举例子说明。

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

asizeof(s),即字符数组 ['I', ' ', 'l', 'o', 'v', 'e', ' ', 'L', 'i', 'n', 'u', 'x', '\0', '\0', '\0'] 的大小;bstrlen(s),即字符数组 "I love Linux" 的长度。

另外,如果声明 char s[20] = "I love Linux\0\0\0";,则 a = sizeof(s); 的值变为20

2. 箱子的大小和装入物品的顺序有关

test1test2 都含有:1个 short、1个 int、1个 double,那么 sizeof(t1)sizeof(t2) 是否相等呢?这是为什么呢?

解读:

c
struct test1 {
    int a;          // 0 ~ 3
    short b;        // 4 ~ 5
    double c;       // 8 ~ 15
} t1;               // 0 ~ 15 -> 16
struct test2 {
    short b;        // 0 ~ 1
    int a;          // 4 ~ 7
    double c;       // 8 ~ 15
} t2;               // 0 ~ 15 -> 16

3. 哦,又是函数

想必在高数老师的教导下大家十分熟悉函数这个概念。那么你了解计算机程序设计中的函数吗?请编写一个 func 函数,用来输出二维数组 arr 中每个元素的值。

c
/*在这里补全func函数的定义*/
int main(void) {
    int arr[10][13];
    for (int i = 0; i < 10; i++) {
        for (int j = 0; j < 13; j++) {
            arr[i][j] = rand();
        }
    }
    func(arr);
}

补全后:

4.就不能换个变量名吗?

  • 请结合下面的程序,简要谈谈 传值传址 的区别。
  • 简要谈谈你对C语言中变量的生命周期的认识。
  • 传值 后,修改形参的值,实参的值不会改变。
  • 传址 后,修改形参的值,实参的值会改变。

解读:

5. 套娃真好玩!

请说明下面的程序是如何完成求和的?

c
unsigned sum(unsigned n) { return n ? sum(n - 1) + n : 0; }
int main(void) { printf("%u\n", sum(100)); }

通过递归完成求和,每次递归返回的为其先前的数之和。相当于 栈(Stack),只有其前的函数的表达式返回值(出栈),新的栈顶函数表达式才能返回值。

特别地,当栈顶元素为 sum(0) 时,会返回 0 以实现从 0n 的求和。

6. 算不对的算术

c
void func(void) {
    short a = -2;
    unsigned int b = 1;
    b += a;
    int c = -1;
    unsigned short d = c * 256;
    c <<= 4;
    int e = 2;
    e = ~e | 6;
    d = (d & 0xff) + 0x2022;
    printf("a=0x%hx\tb=0x%x\td=0x%hx\te=0x%x\n", a, b, d, e);
    printf("c=Ox%hhx\t\n", (signed char)c);
}

7. 指针和数组的恩怨情仇

c
int main(void) {
    int a[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
    int(*b)[3] = a;
    ++b;
    b[1][1] = 10;
    int *ptr = (int *)(&a + 1);
    printf("%d %d %d \n", a[2][1], **(a + 1), *(ptr - 1));

}
c
int main(void) {
    int a[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
    int(*b)[3] = a;
    ++b;
    b[1][1] = 10;
    int *ptr = (int *)(&a + 1);
    // *ptr = *(&a + sizeof(a)) = b
    // **(a + 1) = **(*(&a[0] + sizeof(a[0]))) = a[1][0]
    // *(ptr - 1) = *(&a + sizeof(a) - sizeof(*ptr)) = a[3][3]
    printf("%d %d %d \n", a[2][1], **(a + 1), *(ptr - 1));

}

8. 移形换位之术

下面有 abc 三个变量和4个相似的函数。

  • 你能说出使用这三个变量的值或地址作为参数分别调用这5个函数,在语法上是否正确吗?
  • 请找出下面的代码中的错误。
  • const intint const 是否有区别?如果有区别,请谈谈他们的区别。
  • const int *int const * 是否有区别?如果有区别,请谈谈他们的区别。

9. 听说翻转字母大小写不影响英文的阅读?

请编写 convert 函数用来将作为参数的字符串中的大写字母转换为小写字母,将小写字母转换为大写字母。返回转换完成得到的新字符串。

c
char *convert(const char *s);
int main(void) {
    char *str = "XiyouLinux Group 2022";
    char *temp = convert(str);
    puts(temp);
}

10. 交换礼物的方式

  • 请判断下面的三种 Swap 的正误,分别分析他们的优缺点。
  • 你知道这里的 do {...} while(0) 的作用吗?
  • 你还有其他的方式实现 Swap 功能吗?
  • Swap1()Swap2() 通过宏定义实现两数交换,do {...} while(0) 可以用代码块花括号 {...} 替代,目的是防止宏替换后语句不在同一代码块内。
  • Swap3() 错误,交换形参的值并不能改变实参。
  • Swap4() 利用指针来实现。
c
void Swap4(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

11. 据说有个东西叫参数

你知道 argcargv 的含义吗?请解释下面的程序。你能在不使用 argc 的前提下,完成对 argv 的遍历吗?

c
int main(int argc, char *argv[]) {
    printf("argc = %d\n", argc);
    for (int i = 0; i < argc; i++)
        printf("%s\n", argv[i]);
}

argc 指 argument count,即参数计数器,argv 指 argument vector,即参数数组。程序在运行时传入的第一个参数就是程序的启动路径/文件名,因此 argc 最小为 1。在循环中,整型 argc 会自增到溢出,然后打印 argv[0] 即程序路径。

不使用 argc 遍历 argv 的方法:

c
#include <stdio.h>
int main() {
    _CRTIMP extern int __argc;
    _CRTIMP extern char **__argv;
    printf("argc = %d\n", __argc);
    for (int i = 0; i < __argc; i++)
        printf("%s\n", __argv[i]);
}

12. 人去楼空

这段代码有是否存在错误?谈一谈静态变量与其他变量的异同。

*func3() 返回的是局部变量,在外部赋值时此局部变量已被抛弃,成为了野指针。

13. 奇怪的输出

c
int main(void) {
    int data[] = {0x636c6557, 0x20656d6f, 0x78206f74,
                  0x756f7969, 0x6e694c20, 0x67207875,
                  0x70756f72, 0x32303220, 0x00000a31};
    puts((const char*)data);
}
c
#include <stdio.h>
int main(void) {
    //               c l e W       e m o     x   o t
    int data[] = {0x636c6557, 0x20656d6f, 0x78206f74,
                  // u o y i     n i L       g   x u
                  0x756f7969, 0x6e694c20, 0x67207875,
                  // p u o r     2 0 2          \0 1
                  0x70756f72, 0x32303220, 0x00000a31};
    // unsigned char data[34] = {
    //     0x57, 0x65, 0x6C, 0x63, 0x6F, 0x6D, 0x65, 0x20, 0x74, 0x6F, 0x20, 0x78,
    //     0x69, 0x79, 0x6F, 0x75, 0x20, 0x4C, 0x69, 0x6E, 0x75, 0x78, 0x20, 0x67,
    //     0x72, 0x6F, 0x75, 0x70, 0x20, 0x32, 0x30, 0x32, 0x31, 0x00};
    // }
    puts((const char *)data);
}

14. 请谈谈对从「C语言文件到可执行文件」的过程的理解

  • 编辑:创建和修改C程序的源代码
  • 编译:将源代码转换为机器语言
  • 链接:链接器将源代码由编译器产生的各种模块组合起来,再从C语言提供的程序库中添加必要的代码模块,将它们组成一个可执行的文件
  • 执行:运行程序

15. (选做) 堆和栈

你了解程序中的栈和堆吗?它们在使用上有什么区别呢?请简要说明。

16. (选做) 多文件

一个程序在不使用任何头文件的情况下,如何使用另一个文件中的函数。

17. (选做) GNU/Linux 与文件

  • 你知道如何在 GNU/Linux 下如何使用命令行创建文件与文件夹吗?
  • 你知道 GNU/Linux 下的命令 ls 的每一列的含义吗?
  • 你知道 GNU/Linux 下文件的访问时间、修改时间、创建时间如何查看吗?并简单说说他们的区别。

恭喜你做完了整套面试题,快来参加西邮 Linux 兴趣小组的面试吧!

西邮 Linux 兴趣小组面试时间:
2021年10月25日至2021年10月31日晚8点。
听说面试来的早一点更能获得学长学姐的好感哦。

我们在 FZ103 等你!

西邮Linux兴趣小组2020纳新面试题题解

西邮Linux兴趣小组2022纳新面试题题解

评论区

评论加载中...