2020InterviewSolution

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

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

注:

  1. 本题仅作为面试有限参考
  2. 为节省版面,省去所有#include指令
  3. 题目难度与序号无关
  4. 若无特殊声明,均假设在Linux x86_64 GCC编译器环境下

1. 请试着解释其输出。

int main(int argc , char *argv[]) {
  unsigned char a = 255;
  char ch = 128;
  a -= ch;
  printf("a = %d ch = %d\n", a, ch);
}
11111111
10000000
a-ch=01111111

char(一字节)范围-128至127,所以ch=-128

2. 下面代码的运行输出结果是什么,并说说你的理解。

int main(int argc, char *argv[]) {
  char *str = "Xi You Linux Group 20";
  printf("%d\n", printf(str));
  return 0;
}

printf(str)输出Xi You Linux Group 20,返回值为21(输出字符串长度),

3. 这段代码的输出结果是什么?为什么会出现这样的结果?

int i = 2;
void func() {
  if(i != 0) {
  static int m = 0;//只执行一次
  int n = 0;
  n++;
  m++;
  printf("m = %d, n = %d\n", m, n);
  i--;
  func();
  } else {
  return;
  }
}
int main(int argc, char *argv[]) {
  func();
  return 0;
}

输出m = 1, n = 1
m = 2, n = 1

调用函数func,m,n初值为零,分别++,输出m = 1, n = 1。再调用函数func,n被重新赋初值0。再分别++,输出

m = 2, n = 1,在调用func ,此时i=0,结束。

静态(static)变量存放在静态存储区,只执行一次。生命周期是从对象定义到程序结束的。

4. 下面程序会出现什么结果?为什么会出现这样的结果?

int main(int argc, char * argv[]) {
  char ch = 'A';
  int i = 65;
  unsigned int f = 33554433;
  *(int *)&f >>= 24;
  *(int *)&f = *(int *)&f + '?';
  printf("ch = %c i = %c f = %c\n", ch, i, *(int *)&f);
  return 0;
}

A的ASCII码为65。

f=33554433转为二进制为10000000000000000000000001

00000010000000000000000000000001
右移24位=00000000000000000000000000000010
十进制=2

?的ASCII码为63。63+2=65即A的ASCII码。

5. 下面代码的运行输出结果是什么,并说说你的理解。

int main(int argc, char *argv[]) {
  int a[2][2];
  printf("&a = %p\t&a[0] = %p\t&a[0][0] = %p\n", &a, &a[0], &a[0][0]);
  printf("&a+1 = %p\t&a[0]+1 = %p\t&a[0][0]+1= %p\n", &a+1, &a[0]+1, &a[0][0]+1);
  return 0;
}

&:输出的是地址。

int型占四字节。

&a, &a[0], &a[0][0]指的都是数组的首地址,&a[0][0]+1为首地址+4,&a[0]+1相当于&a[0+1],首地址+8(2*4)。&a+1为首地址加整个二维数组的大小,即+16(2*2*4)

6. 下列程序的功能是什么?有什么问题,你能找出问题并解决它吗?

int* get_array() {
  int array[1121]; 
  for (int i = 0; i < sizeof(array) / sizeof(int); i++) {
    array[i] = i;
  }
  return array;
}
int main(int argc, char *argv[]) { 
  int *p = get_array();
}

功能:定义一个依次增大,大小为1121的一维数组。

应把int array[1121]改为 int *array=(int *)malloc(1121*4);

因为return array相当于返回的是array的地址,而函数结束,栈内存被释放,输出错误。

7. 下面代码的运行输出结果是什么,并说说你的理解。

int main(int argc, char *argv[]) {
  char str[] = "XiyouLinuxGroup"; 
  char *p = str; 
  char x[] = "XiyouLinuxGroup\t\106F\bamily";
  printf("%zu %zu %zu %zu\n", sizeof(str), sizeof(p), sizeof(x), strlen(x));
  return 0;
}

sizeof返回占字节数。字符串长度(不包括"\0")。

sizeof(str)=15+1=16。 (char *)型占8字节。

char x[]中的字符为XiyouLinuxGroup ‘\t’ ‘\106’ F ‘\b’ amily。

sizeof(x)=25, strlen(x)=24

8. 如下程序,根据打印结果,你有什么思考?

int add(int *x, int y) {
  return *x = (*x^y) + ((*x&y)<<1);
}
int a;
int main(int argc, char *argv[]) {
  int b = 2020;
  if(add(&b, 1) || add(&a, 1)) {
  printf("XiyouLinuxGroup%d\n", b);
  printf("Waiting for y%du!\n", a);
  }
  if(add(&b, 1) && a++) {
  printf("XiyouLinuxGroup%d\n", b);
  printf("Waiting for y%du!\n", a);
}
  return 0;
} 

add(&b, 1)=2020^1+(2020&1<<1)

2020=1111110010011111100100
0000000000100000000001
2020^1=111111001012020&1=00000000000

add(&b, 1)=2021

a为全局变量,默认为0.

因为add(&b, 1)在先,调用add(&b, 1)=2021!=0,则(2021||add(&a, 1))=1,没调用add(&a, 1),输出。(2022&&0)=0,if不成立,不输出。

9. 在下段程序中,我们可以通过第一步打印出a的地址,假如在你的机器上面打印结果是0x7ffd737c6db4;我们在第二步用scanf函数将这个地址值输入变量c中;第三步,随机输入一个数字,请问最终输出了什么结果,你知道其中的原理吗?

void func() { 
  int a = 2020;
  unsigned long c;
  printf("%p\n", &a);
  printf("我们想要修改的地址:");
  scanf("%lx", &c);
  printf("请随便输入一个数字:");
  scanf("%d", (int *)c);
  printf("a = %d\n", a);
}

&a输出a的地址,再输入,c就等于a的地址,再把c强制转换成(int *)类型,c变成了指向a的指针。输入的值便储存到了a中。

10. 请问一个C语言程序从源代码到可执行文件中间会进行哪些过程,你能简单描述一下每个环节都做了什么事情吗?

1.先预处理(去注释,宏展开),然后编译,把c语言代码翻译成汇编语言的代码。(.c->.s)

2.再汇编,将之前编译得到的汇编语言代码翻译为二进制机器码。(.s->.o)

3.最后链接,每个目标文件.o合并成为一个可执行程序。(.o->.out)

11. 请解释一下这行代码做了什么?

puts((char*)(int const[]){
0X6F796958,0X6E694C75,0X72477875,
0X3270756F,0X313230,0X00000A
});

以小端输出了每两位的ASCII码对应字符。

大小端:对于一个由2个字节组成的16位整数,在内存中存储这两个字节有两种方法:一种是将低序字节存储在起始地址,这称为小端字节序;另一种方法是将高序字节存储在起始地址,这称为大端字节序。

即:

大端是高字节存放到内存的低地址。

小端是高字节存放到内存的高地址。

操作系统一般为小端。

通讯协议一般为大端。

12. 请随机输入一串字符串,你能解释一下输出结果吗?

int main(int argc, char *argv[]) {
  char str[1121];
  int key;
  char t;
  fgets(str, 1121, stdin);
  for(int i = 0; i < strlen(str) - 1; i++) {
    key = i;
    for(int j = i + 1; j < strlen(str); j++) {
      if(str[key] > str[j]) {
        key = j;
      }
    } 
    t = str[key];
    str[key] = str[i];
    str[i] = t;
  } 
  puts(str);
  return 0;
}

把输入的字符按ASCII码从小到大排列(选择排序)。

fgets(字符数组名,最大字符数n, FILE stream): 从指定的流 stream 读取一行,并把它存储在str所指向的字符串内。当读取*(n-1)**个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止。

13. 用循环和递归求Fibonacci数列,你觉得这两种方式那种更好?说说你的看法。如果让你求Fibonacci数列的第100项,你觉得还可以用常规的方法求解吗?请试着求出前100项的值(tip大数运算)。

递归其实是方便了程序员难为了机器。它只要得到数学公式就能很方便的写出程序。优点就是易理解,容易编程。但递归是用栈机制实现的,每深入一层,都要占去一块栈数据区域,对嵌套层数深的一些算法,递归会力不从心,空间上会以内存崩溃而告终,而且递归也带来了大量的函数调用,这也有许多额外的时间开销。所以在深度大时,它的时空性就不好了。

循环其缺点就是不容易理解,编写复杂问题时困难。优点是效率高。运行时间只因循环次数增加而增加,没什么额外开销。空间上没有什么增加。

递归:

#include<stdio.h>
int main(){
    int t,i,n,x=0,a[30]={0},b[30]={0},c[30]={0};
    a[0]=1;
    b[0]=1;
    scanf("%d",&n);
    for( i=2;i<n;i++){x=0;
        for( t=0;t<30;t++){ 
                c[t]+=a[t]+b[t];
            if(c[t]>=10){
               c[t+1]++;
                c[t]-=10;
            }
        }
        for(t=0;t<30;t++){
            a[t]=b[t];
            b[t]=c[t];
            c[t]=0;
        }
    }
    for(t=29;;t--){
        if(b[t]!=0){
            break;
        }
    }
    for(i=t;i>=0;i--){
        printf("%d",b[i]);
    }
}

14. Linux 实操题

请通过命令创建一个目录,在该目录中创建几个后缀为.Linux的文件,然后通过命令查询这几个文件的基本属性信息(如文件大小,文件创建时间等),之后使用命令查看该目录下文件名含有“.Linux”的文件的数量(不包括子目录下的文件),把得到的数字写入到一个文件中,最后删除此目录。

mkdir x

cd x

touch a.Linux b.LInux

ls -l

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值