面试题浅解

2024-01-19 18:59
文章标签 面试题 浅解

本文主要是介绍面试题浅解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        这里是笔试题的链接,大家可以下载下来然后对照着看看~ http://pan.baidu.com/s/1kTG99HH (以下使用到的程序均在X86_32位机器下进行的。)
1.题 考查隐式数据类型转换 , C 默认的是将需要自动转换的从低级转换为高级具体情况如下:
                        
那么什么时候会发生隐式数据类型转换呢 , 主要有下列的情况 ~:
a>算数运算式中低类型能转换为高类型.
b>复制表达式,右边表达式的值自动转换为左边变量的类型并赋值给它.
c>函数调用中参数传递时,系统隐式地将实参转换为形参类型,并赋值给形参.
d>函数有返回值时, 系统将隐式地返回值表达式类型转换为返回值类型,复制给调用函数.

注:不同的数据类型的数据进行操作时,应该先将其转换成相同的数据类型,然后进行操作,转换规则是由低级转换为高级.
2. 考查sizeof 计算结构和联合所占的字节数
3.考查位运算
看到题目,开始是让求平均数, 我们可以很简单地进行相加除以2,这里却用 a + (b-a)/2 这样做的好处就是防止相加的时候两个数过大, 上溢出.关于下面的功能(当然,需要给后面整体加上括号,符号优先级问题).关于这个为什么这样写, (a&b) + ((a^b)>>1) , 大家知道右移1位 , 相当于除以二. 但是 a&b和a^b是什么意思呢. 我们先来看一个用位运算时现任何两个数的加法的操作.
 :
        首先一位的加法(不产生进位的情况), 用^处理就好了.  关于处理进位情况,当两个位都为1的时候,产生进位. 那么这时就可以用&操作, 保留所有需要进位的位 , 然后对结果进行一次左移, 我们知道左移就相当于进位了. 然后其它位的处理,就使用^进行, 我们把操作分为了两部分, 一部分用来处理进位情况, 一部分用来处理不进位的那些位. 然后我们进行迭代. 直到不产生进位的时候, 也就是& <<1 操作的值为0 的时候. 代码附一下 :
  
01 #include <stdio.h>
02 
03 int cal (int a , int  b) {
04 
05     return  b ? cal ((a  ^  b) , (a & b)<<1) : a ;
06 }
07 
08 int main(int argc, char *argv[])
09 {
10     int a  ,  c ;
11 
12     scanf ("%d%d",&a , &c) ;
13 
14     printf ("val = %d\n" , cal (a , c) );
15 
16     return 0;
17 }
这是一个迭代的操作, 所以我们可以使用递归来完成 . 同样的想实现减法, 给b取非 +1, 很简单就完成了.
然后大家再看这个题 前面那个a&b同样的是判断有没有进位, 如果有进位,那么就保留那一位,因为要除以二,所以不用向左平移 , 后面是处理不用进位的部分 , 同样的需要除以二 , 用向右移位完成. 好啦 ~ 就是这个思路. 至于乘法 , 除法 ,我就没有写了.大家可以研究研究.
4.考查sizeof与strlen的区别,大家应该都知道了. sizeof 在编译后就已经没有了, 已经被计算出来的值替换了. 而strlen则是运行时计算的.sizeof计算的是后面对应的数据类型的字节大小,值得一提的是数组是一个构造数据类型,它是一个整体.应该以整体计算.可以看一个例子.
  
01 #include <stdio.h>
02 
03 int main( )
04 {
05     int a=3 , b  ;
06 
07     b = a + sizeof (a) ;
08 
09     return 0;
10 }
看一下反汇编以后的代码:
080483f0 <main>:80483f0:	55                   	push   ebp80483f1:	89 e5                	mov    ebp,esp80483f3:	83 ec 10             	sub    esp,0x1080483f6:	c7 45 fc 03 00 00 00 	mov    DWORD PTR [ebp-0x4],0x380483fd:	8b 45 fc             	mov    eax,DWORD PTR [ebp-0x4]8048400:	83 c0 04             	add    eax,0x48048403:	89 45 f8             	mov    DWORD PTR [ebp-0x8],eax8048406:	b8 00 00 00 00       	mov    eax,0x0804840b:	c9                   	leave  804840c:	c3                   	ret    804840d:	66 90                	xchg   ax,ax804840f:	90                   	nop

可以看到,add后面的源操作数直接就是4了.
5.考查位域
C 不允许每个位域成员超过基类型的大小.使用sizeof计算位域的大小,将返回基类型的大小,即字节对其以后的大小.关于位域,这里有很多需要注意的点,我在这一一列举一下:
1>.linux下位域可以横跨两个字节,即一个位域可以超过8bit , 但是不能超过自己的类型的sizeof.
2>.不允许对位域成员进行取地址.
3>.如果相邻字段位域类型相同,且前后加起来的大小不超过sizeof类型的大小 , 则后面字段将紧临前一字段存储.
4>.如果相邻字段类型相同但是前后加起来大于类型的sizeof大小,则另起炉灶.
5>.相邻字段间的类型不同,则不同的编译器处理不同.
6>.整个结构体大小为最宽的类型的大小的整数倍.
当然 , 这里说的这些也不能完全概括,之后的需要大家去补充。 关于上面的字节大小, 大家可以自己去验证。
下面再给大家加一点点东西,比特序(wiki给的定义):
In  computing bit numbering  (or sometimes  bit endianness ) is the convention used to identify the  bit  positions in a  binary number  or a container for such a value. The bit number starts with zero and is incremented by one for each subsequent bit position.
大概意思和little-edian和big-endian一样, 不过前者是针对于字节为单位的,现在则讨论的是以bit为单位的。先来看看这样的一道题:
   
 1 #include <iostream>
 2 #include <cstring>
 using namespace std;
 struct A
 5 {
 6     int a:5;
 7     int b:3;
 8 };
 int main(void)
10 {
11     char str[100] = "012345";
12     struct A c;
13     memcpy(&c, str, sizeof(A));
14     cout << c.a << endl;
15     cout << c.b << endl;
16     return 0;
17 }
猜猜结果会是怎么样,我给大家画一下(字符0对应的ASCII 值为48 也就是00110000)

自己敲一下上面的代码,应该可以得到结果 ,然后小小猜一下,也差不多出来了。
这里就涉及到LSB(least signficant bit) , MSB(most signficant bit),小端的CPU 一般采用的都是LSB  0位序 。大端的cpu可能采取LSB也可能采取MSB 。 (关于大端小端,下面会讲)
其实LSB也就是一个“高高低低”的的原则,即数据的最低位放在字节的第0位,从又至左一次递增,所以后面5位是a的数据,前面3位是b的数据,这样就可以解释结果(-16    1)了。所以MSB就和LSB相反喽 。好了,就扯这么多,详细的大家参考这个链接 http://en.wikipedia.org/wiki/Bit_numbering和这位前辈的博客 http://blog.chinaunix.net/uid-25909722-id-2749575.html(注意自己去考证里面的内容哦!实践是检验真理的唯一标准哦。)

6.考查while , do-while 这个 , 就不用多讲了吧 ~ , 相信大家都狠狠懂。
7.考查逗号表达式和switch语句特点,这道题详细的可以参考C99ISO文档 (135页), 里面有详细的说明,我就不赘述了。简单说说吧。
首先逗号表达式大家应该狠是清楚吧, 整个表达式的值是最后一个子表达式的值。而且switch后面的表达式必须是interger类型。 case标签必须是constant expression , 也会发生整型提升。在switch的域内 ,case标签外部的语句,均为无效语句,若是有定义语句,是允许的 , 如果是数据定义,则无论其初始化与否,其值在未重新赋值之前,都是未定义的。(GCC允许在switch中定义函数,clang则不允许,这个行为应该是未定义吧, 由编译器实现决定。尽量避免这样做。)

8.这个题就不用细讲了吧 , 记住这个就行了:a[1] = *(a+1) = *(1+a) = 1[a] ;数组元素的定位也是通过指针实现的。
9.考查数组和指针的联系。
10.考查float和double的精度问题。
都知道的是float的精度小于double的精度,很简单么,double占得字节多呗! 
默认的float小数点后最多有7位有效数字,但是能保证精度准确的只有前6位, 从第7位开始就是不确定的了。但是double就不一样了,double默认的小数点后可以有16位,但是能保证精度的只有前15位,所以,拿一个float与一个字面值常量进行比较,当然得不到你想要的结果。因为字面值常量在C里面是做为double类型来处理的。这样去比较是肯定会出问题的。所以我们比较两个数的时候最好做到类型统一,减少隐式转换带来的副作用。
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
  
int  main () {
  
         float  PI = 3.14000000000000000;
          
         printf  ( "PI =  %.10f \n"  , PI) ;
         printf  ( "double PI = %.10lf \n"  , 3.14) ;
  
         if  (PI == 3.14) {
                 printf  ( "That's impossible !\n" ) ;
         }
  
         return  0 ;
}

[tutu@localhost 面试]$ ./a.out PI = 3.1400001049 double PI = 3.1400000000 [tutu@localhost 面试]$

结果很明显了
11题考查系统栈的一些知识, 首先我们应该知道,局部变量的值是存储在栈里面的。而且栈是向下增长的。看一下反汇编以后的代码~ , (推荐大家去读一下刘欢学长的 浅谈缓冲区溢出之栈溢出 ) :

然后内存中的占空间图如下:

 
关于i为什么分配在数组之前,我测试过放在数组后定义,但是汇编代码是一样的.在崔娇娇学姐那里却不对了,i分配的时候分在了数组的下面.呵呵, 这个就有些让人费解了,找原因,最后看看gcc版本不同. 还有,在64位gcc编译的时候,不会出现这样的溢出.详细的有兴趣大家去研究研究吧~~~ .

12题考查的还是缓冲区问题,但是这个是输入缓冲区
gets是有缓冲的函数,意思是输入会先保存到一个buffer中,当buffer满或者强制刷新的时候(如:换行,fclose操作),buffer会刷新.
缓冲分为:全缓冲,行缓冲,无缓冲 .分别举例说一下吧.
全缓冲:
指的是系统在填满标准IO缓冲区之后才进行实际的IO操作;注意,对于驻留在磁盘上的文件来说通常是由标准IO库实施全缓冲。
行缓冲:
标准IO在输入和输出中遇到换行符时执行IO操作;注意,当流涉及终端的时候,通常使用的是行缓冲。
无缓冲:
无缓冲指的是标准IO库不对字符进行缓冲存储;注意,标准出错流stderr通常是无缓冲的。
我们可以分别实验一下:(摘UNIX环境高级编程8章)
    #include <stdio.h>  #include <string.h>  #include <unistd.h>  char buf[] = "Let's start !\n";  int main()  {    pid_t pid;  if(write(STDOUT_FILENO, buf, strlen(buf)) != strlen(buf))  {  fprintf(stderr, "write error");  return 0;  }  printf("before fork()...\n");  if((pid = fork()) == -1)  {  fprintf(stderr, "fork error");  return 0;  }      sleep(2);  printf("parent process id:%d ", getppid());  printf("process id:%d\n", getpid());  return 0;  } 
直接运行的结果和运行时重定向,结果是有区别的, 这个例子让你很好理解行缓冲和全缓冲.当我们的程序有交互的时候,e.g.和终端交互,缓冲类型为行缓冲.来看一下下输出:
tutu@localhost 面试]$ ./a.out
Let's start !
before fork()...
parent process id:2004 process id:2991
parent process id:2991 process id:2992
[tutu@localhost 面试]$ 
我们再用重定向到文件看看:
[tutu@localhost 面试]$ ./a.out > test.txt
[tutu@localhost 面试]$ vim test.txt
Let's start !
before fork()...
parent process id:2004 process id:2996
before fork()...
parent process id:2996 process id:2997
[tutu@localhost 面试]$
这样就多了一次before fork () ... ,  没有和终端交互,那么就是全缓冲. 也看到了write 是无缓冲的(就暂时这样去理解),它直接写到对应的流中,如输出流.
还有要注意的时gets函数,绝对不推荐大家使用,因为这货不对输入长度检查,输入多少就往栈里尽可能扔多少,完全不估计溢出的情况,所以就是这货引发了1988年一次规模比较大的蠕虫病毒.原因什么的,大家有兴趣可以自己研究,欢学长的博客建议大家一定要看看.以后就赶快扔了gets吧,全部使用fgets (char *s , int size , FILE *stream) .

13.就不用讲了, 大家都会的 . 排序重组么.
14.考查static 的作用, 首先应该知道的是static 变量保存在全局静态区 , 是全局静态区 , 生存期整个程序 , 全局静态变量作用域本文件.局部的那就只能在局部.需要注意的是static 变量初始化的时候必须以常量或者常量表达式.不初始化时,编译器给你自动初始化为0 .
       题目中的初始化方式不对, 由于逗号表达式的值是不定的,并非常量或者常量的表达式.

15. 考查大端小端,实际上就是数据在内存中的存放顺序. 不同的处理器生产商有自己不同的设计. 可以参见这位大神的 ce123的博客,很经典的说.我在这里也是retell.
一般小端的机器有:x86 DEC
大端机器有:POWER PC , IBM , SUN
可能是大端也可能是小端:ARM MAC
现代的CPU都可以大端小端共存的 , 通过跳线来对不同的情况作出处理.  现在我们来大概看一下自己的pc是哪种endian .
#include <stdio.h>int main(int argc, char *argv[])
{int a = 0x12345678 ; char * p = (char *) &a ;printf ("%x\n" , *p) ;printf ("%x\n" , *(p+1)) ;printf ("%x\n" , *(p+2)) ;printf ("%x\n" , *(p+3)) ;return 0;
}
方法很多,这样只是简便一点吧 . 看一下结果 :
[tutu@localhost 面试]$ ./a.out 
78
56
34
12
[tutu@localhost 面试]$ 
简单画一下 :

可以看到高位自己放在了高地址, 低位字节放在了低地址.这是典型的高高低低.我们这里的单位是字节即8bit为atomic elements.
对应大端呢, 就是倒过来了.

JAVA和网络传输都使用的大端, 所以,我们在做网络相关的开发时, 总是需要这几个函数:htonl(), htons(), ntohs(),ntohl() , 就是用来转换字节序的.详细的大家自己去google吧.

16题考查C预处理都做了什么即 , 宏替换在先还是去除注释在先 .这个在C99文档里(147页左右)有相关说明.这里我大概说一下这里有前辈的链接 C预处理的步骤
1.三连符替换成相应的单字符
2.把用 \字符续行的多行代码接成一行比如
#define STR "hello, "\"world"
替换成一行
3.把注释(不管是单行注释还是多行注释)都替换成一个空格。
4.处理Token(记号)和空白字符 (即分割)
5.宏展开,包含源文件
...
后面的大家去研究吧,更细的东西在链接里.大家有兴趣去看看吧.

17题考查枚举常量,这个相信大家都会了 , 就不多讲了.
18题又考查sizeof , 这个上面已经给大家将了,可以参照上面的方法.做一下,一目了然.
19.纯粹时考查你的细心程度. 当然这也告诉大家 , 注意自己的代码风格,格式.别到最后自己都看不懂自己的代码了.
21题 我相信大家一定会的.
20题23题24题25题都是编程题目, 这个我觉得要是我讲的画, 你的思路就会被我的想法占据. 我也相信大家都会的. 这里只简单说一下.
int my_strcmp (const char * dest ,const char * source ) {if ( NULL == dest || NULL == source ) {printf ("error\n") ;exit (-1) ;}while (( 0 != *dest ) && ( 0 != *source )) {if (tolower(*dest) == tolower(*source)) {dest++ ;source++ ;}else break ;}return *dest - *source ;}
思路比较简单.实现的也很简单.
23题  用两个变量(max , less_max)分别保存最大和次大,假定第一个节点最大,赋值给max , 并初始化less_max为INT_MIN , 每次向前比较, 发现比当前最大值大 , 将max更新,并同时更新less_max为max之前值, 当发现当前值比max小,这时也要比较是否比less_max大,如果大,则更新less_max.嗯 , 思路就是这样.
24.字符串匹配问题,有库函数可以调的 char * strstr(const char * , const char *),返回第一次出现子串的地址. 这里让大家手动实现,有两种方法, BF , KMP , 这是两种不同的思想.BF 是暴力去匹配, 也是我们平时用的多的. KMP则是使用一定的算法(呜呜, 有点难理解) . 这个有小组张续学长的博客在,大家上去可以仔细研究. KMP详细过程

25.实现一个范型的swap , 方法很多, 我这里直接调的库函数
void 	swap (void * dest , void * source , int size ) {void * p = malloc (size) ;memcpy (p , dest , size) ;memcpy (dest , source , size) ;memcpy (source , p , size) ;free (p) ;
}




这篇关于面试题浅解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/623374

相关文章

荣耀嵌入式面试题及参考答案

在项目中是否有使用过实时操作系统? 在我参与的项目中,有使用过实时操作系统。实时操作系统(RTOS)在对时间要求严格的应用场景中具有重要作用。我曾参与的一个工业自动化控制项目就采用了实时操作系统。在这个项目中,需要对多个传感器的数据进行实时采集和处理,并根据采集到的数据及时控制执行机构的动作。实时操作系统能够提供确定性的响应时间,确保关键任务在规定的时间内完成。 使用实时操作系统的

一些其他面试题

阿里二面:那你来说说定时任务?单机、分布式、调度框架下的定时任务实现是怎么完成的?懵了。。_哔哩哔哩_bilibili 1.定时算法 累加,第二层每一个格子是第一层的总时间400 ms= 20 * 20ms 2.MQ消息丢失 阿里二面:高并发场景下引进消息队列有什么问题?如何保证消息只被消费一次?真是捏了一把汗。。_哔哩哔哩_bilibili 发送消息失败

zookeeper相关面试题

zk的数据同步原理?zk的集群会出现脑裂的问题吗?zk的watch机制实现原理?zk是如何保证一致性的?zk的快速选举leader原理?zk的典型应用场景zk中一个客户端修改了数据之后,其他客户端能够马上获取到最新的数据吗?zk对事物的支持? 1. zk的数据同步原理? zk的数据同步过程中,通过以下三个参数来选择对应的数据同步方式 peerLastZxid:Learner服务器(Follo

java常用面试题-基础知识分享

什么是Java? Java是一种高级编程语言,旨在提供跨平台的解决方案。它是一种面向对象的语言,具有简单、结构化、可移植、可靠、安全等特点。 Java的主要特点是什么? Java的主要特点包括: 简单性:Java的语法相对简单,易于学习和使用。面向对象:Java是一种完全面向对象的语言,支持封装、继承和多态。跨平台性:Java的程序可以在不同的操作系统上运行,称为"Write once,

【Kubernetes】常见面试题汇总(三)

目录 9.简述 Kubernetes 的缺点或当前的不足之处? 10.简述 Kubernetes 相关基础概念? 9.简述 Kubernetes 的缺点或当前的不足之处? Kubernetes 当前存在的缺点(不足)如下: ① 安装过程和配置相对困难复杂; ② 管理服务相对繁琐; ③ 运行和编译需要很多时间; ④ 它比其他替代品更昂贵; ⑤ 对于简单的应用程序来说,可能不

【附答案】C/C++ 最常见50道面试题

文章目录 面试题 1:深入探讨变量的声明与定义的区别面试题 2:编写比较“零值”的`if`语句面试题 3:深入理解`sizeof`与`strlen`的差异面试题 4:解析C与C++中`static`关键字的不同用途面试题 5:比较C语言的`malloc`与C++的`new`面试题 6:实现一个“标准”的`MIN`宏面试题 7:指针是否可以是`volatile`面试题 8:探讨`a`和`&a`

Laravel 面试题

PHP模块 PHP7 和 PHP5 的区别,具体多了哪些新特性? 性能提升了两倍 结合比较运算符 (<=>) 标量类型声明 返回类型声明 try…catch 增加多条件判断,更多 Error 错误可以进行异常处理 匿名类,现在支持通过new class 来实例化一个匿名类,这可以用来替代一些“用后即焚”的完整类定义 …… 了解更多查看文章底部链接 PHP7 新特性 为什么 PHP

【吊打面试官系列-Redis面试题】说说 Redis 哈希槽的概念?

大家好,我是锋哥。今天分享关于 【说说 Redis 哈希槽的概念?】面试题,希望对大家有帮助; 说说 Redis 哈希槽的概念? Redis 集群没有使用一致性 hash,而是引入了哈希槽的概念,Redis 集群有 16384 个哈希槽,每个 key 通过 CRC16 校验后对 16384 取模来决定放置哪个槽, 集群的每个节点负责一部分 hash 槽。

【Kubernetes】常见面试题汇总(一)

目录 1.简述 etcd 及其特点? 2.简述 etcd 适应的场景? 3.简述什么是Kubernetes? 4.简述 Kubernetes和 Docker的关系? 1.简述 etcd 及其特点? (1)etcd 是Core0s 团队发起的开源项目,是一个管理配置信息和服务发现(service discovery)的项目,它的目标是构建一个高可用的分布式键值(keyvalue)数据

2018秋招C/C++面试题总结

博主从8月中旬开始大大小小面试了十几家公司,至今也许是告一段落吧,希望后面会有好结果,因此总结记录一些C/C++方向常见的问题。和大家一起学习! 参考了互联网的各种资源,自己尝试归类整理,谢谢~ 一、C和C++的区别是什么? C是面向过程的语言,C++是在C语言的基础上开发的一种面向对象编程语言,应用广泛。 C中函数不能进行重载,C++函数可以重载 C++在C的基础上增添类,C是一个结构