西邮Linux兴趣小组2026纳新面试题 0.树懒闪电的二进制车牌 由提示得出16个按钮是斐波纳契数列,屏幕显示的数字为11010100101,将这串二进制数字改为十进制,得出十进制的值为1701, 而二进制中的1代表按下按钮,0代表不按这个按钮,所以应该按第1,3,6,8,10,11个按钮,但是不能按相邻的两个,有因为每项等于前两项之和,所以第10和第11个按钮可以不按,改为按第13个按钮,所以最后按的按钮为第1,3,6,8,13个按钮,并且十进制为1701 1.一句印不完的欢迎词 int main ( ) { while ( 1 ) { printf ( "Hi! " ) ; if ( ! printf ( "The 202%d, Welcome to Xiyou Linux Group!\n" , printf ( "guys! " ) ) ) { break ; } } } 代码中while括号内是1,所以会一直执行while中的代码,第一个输出是Hi! ,后面if中是一个嵌套的输出,先看最内层的,输出"guys! ",加上后面的空格。总共是输出了6个字符,所以在外层的输出中%d是6, 所以最后输出的是Hi! guys! The 2026, Welcome to Xiyou Linux Group!,并且一直输出,不会停下 2.失忆的交换生 void swap_val ( int x, int y) { int tmp= x; x= y; y= tmp; } void swap_ptr ( int * x, int * y) { int tmp= * x; * x= * y; * y= tmp; } int main ( ) { int a= 10 , b= 20 ; swap_val ( a, b) ; printf ( "swap_val:a=%d,b=%d\n" , a, b) ; swap_ptr ( & a, & b) ; printf ( "swap_ptr:a=%d,b=%d\n" , a, b) ; } c语言采用的都是值传递,在swap_val函数中,我们只是将a和b的值进行复制代入了函数当中,只有形参发生改变,而不影响实参,所以第一个输出中a和b的值不会改变,在swap_ptr函数中我们使用了指针,获取了a和b的地址,通过指针传参来改变了a和b的值, 最终的输出为swap_val:a=10,b=20 swap_ptr:a=20,b=10。 3.sizeof的视力表 void inspect ( char text[ ] , int ( * matrix) [ 4 ] ) { printf ( "%zu %zu %zu %zu\n" , sizeof ( text) , sizeof ( matrix) , sizeof ( * matrix) , strlen ( text) ) ; } int main ( ) { char a[ ] = "Linux\0Group" ; char * p= a; int b[ 2 ] [ 4 ] = { { 1 , 2 , 3 , 4 } , { 5 , 6 , 7 , 8 } } ; printf ( "%zu %zu %zu\n" , sizeof ( a) , sizeof ( p) , sizeof ( a+ 0 ) ) ; printf ( "%d %d\n" , a[ 5 ] = '\0' , strcmp ( a, "Linux" ) ) ; inspect ( a, b) ; } %zu中z是size_t类型,u是无符号输出,在主函数中,a中的"Linux\0Group"加上最后的’\0’一共有12个字节,所以输出12,p是指针类型,在典型64位系统中指针是8个字节,所以输出8,a+0也是指针。所以也输出8,所以第一个printf输出的是12 8 8,第二个输出中因为数组的下标是从0开始的,所以a[5]为’\0’,所以两个相等,结果为真,所以输出0,第二个是进行比较,因为比较为’\0’之前的,字符串都相等,所以结果为真,所以输出的也是0,所以第二个的输出为0 0,第三个输出为inspect函数中的输出,test是一个指针,所以输出8,matrix也是一个指针,所以输出的也是8,*matrix是int类型的,占四个字节,一共有四个,所以占了4 *4=16个字节,所以输出16,strlen代表的是有效长度,在’\0’之前的,所以共输出5个字节,所以最后一个输出为 8 8 16 5 所以最后的所有输出为12 8 8 0 0 8 8 16 5 4.XOR密钥:藏在字节里的悄悄话 int main ( ) { int nums[ ] = { 167 , 150 , 134 , 144 , 138 , 179 , 150 , 145 , 138 , 135 , 184 , 141 , 144 , 138 , 143 } ; int size= sizeof ( nums) / sizeof ( nums[ 0 ] ) ; for ( int i= 0 ; i< size; i++ ) { int mask= 0 ; int bitval= 1 ; int bits= 8 ; while ( bits-- > 0 ) { mask|= bitval; bitval<<= 1 ; } printf ( "%c" , nums[ i] ^ mask) ; if ( i== size- 1 ) { printf ( "\n" ) ; } } } |=是按位或赋值运算符,相当于先进行按位或运算,再把结果赋值给左边的变量,在for循环中,bit的值为8,在,执行while中的代码,直到bit–的值变为0,在while循环中,先是将bitval和mask按位或进行运算,再将bitval向左移一位,bitval的初始值为,所以初始值为0b00000001,每次进行"<<=1"都会向左移动一位,最后直至bits–变为0,得到bitval变为0b11111111=255(0xFF),最后构造出了掩码mask为255,最后我们要输出的是nums[i]^mask,输出时进行了异或,在for循环中将数组中的每个数字都和mask进行了异或,当我们和1做异或的时候,我们的编码上的位数会进行翻转,之后再输出中,我们输出的是%c,则ASCLL编码会转换为字符,最后i的最大值为size-1,所以最后if的中的条件成立,输出换行符,代码运行结束所以最后进行翻转在转换字符生成的字符为 XiyouLinuxGroup 5.宏召唤术:括号去哪了 # define SQUARE ( x) x* x# define MAX ( a, b) ( ( a) > ( b) ? ( a) : ( b) ) int main ( ) { int i= 3 ; printf ( "%d\n" , SQUARE ( i+ 1 ) ) ; printf ( "%d\n" , MAX ( i, 5 ) ) ; printf ( "%d\n" , MAX ( i++ , 5 ) ) ; printf ( "%d\n" , i) ; } 第一个输出中进行展开,得到的算式为i+1*i+1=3+3+1=7,因为宏参数中没有进行括号保护,乘法是双目运算符,加减法是单目运算符,所以先进行乘法运算,所以输出7,第二个输出中,是三目运算符,i为3,是小于5的,所以结果为假,所以输出的是’:‘后面的(b),是5,第三个输出中,三目运算符为((i++)>(5)?(i++):(5)),i++是后置自增,所以刚开始是3和5比较,结果为假,i变为4,后面输出’:'后面的5,所以输出的为5,最后一个输出,在第三个输出中i变为4,所以输出4, 所以最后的输出为7,5,5,4 6.p与q的步幅之争 int main ( ) { int a[ 4 ] = { 1 , 2 , 3 , 4 } ; int * p= a; int ( * q) [ 4 ] = & a; printf ( "%td %td\n" , ( char * ) ( p+ 1 ) - ( char * ) p, ( char * ) ( q+ 1 ) - ( char * ) q) ; printf ( "%d %d\n" , * ( p+ 2 ) , * ( * q+ 2 ) ) ; } 第一个输出中 (p+1)会移动一个int变量,则为四个字节,所以第一个输出的4,q是一个数组指针,(q+1)则会跨过一整个数组,是4*4=16个字节,所以输出16, 第二个输出中,*(p+2)就等于a[2],所以输出的是3, *( *q+2)相等于( *a[0]+2)等于a[2],也输出3, 所以最后输出的是 4 16 3 3 7.记性特别好的x int visit ( int n) { static int x= 0 ; if ( n== 0 ) return x; x+= n; printf ( "before %d %d\n" , n, x) ; int result= visit ( -- n) ; printf ( "after %d %d %d\n" , n, x, result) ; return x+ result; } int main ( ) { printf ( "first=%d\n" , visit ( 3 ) ) ; printf ( "second=%d\n" , visit ( 2 ) ) ; } static修饰了x,所以x只会在刚开始的时候初始化一次,后面都不会初始化,在主函数中,第一个输出中,先令n为3.后面执行x+=n,使x变为3,然后输出,后面n–,变为visit(2),所以在从开始进行输出,x变为2+3=5,然后进行输出,n再减一,变为visit(1),再返回输出,x变为了6,再输出,n变为了0,返回执行,if条件成立,返回x,这个时候x为6,n为0,result为visit(0),得出visit初始值变为了6,所以第一次输出的时候为0 6 6,后面再一层一层往上递归,后面返回x+result,n加一,得出result变为6+6=12,最后n回到2,result变为12+6=18,返回值为18+6=24,得出visit(3)=24,同理可得visit(2)也是同样的,但是a的初始值为6,不断进行x+=n,最终x变为9,n变为0,再进行递归,n不断加加,result不断加x,最终n返回1,a的值为9,result变为18,返回18+9=27, 所以最终的输出为 before 3 3 before 2 5 before 1 6 after 0 6 6 after 1 6 12 after 2 6 18 first=24 before 2 8 before 1 9 after 0 9 9 after 1 9 18 second=27 8.谁能动const大神的奶酪 struct p { int x; const int y} ; int main ( ) { struct p p1= { 11 , 22 } , p2= { 33 , 44 } ; const struct p p3= { 55 , 66 } ; struct p * const ptr1= & p1; const struct p * ptr2= & p2; const struct p * const ptr3= & p3; ptr1-> x= 111 ptr2-> x= 333 ; ptr3-> x= 555 ; ptr1-> y= 222 ; ptr1= & p2; ptr2-> y= 444 ; ptr2= & p1; ptr3-> y= 666 ; ptr3= & p1; } const是修饰变量的,被他修饰的变量是不可以被修改的,而const有一个原则,谁离const最近谁就会被const修饰, 第一个const离ptr1最近,所以ptr1本身不可以被修改, 第二个,离struct最近,所以我们的指向对象&p2是不可以被修改的, 第三个中有两个const,所以ptr3本身和指向对象都不可以被修改, const又修饰变量y,所以y不能被修改, 下列的9个操作中,只要有y和ptr3的便是不合法的,所以最后只有ptr1->x=111和ptr2=&p1是成立的 9.当函数变成了数字 typedef int ( * Bin0p) ( int , int ) ; int add ( int a, int b) { return a+ b; } int sub ( int a, int b) { return a- b; } int mul ( int a, int b) { return a* b; } Bin0p ops[ 3 ] = { add, sub, mul} ; int ( * get_op ( char c) ) ( int , int ) { switch ( c) { case '+' : return add; case '-' : return sub; default : return mul; } } int main ( ) { printf ( "%d\n" , get_op ( '*' ) ( 7 , - 2 ) ) ; printf ( "%d\n" , ops[ 1 ] ( INT_MIN, 20 ) ) ; printf ( "%d\n" , get_op ( '+' ) ( 0xFFFFFFFF , 2027 ) ) ; } 在主函数中,第一个输出中的get_op函数中,没有*号所以,进入default,返回Mul,在mul里是a *b,所以输出7 *-2=14,第二个输出中ops[1]在数组中就是sub函数。因为数组的下标是从零开始的,所以最后是要输出的是sub(INT_MIN,20),sub函数是返回a-b,所以最后输出INT_MIN-20,但是INT_MIN代表的含义为int类型中的最小数,再进行相减会出错,第三个输出中0xFFFFFFFF是unsigned int类型的,进行隐式转换,则在int类型中的值为-1,所以最后要输出的为(-1,2027),输出的函数为get_op(‘+’),则返回的是add函数,返回的就是a+b,所以最后输出的为-1+2027=2026, 但是这个代码写的并不表准,因为函数指针是不能进行加减乘除的 10.字节战队排排站:大端,小端与内存对齐 struct data_box { int num; union { unsigned int u32_val; unsigned char bytes[ 4 ] ; char string[ 32 ] ; } un; short tag; long long magic; int buf[ 4 ] ; } ; int main ( ) { int arr[ ] = { 0x00000000 , 0x6F796958 , 0x694C2075 , 0x2078756E , 0x756F7247 , 0x00000070 , 0x44556677 , 0x8899aabb } ; printf ( "%s\n" , ( ( struct data_box * ) arr) -> un. string) ; printf ( "byte0=0x%02X\n" , ( ( struct data_box * ) arr) -> un. bytes[ 0 ] ) ; printf ( "byte1=0x%02X\n" , ( ( struct data_box * ) arr) -> un. bytes[ 1 ] ) ; printf ( "byte2=0x%02X\n" , ( ( struct data_box * ) arr) -> un. bytes[ 2 ] ) ; printf ( "byte3=0x%02X\n" , ( ( struct data_box * ) arr) -> un. bytes[ 3 ] ) ; } 打端:数字高字节存低地址,网络传输使用大端 小端:低字节存地内存地址 union是联合体,所有成员共享一块内存,他的大小取决于他族内成员最大的大小,内存对其则是每个成员的起始位置必须是他的大小的整数倍,他的总体大小必须是他内部最大成员的整数倍,原理则是CPU访问内存的时候,不是单个字节进行读取的,而是一块一块读取的,如果没有对奇,则需要再读一次内存,会使速度变慢 在输出中,((struct data_box*)arr)强制把 int arr[]首地址转换为结构体指针,而arr[]的首地址则指向num,所以第二个0x6F796958才是联合体的起始位置,因为这个代码是小端,低地址存地字节,且地址的存放顺序为从小到大,所以un.bytes[0]为0x58, un.bytes[1]为0x69, un.bytes[2]为0x79, un.bytes[3]为0x6F,0x代表16进制,所以要先将这些转换为十进制,再进行拼接。最后输出Xiyou Linux Group 而后面的四个输出为 byte0=0x58 byte1=0x69 byte2=0x79 byte3=0x6F