Linux兴趣小组近三年面试题总结
1.解释下面程序的运行结果,并总结static的用法
int *func(void)
{
static int a = 1;
a++;
return &a;
}
int main(int argc , char *argv[])
{
int *b;
b =func();
printf("%d\n",*b);
b = func();
printf("%d\n",*b);
return 0;
}
这道题如果对于不认识static的人来说答案就是2,2;其实正确答案是2,3;为什么?
其中的奥妙就在这个static中,这里的func() 函数中的 a被定义为一个静态局部变量**(程序在离开他们所在的函数后,这些变量不会消失)**,所以在函数第二次进入func()时a的值不是1,而是2。
2.解释程序的运行结果:
突然发现面试题里所有的函数名都是func
void func(char *a)
{
printf("%lu\n",sizeof(a));
printf("%lu\n",strlen(a));
}
int main(int argc , char *argv[])
{
char a[] = "hello world";
func(a);
printf("%lu\n",sizeof(a));
printf("%lu\n",strlen(a));
return 0;
}
这道题是有坑的,我第一次做就做错了;我把func函数里面求得sizeof(a)数了出来最终数成了12;其实函数里面的a是一个指针变量,在linux_gcc下指针变量的大小为8,所以这里的sizeof(a)的大小就是8;
再来说一下strlen这个函数,这个函数是来计算字符串的长度的,众所周知字符串会在末尾自动加一个’\0’,但是我们的strlen函数是不计算这个’\0’的也可以说strlen函数遇到’\0’就会返回。例如
char a[] = "1\0sad";
int k = strlen(a);
这个时候k的值就等于1了;
还有一种情况:
char a[] = "12\0xsad\0123456";
int k = strlen(a);
可能有的人会觉得很简单k等于2嘛但是其实不是的k等于6;细心地朋友数一数会发现他把’\0x’看做了一个字符;这里是为什么呢?因为计算机在查看字符的时候是一个一个查看的首先他先收到了’ \ ‘,接着他收到了’0’,’ \ ‘和’0’可以组成一个字符所以继续向下查找找到了’x’,’\0x’是一个字符所以继续向下查找’\0xs’不能构成一个字符所以输出’\0x’这一个字符;
3.解释该函数的输出结果
void func(void)
{
unsigned int a = 6;
int b = -20;
(a+b>6) ? puts(">6") : puts("<6");
}
先来介绍一下?:这个三目运算符;这个的意思就是如果(a+b>6)为真就执行puts(">6")否则就执行puts("<6");
接下来我们就来讲述这个看起来很简单的函数;为什么说看似很简单?因为a+b一眼看过去就是小于6的所以这个题很简单;但是人是这么想的计算机呢?这计算机中数字的存储都是以二进制的形式;在计算的时候都是以补码来进行计算的
而无符号数的补码最高位不表示符号,所以一个负数表示为无符号数的时候他就会变成一个很大的正数,所以上题中的a在和b相加的时候b会变成一个很大的正数所以结果就大于6了;
那我们如果输出a+b呢大家可以试试以%d输出和以%lu输出的不同;%lu是专门输出无符号数的。
4.阅读以下代码
int main(int argc , char *argv[])
{
int a[3][4];
printf("%p%p%p%p%p%p\n",&a[0][0],a[0],a,a[0]+1,a+1,a[1]);
return 0;
}
若a[0][0]的地址0x00000000,求程序输出的结果:
先放答案:
0x00000000 0x00000000 0x00000000 0x00000004 0x00000030 0x00000010
大家比较困惑的应该就是a+1这个了吧:
&a[0]+1,就是跨过第一行,即一个int[4];a[0]+1就是跨国第一行的第一个元素,即&a[0][1]。&a+1自然就跨过了整个数组。
5.下列结构体在内存中所占空间的大小分别是多少?
struct node
{
int x;
char y;
double z;
};
struct node
{
int x;
double y;
char z;
};
这道题考的是一个内存对齐原则:
1.起始位置为成员数据类型所占内存的整数倍,若不足则不足部分用数据讲内存填充为该数据类型的整数倍;
2.结构体所占内存为其成员变量中所占空间最大数据类型的整数倍;
先看第一个结构体:
所以这道题结构体的大小第一个是16;
同理可得第二个结构体:
第二个结构体的大小是24;
6.解释程序的运行结果
#define f(a,b) a##b
#define g(a) #a
#define h(a) g(a)
int main(int argc , char *argv[])
{
printf("%s\n",h(f(1,2)));
printf("%s\n",g(f(1,2)));
return 0;
}
相信大家和我一样看到这个题的那一刻我是一脸懵逼的;查了一些资料我知道了#是把宏参数变成一个字符串;##是把两个宏参数连接在一起;
好了知道了这个以后那答案不是很简单输出12 12;好吧我还是错了;因为关于宏定义的展开规则还有以下过程:
再展开当前宏函数时,如果形参有#或##则不进行宏参数的展开,否则县展开宏参数,在战绩啊当前宏;
本题中h(f(1,2)和g(f(1,2)),我们会发现h作为printf函数的形参没有#或##所以进行展开最终打印出12;而g作为printf函数的形参有#a,所以不进行展开最终打印出f(1,2);
7.请解释当i的值分别为1,2和3的时候,程序的运行结果
switch(i)
{
case 1:
printf("i'm case1\n");
break;
default:
printf("i'm default\n");
case2:
printf("i'm case2\n");
}
这道题其实没什么说的,就是switch语句每一个case后面都要有break来跳出函数,否则就会一直运行,直到遇到break或者switch结束;
8.这三个指针有什么区别?
int a = 3;
const int *p1;
int const *p2;
int *const p3 = &a;
const int *const p4 = &a;
这里有两个概念一个叫做指针常量,一个叫常量指针;
指针常量:指向常量的指针,这里所说的“常量”其实在解引用时起到所谓常量的作用,并非是指向的变量就是常量,指向的变量既可以是非常量也可以是常量;总体来说就是不能修改指向某指针变量的值;
常量指针:不能改变指向的变量(即不能改变指针保存的内容)
指向常量的常量指针:既不能修改指向某指针变量的值,也不能修改指向某指针变量的值;
好了说完上述三个概念那么到底哪个是常量指针哪个是指针常量呢?
本题中:
int *const p3 = &a是常量指针,常量指针必须赋初值,而且不可以改变指向的变量,但是可以改变地址中保存变量的值例如:
int b = 5;
p3 = &b; //错误;
*p3 = 5; // 正确;
const int *p1;和int const *p2是指针常量,他们可以改变指向的地址,但是不能改变地址里的值;例如:
int b = 5;
p1 = &b; // 正确;
*p1 = 5; // 错误;
9.下列程序分别输出的是数组中的第几个0?
int main(in argc , char *argv[])
{
int a[][2] = {0,0,0,0,0,0,0,0};
for(int i = 0;i<=2;i++)
printf("%d",a[i][i]);
return 0
}
这道题第一眼看过去越界了啊!!但是其实要先搞清二维数组在内存中的存储方式,他并不是立体的他还是线性的;
这个二维数组在内存中是这样子的所以a[2][2]应该是a[2][1]下一个即a[3][0];
10.下面代码会输出什么
int main(int argc , char *argv[])
{
int a = 10, b = 20, c = 30;
printf("%d %d\n", b = b*c , c = c*2 );
printf("%d\n",printf("%d\n",a+b+c));
return 0;
}
这道题考了两个东西,先说简单点的就是printf函数的返回值,printf函数返回打印字符串的数目;例如:
printf("%d",123);
printf("%s","123\0ggfdr");
这两个分别返回多少呢,答案是,第一个返回3,第二个也返回3;可见printf函数遇到’\0’就返回了;
然后再说第二个问题就是本题中b = b * c 和 c = c*2 谁先计算呢,这关系到b = 打印出来的值呢;事实上printf函数就像是一个栈,从右向左进栈;
所以这个打印出来输出的b是1200而不是600;
11.下面代码正确吗?若正确,请说明代码作用;若不正确,请指出错误并修改。
int main(int argc , char *argv[])
{
char *ptr = NULL;
ptr = (char*)malloc(17);
strcpy(ptr , "xiyou linux group");
}
这道题看起来没有错误,但是在电脑上运行一下是会报错的,问题就在这个strcpy函数上,这是一个字符串拷贝函数,是不改变地址的;而char *ptr,是一个指向常量区的指针,所以他其实是一个指针常量,是不能改变他地址里面的内容的;这道题把指针换成数组就可以了;
void get_str(char ptr[])
12.对比下面程序在linux和windows上的输出结果,并思考原因
int main(int char , char *argv[])
{
while(1)
{
fprintf(stdout,"Group");
fprintf(stderr,"XiyouLinux");
getchar();l
}
return 0;
}
在windows下输出Group XiyouLinux
在Linux下输出XiyouLinux Group
咦为什么两个答案不一样呢?
我们来看看stdout 和 stderr, stdout, stdin, stderr的中文名字分别是标准输出,标准输入和标准错误。
当一个用户进程被创建的时候,系统会自动为该进程创建三个数据流,也就是题目中所提到的这三个。那么什么是数据流呢(stream)?我们知道,一个程序要运行,需要有输入、输出,如果出错,还要能表现出自身的错误。这是就要从某个地方读入数据、将数据输出到某个地方,这就够成了数据流。
因此,一个进程初期所拥有的这么三个数据流,就分别是标准输出、标准输入和标准错误,分别用stdout, stdin, stderr来表示。。这3个文件分别为标准输入(stdin)、标准输出(stdout)、标准错误(stderr)。它们在<stdio.h>中声明,大多数环境中,stdin指向键盘,stdout、stderr指向显示器。之所以使用stderr,若因某种原因造成其中一个文件无法访问,相应的诊断信息要在该链接的输出的末尾才能打印出来。当输出到屏幕时,这种处理方法尚可接受,但如果输出到一个文件或通过管道输出到另一个程序时,就无法接受了。若有stderr存在,即使对标准输出进行了重定向,写到stderr中的输出通常也会显示在屏幕上。
说了这么多还是不知道为什么不同
在linux下,stdout是行缓冲的,他的输出会放在一个buffer里面,只有到换行的时候,才会输出到屏幕。而stderr是无缓冲的,会直接输出,举例来说就是fprintf(stdout, “xxxx”) 和 fprintf(stdout,“xxxx\n”),前者会缓存,直到遇到新行才会一起输出。而fprintf(stderr, “xxxxx”),不管有么有\n,都输出。
13.(个人体会改编)
int main()
{
int a = 2;
printf("%d",sizeof(a=1));
printf("%d",a);
}
这道题的输出是4,2;并不是大家想的4,1;为什么?难道sizeof(a=1)没有改变a的值?
不是的!是因为sizeof的运行是在编译阶段就完成了!!!