2020面试题

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

unsigned char 的范围是0-255;所以a就是255
而char的范围是-128-127;这里ch越界了最大的char是127
二进制为0111 1111;在加一变成128就会变为1000 0000;这在有符号二进制中表示的是-128;所以ch真正的值是-128,a-ch=255-128=127.

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

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

这是printf函数的一个嵌套,打印%d,看后面返回值,后面又是printf函数,继续打印str,此时打印的总字符数为21,所以返回值为21;在打印21.最终输出结果为
Xi You Linux Group 2021

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

首先i=2,if成立,这里用static修饰m,意为静态变量,延长了m的生命周期,m的值会保留到下一次调用该函数时。而n在结束后就会被销毁,所以第一次m++,n++后,二者都为1,此时i为1,if成立,继续进入函数,此时m保留上次的值,自加变为2,而由于n被销毁,所以这次依然从0开始,自加结果为1.
此时i=0走else返回m和n的值,分别为,1;

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

ch = A i = A f = A

65是’A’的ASCII码。

*(int *)&f相当于作强制类型转换,在左移24位之后对应的10进制数字再加上’?'对应的ASCII码,即为’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;
}

a是数组名,a[0]表示二维数组第一行,a[0][0]表示数组第一个元素,首先他们三个表示的地址都是一样的,均为数组首元素的地址,而区别在于,给他们加以运算时,移动的距离不同,a是数组名,如果+1就会移动整个数组的距离即4 * 4=16;而a[0]+1,就会移动到第二行,移动两个int的距离,即2*4=8;最后a[0][0]移动一次就是a[0][1]了,即移动了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();
}

返回的是函数内定义的数组,生命周期已经结束,可以考虑使用指针数组

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

str内总计15字符,sizeof计算它大小的时候仍会计算字符串末尾的\0;所以结果为16,而strlen不会,结果为15,x里表示的\t为制表符,站两个长度,、106转义为大写字母F,\b转为退格符。我们这里puts一下x
XiyouLinuxGroup FFamily
易得sizeof结果为25,strlen结果为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;
} 

XiyouLinuxGroup2021
Waiting for y0u!
这里要搞清楚||是或,&&是与
或运算的原则是
00=0,01=1, 10=1, 11=1.
与运算的原则是
00=0, 01=0, 10=1, 11=1.
由此可判断出输出结果为
XiyouLinuxGroup2021
Waiting for y0u!

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

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

四个阶段:
(1)预处理阶段,预处理器(cpp)根据字符#开头的命令,修改原始的C程序。

(2)编译阶段,将c语言文件从高级语言转为汇编语言。

(3)汇编阶段,将汇编语言转化为二进制语言。

(4)链接阶段,将使用的头文件与本文件链接起来。

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

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

首先,这行代码输出了
XiyouLinuxGroup2021。
将里面的内容强制转化为字符型。
这里date里存储的是16进制整形,随后又将其转化为char类型并输出,所以要根据数字对应的ascll码值进行转化,即两位对应一个字符,又因为c语言中存在大小端
大端存储模式:是指数据的低位字节序保存在内存的高地址中,而数据的高位字节序保存在内存的低地址中
小端存储模式:是指数据的低位字节序保存在内存的低地址中,而数据的高位字节序保存在内存的高地址中
所以最终打印时为倒序打印。

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

本质为冒泡排序,即交换排序的一种。通过判断输入字符的ascll来对字符进行从小到大的输出。

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

递归更简洁,但费时间和空间,有可能发生栈溢出,循环费时短,但代码比较复杂臃肿。
大数计算:

#include <stdio.h>
#include <string.h>
int a[1005][1005];
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		int n;
		scanf("%d",&n);
		int c;
		int d=0;
		memset(a,0,sizeof(a));
		a[1][0]=1;a[2][0]=1;
		for(int i=3;i<=n;i++)
		{
			c=0;
			for(int j=0;j<=d;j++)
			{
		    	a[i][j]=a[i-1][j]+a[i-2][j]+c;
				c=a[i][j]/10;
				a[i][j]%=10;
			}
			while(c!=0)
			{
				a[i][++d]=c%10;
				c/=10;
			}
		}
		for(int i=d;i>=0;i--)
		{
			printf("%d",a[n][i]);
		}
		printf("\n");
	}
	return 0;
}

14. Linux 实操题

请通过命令创建一个目录,在该目录中创建几个后缀为.Linux的文件,然后通过命令查询这几个文件的基本属性信息(如文件大小,文件创建时间等),之后使用命令查看该目录下文件名含有“.Linux”的文件的数量(不包括子目录下的文件),把得到的数字写入到一个文件中,最后删除此目录。
首先创建目录
mkdir linux
在创建文件夹
touch a.linux
touch b.linux
touch c.linux
这时我们切到该目录来查看信息
cd linux
然后查看
ls -h 查看大小
ls -l 列表查看文件信息
这里可以直接
ls -lh
这里找个文件夹把数量输进去并保存退出
vim …
i
6
ESC :wq
这里写好了,然后删除该目录
cd linux
rmdir

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值