西邮Linux兴趣小组2024纳新面试题 0.聪明的吗喽 猴子每走一米会消耗掉三根香蕉,分别是往前走一米会消耗一根,返回起点会消耗一根,再搬另外50根会再消耗一根,一共消耗三根,所以我们只需要找到一个中转站,使香蕉的数量小于50根,便不会进行往返,所以我们走的路程消耗的香蕉要大于50根,一米消耗三根,所以在50/3约等于17米处作为中转站,刚好使剩下的香蕉为49根,小于50根,则剩下的49根香蕉我们可以直接带回去,后面剩余50-17=33米,每走一米消耗一根香蕉,所以最后猴子剩余49-33=16根香蕉。 1.西邮Linux欢迎你啊 int main(){ unsigned int a=2024; for( ;a>=0;a--) printf("%d\n",printf("Hi guys! Join Linux-2%d",printf(""))); return 0; }unsigned是无符号类型的,所以a是永远大于0的,所以for循环会一直执行,for中的输出是一个嵌套,先看最里面的输出的是" ",输出了0个字符,所以%d是0,在printf里面的一共有24个字符,所以在输出24,所以输出的%d是2024,而for是一直循环的,所以本题代码会一直输出Hi guys! Join Linux-2024。 2.眼见不一定为实 int main ( ) { char p0[ ] = "I love Linux" ; const char * p1= "I love Linux\0Group" ; char p2[ ] = "I love Linux\0" ; printf ( "%d\n%d\n" , strcmp ( p0, p1) , strcmp ( p0, p2) ) ; printf ( "%d\n%d\n" , sizeof ( p0) == sizeof ( p1) , strlen ( p0) == strlen ( p1) ) ; return 0 ; } sizeof:运算符,计算整个占用的字节,包扩字符串中的’\0’ strlen:库函数,统计’\0’之前的字节数 第一个输出中比较p0与p1,strcmp是比较到第一个’\0’之前,所以p0与p1是相等的,所以输出0, 第二个输出中在’\0’之前也是相等的,所以也输出0, 第三个输出sizeof是包含整个的,p2明显比p1的长,所以两个不相等,所以输出的是0, 第四个输出中是比较p0与p1在’\0’之前的的有效长度,两个有效长度相等,所以输出1。 所以最后输出0 0 0 1。 3. 1.1-1.0!=0.1 int main ( ) { float a= 1.0 , b= 1.1 , ex= 0.1 ; printf ( "b-a=ex is %s\n" , ( b- a== ex) ? "ture" : "false" ) ; int A= a* 10 , B= b* 10 , EX= ex* 10 ; printf ( "B-A=EX is %s\n" , ( B- A== EX) ? "ture" : "false" ) ; } float是二进制浮点数,所以在运行过程中我们会先将变量转换为二进制再转换过来进行运算,这就导致在运算过程中变量的值是近似接近我们原先赋予的值的, 在第一个输出中,三目运算符中b-a只能近似等于ex所以不成立,输出’:'后面的false, 第二个输出中,A为a*10,变为10.0,B为b *10,变为11,EX为ex *10,变为10,则这三个全部变为整数,整数不存在精度丢失,所以三目运算符中B-A=EX是正确的,我们输出"?"后面的ture, 所以最后输出的是b-a=ex is false B-A=EX is ture。 4.听说爱用位运算的人技术都不太差 int average ( int nums[ ] , int start, int end) { if ( start== end) return nums[ start] ; int mid= ( start+ end) / 2 ; int leftAvg= average ( nums, start, mid) ; int rightAvg= average ( nums, mid+ 1 , end) ; return ( leftAvg& rightAvg) + ( ( leftAvg^ rightAvg) >> 1 ) ; } mid=(start+end)/2是算出数中的中间,便于后面进行分开,然后使用递归将整个分为了两部分并求出两部分的平均值一部分是leftavg,一部分是rightavg,最后返回的第一个括号内如果两个二进制的对应位都是1,则相加为10,进一位,第二个括号内对应位为一个0,一个1,相加结果为一留在本位, 整个推导过程便是a+b=(a&b)*2+(a^ b),(a+b)/2=(a&b)+((a ^ b)>>1),最后得出平均数 第一个if是如果左边的平均数与右边的平均数这两个数字相等,便结束,这也意味着这个数字就是我们要求的平均数。 优点:如果a+b是一个超出int范围内的值的话,普通求平均值会栈溢出,而这个函数避免了两个数字的相加,从而可以得到结果 缺点:这个让人不容易理解,且代码不适合小数平均数。 5.全局还是局部 int i= 1 ; static int j= 15 ; int func ( ) { int i= 10 ; if ( i> 5 ) i++ ; printf ( "i=%d,j=%d\n" , i, j) ; return i% j; } int main ( ) { int a= func ( ) ; printf ( "a=%d\n" , a) ; printf ( "i=%d,j=%d\n" , i, j) ; return 0 ; } 在func函数中,先把10赋给i,如果i大于5,则i加一,所以i为11,输出了11,15,返回了i%j==11 在main函数中,static修饰局部变量,这个变量只会在开始初始化一次,后面不会进行初始化,所以在main函数中,i1,j 15,输出a的值,就是函数func算出的余数11,所以a==11,后面输出i的值与j的值。 所以最后输出的是 i=11,j=15 a=11 i=1, j=15 6.指针的修罗场:改还是不改,这是个问题 int main ( int argc, char * * argv) { int a= 1 , b= 2 ; const int * p= & a; int * const q= & b; * p= 3 , q= & a; const int * const r= & a; * r= 4 , r= & b; return 0 ; } const是修饰变量的,被他修饰的变量是不可以被修改的,而const有一个原则,谁离const最近谁就会被const修饰, 第一个const int *p=&a就是指向的对象&a不能被修改, 第二个int *const q=&b就是q本身不能被修改, 第三个const int * const r=&a就是两个都不能被修改, 所以q=&a与下面的两个对*r和r的改变是不合理的 7.物极必返? int main ( int argc, char * argv[ ] ) { while ( argc++ > 0 ) ; int a= 1 , b= argc, c= 0 ; if ( -- a|| b++ && c-- ) for ( int i= 0 ; argv[ i] != NULL ; i++ ) printf ( "argv[%d]=%s\n" , i, argv[ i] ) ; printf ( "a=%d,b=%d,c=%d\n" , a, b, c) ; return 0 ; } argc:命令行参数的个数 ,argv:保存参数字符串的指针数组, 命令行参数有两个,所以argc的初始值为2,所以while中的条件一直成立,所以argc一直自增,直到int的最大值后变为负数为-2147483647,再执行if里面的条件,–a为前置自减,得出值为0,所以条件为假,继续执行||后面的b++,得出b=-2147483646,执行c–,得出c=-1,因为argv的输出在while条件不成立之后,所以不会输出, 所以最后的输出为a=0,b=-2147483646,c=-1。 8.指针?数组?数组指针?指针数组? int main ( ) { int a[ 2 ] = { 4 , 8 } ; int ( * b) [ 2 ] = & a; int * c[ 2 ] = { a, a+ 1 } ; return 0 ; } a, a+ 1 , & a, & a+ 1 , * ( a+ 1 ) , sizeof ( a) , sizeof ( & a) * b, * b+ 1 , b, b+ 1 , * ( * b+ 1 ) , sizeof ( * b) , sizeof ( b) c, c+ 1 , & c, & c+ 1 , * * ( c+ 1 ) , sizeof ( c) , sizeof ( & c) 看第一组,第一组的第一个是输出a,也就是a的首地址a[0],所以输出4,第二个是a+1,则为a[1],所以输出的是8,第三个输出的是整个数组的地址,类型为int(*b)[2],第四个为&a+1,跳过了整个数组,所以地址为&a+2 *4=&a+8,第五个输出 *(a+1)等价于a[1],所以输出8,第六个输出a的字节,a是整个数组的大小,所以输出2 *4=8,最后一个输出的是&a的字节数,&a的指针,所以为8字节, 第二组中是一个数组指针,第一个*b,对 *b进行解引用,就等价于数组a[0],得到值为4,第二个 *b+1,就是&a[1],偏移一个int,第三个b是数组指针的值,也就是&a,输出&a的地址,第四个b+1跳过整个数组,也就是2 *4=8个字节,所以输出的是&a+8, 第五个 *( *b+1)就是a[0]+1得到a[1],所以输出的为8,第六个输出 *b的字节, *b就是数组a,所以字节为2 *4=8个字节。,最后一个b的字节,b是指针,在六十四位下是8个字节,所以输出8 第三组中是一个指针数组,第一个c就是c的首元素地址&c[0],第二个c+1,就是&c[1],偏移了8个字节,第三个&c,输出的就是整个数组的地址,第四个&c+1跳过了整个数组,所以是2*8=16个字节,第五个**(c+1)最后变为 *c[1]变为a[1],所以输出8,第六个c的字节,c中含有两个指针,所以,占了8 *2=16个字节,最后一个&c的字节数,&c是指针,所以输出8个字节 9.嘻嘻哈哈,好玩好玩 # define SQUARE ( x) x* x# define MAX ( a, b) ( a> b) ? a: b; # define PRINT ( x) printf ( "嘻嘻,结果你猜对了吗,包%d滴\n" , x) ; # define CONCAT ( a, b) a## b int main ( ) { int CONCAT ( x, 1 ) = 5 ; int CONCAT ( y, 2 ) = 3 ; int max= MAX ( SQUARE ( x1+ 1 ) , SQUARE ( y2) ) PRINT ( max) return 0 ; } ##是宏拼接运算符,是把两个标记粘在一起合成一个新名字, 则代码中CONCAT(x,1)为x1,同理CONCAT(y,2)y2,所以x1=5, y2=3,SQUARE(x1+1)因为没有括号,所以运算为x1+1*x1+1=11,SQUARE(y2)为y2 *y2=9, 所以最后变为MAX(11,9),三目运算符中11大于9,所以输出"?"后面的a,所以最后输出的是11, 所以最后输出嘻嘻,结果你猜对了吗,包11滴。 10.我写的排序最快 int main ( ) { int arr1[ ] = { 2 , 3 , 1 , 3 , 2 , 4 , 6 , 7 , 9 , 2 , 10 } ; int arr2[ ] = { 2 , 1 , 4 , 3 , 9 , 6 , 8 } ; int len1= sizeof ( arr1) / sizeof ( arr1[ 0 ] ) ; int len2= sizeof ( arr2) / sizeof ( arr2[ 0 ] ) ; result result; your_sort ( arr1, len1, arr2, len2, & result) ; for ( int i= 0 ; i< reslut. len; i++ ) { printf ( "%d" , result. arr[ i] ) ; } free ( result. arr) ; return 0 ; } typedef struct { int * arr; int len; } result; void bubble_sort ( int * a, int n) { for ( int i= 0 ; i< n- 1 ; i++ ) { for ( int j= 0 ; j< n- 1 - i; j++ { if ( a[ j] > a[ j+ 1 ] ) { int a= a[ j] ; a[ j] = a[ j+ 1 ] ; a[ j+ 1 ] = t; } } } } void your_sort ( int arr1[ ] , int len1, int arr2[ ] , int len2, result* res) { int total= len1+ len2; int * tmp= ( int * ) malloc ( total* sizeof ( int ) ) ; int k= 0 ; for ( int i= 0 ; i< len1; i++ ) tmp[ k++ ] = arr1[ i] ; for ( int i= 0 ; i< len2; i++ ) tmp[ k++ ] = arr2[ i] ; bubble_sort ( tmp, total) ; int * uniq= ( int * ) malloc ( total* sizeof ( int ) ) ; int uniq_len= 0 ; for ( int i= 0 ; i< total; i++ ) { if ( uniq_len== 0 || tmp[ i] != uniq[ uniq_len- 1 ] ) { uniq[ uniq_len++ ] = tmp[ i] ; } } free ( tmp) ; res-> arr= uniq; res-> len= uniq_len; } 11.猜猜我是谁 int main ( ) { void * a[ ] = { ( void * ) 1 , ( void * ) 2 , ( void * ) 3 , ( void * ) 4 , ( void * ) 5 } ; printf ( "%d\n" , * ( ( char * ) a+ 1 ) ) ; printf ( "%d\n" , * ( int * ) ( char * ) a+ 1 ) ; printf ( "%d\n" , * ( ( int * ) a+ 2 ) ) ; printf ( "%lld\n" , * ( ( long long * ) a+ 3 ) ) ; printf ( "%d\n" , * ( ( short * ) a+ 4 ) ) ; return 0 ; } 第一个输出中强制将a转换为char*类型,所以后面加一是向后移动一个字节,void总共八个字节,首地址向后移动一个字节是0, 第二个输出中先取的是char*的首地址,然后强制转换为Int类型加四个字节,得到值为一,后面再进行加一得到值为2, 在第三个输出中也是先转为int*类型的,后面加2代表向后移动了2 *4=8个字节,正好到a[1]也就是(void *)2,最后输出2, 第四个输出中 long long的字节为8,加三就代表移动了3*8=24个字节,刚好移动到a[3]=(void *)4,所以最后输出的是4, 最后一个short占据2个字节,后面加4一共个移动了2*4=8个字节,刚好跳过a[0]输出的是a[1]所以最后输出的是2, 所以最后输出的是0 2 2 4 2 12.结构体变小写奇遇记 union data{ int a; double b; short c; } ; typedef struct node { long long a; union data b; void ( * change) ( struct node * n) ; char string[ 0 ] ; } Node; void func ( Node* node) { for ( size_t i= 0 ; node-> string[ i] != '\0' ; i++ ) node-> string[ i] = tolower ( node-> string[ i] ) ; } int main ( ) { const char * s= "WELCOME TO XIYOULINUX_GROUP!" ; Node* p= ( Node* ) malloc ( sizeof ( Node) + ( strlen ( s) + 1 ) * sizeof ( char ) ) ; strcpy ( p-> string, s) ; p-> change= func; p-> change ( p) ; printf ( "%s\n" , p-> string) ; return 0 ; } char string[0]是柔性数组,也就是零长度数组,只能放在结构体的最后一个,且在计算结构体大小的时候是不将他计入计算的,union是联合体,他的大小取决于联合体里面最大成员的大小,在代码中union里的最大成员为double,占了8个字节,所以union的字节为8,long long占据的字节为8,void(*change)是函数指针,占据8个字节,最后一个是柔性数组,不计入计算,所以结构体的大小是8+8+8=24, 主函数中先是给了一串大写字母,后面malloc一次分配整个内存,再用函数strcpy将s复制到柔性数组,后面再将函数指针指向func函数,p->change§就等价于调用func(p),在func函数中遍历整个字符串,直到i为’\0’时,结束循环,在循环中,我们将字符串中的字母全部变为小写, 所以最后输出的welcome toxiyoulinux_group!