2021InterviewSolution

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

感谢 Zhilu 重新录入题目原件。好人一生平安。

注:

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

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

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

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

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

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

题解:

结果:16 12

sizeof是运算符,计算的是你使用的操作数所占的空间字节大小,包括’\0’。
而strlen是函数,计算的是字符串的长度,遇到’\0’就停止不包括’\0’。

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

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

struct test1 {
  int a;
  short b;
  double c;
};
struct test2 {
  short b;
  int a;
  double c;
};
int main(void) {
  struct test1 t1;
  struct test2 t2;
  printf("sizeof (t1) : %d\n", sizeof(t1));
  printf("sizeof(t2): %d\n", sizeof(t2));
}

题解:

结果:16 16
结构体内存对齐详细见:
https://blog.csdn.net/weixin_44343938/article/details/126932999?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166890900816800182183666%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=166890900816800182183666&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-1-126932999-null-null.142v65opensearch_v2,201v3add_ask,213v2t3_esquery_v1&utm_term=%E7%BB%93%E6%9E%84%E4%BD%93%E5%86%85%E5%AD%98%E5%AF%B9%E9%BD%90%E8%A7%84%E5%88%99&spm=1018.2226.3001.4187

3. 哦,又是函数

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

/*在这里补全func函数的定义*/
void func(int (&arr)[10][13])
{
    for (int i = 0; i < 10; i++)
    {
        for (int j = 0; j < 13; j++)
        {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }
}

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语言中变量的生命周期的认识。
int ver = 123;
void func1(int ver) {
  ver++;
  printf("ver = %d\n", ver);
}
void func2(int *pr) {
  *pr = 1234;
  printf("*pr = %d\n", *pr);
  pr = 5678;
  printf("ver = %d\n", ver);
}
int main() {
  int a = 0;
  int ver = 1025;
  for (int a = 3; a < 4; a++) {
    static int a = 5;
    printf("a = %d\n", a);
    a = ver;
    func1(ver);
    int ver = 7;
    printf("ver = %d\n", ver);
    func2(&ver);
  }
  printf("a = %d\tver = %d\n", a, ver);
}

题解:

结果:
a = 5
ver = 1026
ver = 7
*pr = 1234
ver = 123
a = 4 ver = 1025

传值和传址:

传值:把A的数值传到B,改变B,A不会跟着变,B存的是跟A一样的值;

传址:把A的地址传到B,改变B,A同时跟着变,B存的只是A的地址(类似电脑的快捷方式)。

传址,相当于是把A的地址传给B,所以说改变B的时候,相当于改变A,所以说B变的时候A也跟着变。

【效率比较】

一般而言,传址比传值效率高。因为传值对整个类型进行了拷贝,而传址只复制了地址。比如,现有一个结构体,存储了大量的数据。传值拷贝了整个结构体,而传址只是拷贝了地址。

5. 套娃真好玩!

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

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

题解:

结果:5050

条件运算符 表达式1?表达式2:表达式3 如果表达式1的值为真,则求解表达式2,表达式为假,则求解表达式3

函数的递归思想,读者如有不懂,cv调试一下

6. 算不对的算术

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

题解:

结果:
a=0xfffe b=0xffffffff d=0x2022 e=0xffffffff
c=Oxf0

依旧是运算符优先级:优先级:取反>左移>按位与>按位异或>按位或

%x代表以16进制的 输出32位

%hx 代表以16进制的 输出16位

%hhx 代表以16进制的 输出8位

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

#include<stdio.h>
int main()
{
    int a[3][3] = { {1,2,3},{4,5,6},{7,8,9} };
    int(*b)[3] = a;//指向数组的指针
    ++b;//指向a首地址后一个元素
    b[1][1] = 10;//a[2][1]=10
    int* ptr = (int*)(&a + 1);//&a指向a数组开头,+1后指向下一个数组, 所以*(ptr - 1)=a[2][2]
    printf("%d %d %d\n", a[2][1], **(a + 1), *(ptr - 1));//**(a+1)=a[1][1]
    return 0;//结果为10 4 9
}

8. 移形换位之术

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

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

详细见:
https://blog.csdn.net/huangchijun11/article/details/72725463?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166890979516782428632372%2522%252C%2522scm%2522%253A%252220140713.130212432.pc%255Fall.%2522%257D&request_id=166890979516782428632372&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~pc_ctr_v2-1-72725463-null-null.142v65opensearch_v2,201v3add_ask,213v2t3_esquery_v1&utm_term=%60const%20int%20*%60%E5%92%8C%60int%20const%20*%60&spm=1018.2226.3001.4187

int a = 1;
int const b = 2;
const int c = 3;
void funco(int n) {
  n += 1;
  n = a;
}
void func1(int *n) {
  *n += 1;
  n = &a;
}
void func2(const int *n) {
  *n += 1;
  n = &a;
}
void func3(int *const n) {
  *n += 1;
  n = &a;
}
void func4(const int *const n) {
  *n += 1;
  n = &a;
}

const int 和int const都是指该int类型变量不能被修改,没有区别

const int和int const也没有区别,都是指指针指向的值不能被改变

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

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

char *convert(const char *s);
int main(void) {
  char *str = "XiyouLinux Group 2022";
  char *temp = convert(str);
  puts(temp);
}
char* convert(const char* s)
{
    int i;
    char* tmp = (char*)malloc(sizeof(char) * strlen(s));
    tmp[21] = '\0';
    strcat(tmp, s);
    for (i = 0; i < 21; i++) {
        if (tmp[i] >= 96 && tmp[i] <= 122) {
            tmp[i] -= 32;
        }
        else if (tmp[i] >= 65 && tmp[i] <= 90) {
            tmp[i] += 32;
        }
    }
    free(tmp);
    return tmp;
}

10. 交换礼物的方式

  • 请判断下面的三种Swap的正误,分别分析他们的优缺点。
  • 你知道这里的do {...} while(0)的作用吗?
  • 你还有其他的方式实现Swap功能吗?
#define Swap1(a, b, t) \
  do {                 \
    t = a;             \
    a = b;             \
    b = t;             \
  } while (0)
#define Swap2(a, b) \
  do {              \
    int t = a;      \
    a = b;          \
    b = t;          \
  } while (0)
void Swap3(int a, int b) {
  int t = a;
  a = b;
  b = t;
}

题解:

do while配合宏定义使用!

①swap1直接从原函数中传递参数,方便

②swap2是创建了一个变量t,用来交换,占用内存小

③swap3是错误的。它只是改变了变量a,b的值,并没有传地址,仅仅是在函数体内部改变了a,b的值,相当于没有对a,b做任何操作,函数运行后a,b的值不会发生改变。

//传地址
void Swap(int* a,int* b)
{
   int t=*a;
   *a=*b;
   *b=t;
}

11. 据说有个东西叫参数

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

不能!!!(悲)

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

12. 人去楼空

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

#include<stdio.h>
#include<stdlib.h>
int *func1(void)
{
    static int n=0;//生存周期是从调用它开始到本函数结束
    n=1;
    return &n;//只能返回指针值也就是n的地址,因为静态变量的值一直保存,且地址一直存在(重点)
}
int *func2(void)
{
    int*p=(int*)malloc(sizeof(int));
    *p=3;
    return p;//返回指向新分配内存的首地址
}
//错误
int *func3(void)
{
     int n=4;
     return n;//不可返回局部变量的地址(&n),作用完就被释放了。
}


13. 奇怪的输出

int main(void) {
  int data[] = {0x636c6557, 0x20656d6f, 0x78206f74,
                0x756f7969, 0x6e694c20, 0x67207875,
                0x70756f72, 0x32303220, 0x00000a31};
  puts((const char*)data);
}

同22-12

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

1.预处理(主要处理代码中以字符#开头的命令)
2.编译
3.汇编(把汇编语句转换为机器指令)
4.链接(链接器将所有二进制的目标文件和系统组件组合成一个可执行文件(.exe))

15. (选做) 堆和栈

你了解程序中的栈和堆吗?它们在使用上有什么区别呢?请简要说明。
https://blog.csdn.net/K346K346/article/details/80849966?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166891059316800213074800%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=166891059316800213074800&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-1-80849966-null-null.142v65opensearch_v2,201v3add_ask,213v2t3_esquery_v1&utm_term=%E6%A0%88%E5%92%8C%E5%A0%86&spm=1018.2226.3001.4187

16. (选做) 多文件

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

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

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

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

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

我们在FZ103等你!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值