总结几道C语言经典面试笔试题,涉及联合体,位段,大小端等知识点,有兴趣的可以刷刷。
1.联合体大小问题
#include <stdio.h> union Un { short s[7]; int n; }; int main() { printf("%d\n", sizeof(union Un)); return 0; }
联合体大小至少是最大成员大小,而最大成员大小是 short s[7],14字节。
而联合体大小又要求内存对齐,所以要求是最大对齐数4的倍数。所以浪费两个字节
最终大小为16.
结构体向int对齐,7个short一共是14字节,对齐后是16字节。n是单独的4字节,由于是union,所以n与s共用空间,只取最长的元素,故占用16字节。
2.联合体+大小端问题
在X86下,小端字节序存储,有下列程序
#include<stdio.h> int main() { union { short k; char i[2]; }*s, a; s = &a; s->i[0] = 0x39; s->i[1] = 0x38; printf(“%x\n”,a.k); return 0; }
输出结果是(? )
A.3839
B.3938
C.380039
D.不确定
结果:
分析:
3.形参与实参关系+内存开辟问题
关于下面代码描述正确的是( ?)
void GetMemory(char *p) { p = (char *)malloc(100); } void Test(void) { char *str = NULL; GetMemory(str); strcpy(str, "hello world"); printf(str); }
A.上面代码没问题
B.上面代码存在内存泄露
C.上面代码可能会崩溃,即使GetMemory函数返回,str依然为NULL
D.GetMemory函数无法把malloc开辟的100个字节带回来
4.位段使用问题
关于下面答案正确的是( ?)
int main() { unsigned char puc[4]; struct tagPIM { unsigned char ucPim1; unsigned char ucData0 : 1; unsigned char ucData1 : 2; unsigned char ucData2 : 3; }*pstPimData; pstPimData = (struct tagPIM*)puc; memset(puc,0,4); pstPimData->ucPim1 = 2; pstPimData->ucData0 = 3; pstPimData->ucData1 = 4; pstPimData->ucData2 = 5; printf("%02x %02x %02x %02x\n",puc[0], puc[1], puc[2], puc[3]); return 0; }
A.02 03 04 05
B.02 29 00 00
C.02 25 00 00
D.02 29 04 00
puc是一个char数组,每次跳转一个字节,结构体不是,它只有第一个元素单独享用一字节,其他三个元素一起共用一字节,所以puc被结构体填充后,本身只有两个字节会被写入,后两个字节肯定是0,至此AD排除,然后第一个字节是2就是2了,第二个字节比较麻烦,首先ucData0给了3其实是越界了,1位的数字只能是0或1,所以11截断后只有1,同理ucData1给的4也是越界的,100截断后是00,只有5的101是正常的。填充序列是类似小端的低地址在低位,所以排列顺序是00 101 00 1。也就是0010 1001,即0x29,故选B。
5.返回栈空间地址问题
关于下面代码描述正确的是(? )
char *GetMemory(void) { char p[] = "hello world"; return p; } void Test(void) { char *str = NULL; str = GetMemory(); printf(str); }
A.printf函数使用有问题
B.程序正常打印hello world
C.GetMemory函数返回的地址无法正常使用
D.程序存在内存泄露
A.printf函数正常使用
B.程序不一定能打印出来hello world
C是正确的,.GetMemory函数返回的地址赋给str,然后打印这块地址的内容,但要注意局部变量的生命域是在函数内部的,当出了函数,变量就不存在了,那块空间就销毁了,不再受p指针维护。
而str仍然去访问这块空间,就会出问题
D没有动态申请空间,哪里的内存泄漏呢?
说明:此题考的是“局部变量的指针不能做返回值,因为函数内的空间在函数返回后就会释放调”这一点。
6.找单身狗问题1
int Find1(int arr[],int sz) { int num = 0; int i = 0; for (i = 0; i < sz; i++)//相同的数异或为0 异或的特点是相同为0,相异为1 { num ^= arr[i];// 1^2^3^4^5^1^2^3^4=5 } return num; } int main() { int arr[] = { 1,2,3,4,5,1,2,3,4 }; //找只出现1次的单身狗 int sz = sizeof(arr) / sizeof(arr[0]);//计算数组大小 int ret=Find1(arr,sz);//查找 printf("%d", ret); return 0; }
解题关键:利用异或特点:相同的数异或为0
7.找单身狗问题2
思路:
找出一个只出现过一次的数字的问题处理方法就是找一个数字把里面所有的数字都异或一遍,利用异或两次等于没异或的特点来处理。那么如果有两个数字都只出现了一次,那么如此得到的应该是两个数异或的结果。首先这个结果肯定不是0(要不然就全都配对了),所以里面一定至少一位是一。找出值为1的一位,以这一位的值将结果分为两组。例如1 2 3 4 1 2,异或完的结果应该是3^4得到的111,那么随便找一位就行了。例如找最低位,那么这一位是1的有1 3 1,是0的有2 4 2,由于是利用异或结果为1的某一位分的组,所以两个待查询数字一定分别在两组中。所以再找两个变量,分别异或两组数,即可找到这两个数。
void Find_single_dog(int arr[], int sz,int single_dog[2]) { int ret = 0; int i; for (i = 0; i < sz; i++) { ret ^= arr[i]; } //再找到ret哪位为1,根据1来分类 int pos = 0;//记录哪个二进制位为1 for (i = 0; i < 32; i++) { if (((ret >> i) & 1) == 1) { pos = i; break; } } int num1 = 0; int num2 = 0; //分类,要将两个单身狗分到不同的组里 for (i = 0; i < sz; i++) { if (((arr[i] >> pos) & 1) == 1) { num1 ^= arr[i]; } else { num2 ^= arr[i]; } } single_dog[0] = num1; single_dog[1] = num2; } int main() { int arr[] = { 1,2,3,4,5,6,1,2,3,4 }; //有两个数只出现1次,其他数都出现2次,找到这两个数 //我们可以将这两个数分到两个组里。 //根据异或,将全部都异或,最后的结果就是两个数异或的结果,异或的结果肯定不为0。那二进制位上肯定有1 //而出现1是因为这两个数在相同的二进制位上不同而产生的,所以我们可以根据低位为1来区分两个数 //肯定是一个数是1而另一个数该位为0 //1.首先全部异或 int sz = sizeof(arr) / sizeof(arr[0]); int single_dog[2] = { 0 }; Find_single_dog(arr, sz,single_dog); printf("%d %d", single_dog[0],single_dog[1]); return 0; }
结果: