C语言经典面试笔试题

简介: 结构体向int对齐,7个short一共是14字节,对齐后是16字节。n是单独的4字节,由于是union,所以n与s共用空间,只取最长的元素,故占用16字节。

总结几道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.不确定


结果:


0175d004bb2949d7917c8bbb18f32908.png


分析:


c2e05171597f413d84b29e42506f609c.png


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个字节带回来


2d8f87476a1040cf8e31bc9fbc79d6ec.png


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


416a2d8c9cd14ea99dd4a4dd9b1b3478.png


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


a14bfdce4563469ea22bf2b7ba7f16e9.png


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


f3bd264ded894217bb56195d5dfe9a47.png


思路:


找出一个只出现过一次的数字的问题处理方法就是找一个数字把里面所有的数字都异或一遍,利用异或两次等于没异或的特点来处理。那么如果有两个数字都只出现了一次,那么如此得到的应该是两个数异或的结果。首先这个结果肯定不是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;
}


结果:


b933f465c0b14427981a1fbcf5b0d1b1.png

相关文章
|
23天前
|
存储 安全 编译器
C语言面试题1-10
指针声明后立即初始化。 内存释放后将指针置为NULL。 避免越界访问。 10. 一个指针变量占几个字节? 一个指针变量的大小与系统和编译器相关。在32位系统中,指针变量占4个字节;在64位系统中,指针变量占8个字节。 通过深入了解以上问题,能够更好地掌握C语言内存管理的核心概念,提高编写高效、安全代码的能力。
23 1
|
2月前
|
存储 编译器 C语言
从C语言到C++_34(C++11_下)可变参数+ lambda+function+bind+笔试题(下)
从C语言到C++_34(C++11_下)可变参数+ lambda+function+bind+笔试题
37 5
|
21天前
|
程序员 C语言 C++
【C语言基础】:动态内存管理(含经典笔试题分析)-2
【C语言基础】:动态内存管理(含经典笔试题分析)
|
21天前
|
程序员 编译器 C语言
【C语言基础】:动态内存管理(含经典笔试题分析)-1
【C语言基础】:动态内存管理(含经典笔试题分析)
|
23天前
|
存储 安全 编译器
C语言面试题11至20题
在C语言中,可以使用以下方式实现循环: for循环:用于确定次数的循环。 for (int i = 0; i < 10; i++) { // 循环体 } while循环:用于条件控制的循环。 while (condition) { // 循环体 } do-while循环:至少执行一次的条件循环。 do { // 循环体 } while (condition); 通过深入理解这些面试题,可以更好地准备编程面试,展示对编程原理和技术细节的深刻掌握。
18 3
|
23天前
|
存储 缓存 C语言
C语言面试题30至39题
. 用变量a给出下面的定义 由于题目未明确定义,这里给出几个常见定义: 整数变量:int a; 字符变量:char a; 浮点变量:float a; 双精度浮点变量:double a; 指针变量:int *a; 通过理解和掌握这些面试题,可以更好地准备编程面试,展示对编程原理和技术细节的深刻掌握。
19 2
|
2月前
|
存储 算法 C语言
从C语言到C++_39(C++笔试面试题)next_permutation刷力扣
从C语言到C++_39(C++笔试面试题)next_permutation刷力扣
18 5
|
2月前
|
算法 编译器 C语言
从C语言到C++_34(C++11_下)可变参数+ lambda+function+bind+笔试题(中)
从C语言到C++_34(C++11_下)可变参数+ lambda+function+bind+笔试题
28 2
|
2月前
|
存储 C语言 Python
从C语言到C++_24(二叉搜索树)概念+完整代码实现+笔试题(下)
从C语言到C++_24(二叉搜索树)概念+完整代码实现+笔试题
47 3
|
2月前
|
算法 编译器 C语言
从C语言到C++_34(C++11_下)可变参数+ lambda+function+bind+笔试题(上)
从C语言到C++_34(C++11_下)可变参数+ lambda+function+bind+笔试题
17 1