西邮Linux兴趣小组2020纳新试题题解
感谢 Zhilu 重新录入题目原件。好人一生平安。
注:
- 本题仅作为面试有限参考
- 为节省版面,省去所有
#include
指令- 题目难度与序号无关
- 若无特殊声明,均假设在
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=11111100100 | 11111100100 |
---|---|
00000000001 | 00000000001 |
2020^1=11111100101 | 2020&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