面试The interview questions and answers.docx
- 文档编号:23976762
- 上传时间:2023-05-23
- 格式:DOCX
- 页数:45
- 大小:35.43KB
面试The interview questions and answers.docx
《面试The interview questions and answers.docx》由会员分享,可在线阅读,更多相关《面试The interview questions and answers.docx(45页珍藏版)》请在冰豆网上搜索。
面试Theinterviewquestionsandanswers
/**c面试题库整理
目的:
提高学员c面试能力
时间:
2013.03整理人:
hejie
**/
/**面试题库修改1:
修改日志:
时间2013.03.14修改人:
hejie
修改内容:
1.调整不合理分类
2.删除部分重复题目
3.添加部分题目,题库更加丰富
4.修改答案剖析,使之更合理,标准
**/
基础部分
关键字
试题1:
关键字const有什么含意?
答案:
(1)可以修饰const常变量
(2)const可以修饰函数的参数、返回值,甚至函数的定义体。
被const修饰的东西都受到强制
试题2:
分析以下代码定义,说明其特性
constinta;
intconsta;
constint*a;
int*consta;
intconst*consta;
答案:
前两个的作用是一样,a是一个常整型数。
第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。
第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。
最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。
思考3:
const修饰的常量与宏的区别
答案:
const常量有数据类型,而宏常量没有数据类型。
编译器可以对前者进行类型安全检查。
而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
试题4:
关键字volatile有什么含意?
并给出三个不同的例子。
答案:
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。
精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。
1)并行设备的硬件寄存器(如:
状态寄存器)
2)一个中断服务子程序中会访问到的非自动变量(Non-automaticvariables)
3)多线程应用中被几个任务共享的变量保护,可以预防意外的变动,能提高程序的健壮性。
试题5:
1)一个参数既可以是const还可以是volatile吗?
解释为什么。
2);一个指针可以是volatile吗?
解释为什么。
3);下面的函数有什么错误:
intsquare(volatileint*ptr)
{
return*ptr**ptr;
}
答案:
1)是的。
一个例子是只读的状态寄存器。
它是volatile因为它可能被意想不到地改变。
它是const因为程序不应该试图去修改它。
2)是的。
尽管这并不很常见。
一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。
3)这段代码有点变态。
这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:
intsquare(volatileint*ptr)
{
inta,b;
a=*ptr;
b=*ptr;
returna*b;
}
由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。
结果,这段代码可能返不是你所期望的平方值!
正确的代码如下:
longsquare(volatileint*ptr)
{
inta;
a=*ptr;
returna*a;
}
试题6.关键字static的作用是什么?
答案:
在C语言中,关键字static有三个明显的作用:
1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2)在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。
它是一个本地的全局变量。
3)在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。
那就是,这个函数被限制在声明它的模块的本地范围内使用。
总结:
static全局变量与普通的全局变量有什么区别:
static全局变量只初使化一次,防止在其他文件单元中被引用;
static局部变量和普通局部变量有什么区别:
static局部变量只被初始化一次,下一次依据上一次结果值;
static函数与普通函数有什么区别:
static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝.
试题7.如何引用一个已经定义过的全局变量?
答案:
可以用引用头文件的方式,也可以用extern关键字,如果用引用头文件方式来引用某个在头文件中声明的全局变理,假定你将那个变写错了,那么在编译期间会报错,如果你用extern方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错。
试题8:
在C++程序中调用被C编译器编译后的函数,为什么要加extern“C”?
例子:
#ifndef__INCvxWorksh
#define__INCvxWorksh
#ifdef__cplusplus
extern"C"{
#endif
/*...*/
#ifdef__cplusplus
}
#endif
#endif/*__INCvxWorksh*/
答案:
由于c语言是没有重载函数的概念的,所以c编译器编译的程序里,所有函数只有函数名对应的入口。
而由于c++语言有重载函数的概念,如果只有函数名对应的入口,则会出现混淆,所以c++编译器编译的程序,应该是函数名+参数类型列表对应到入口。
假设某个函数的原型为:
voidfoo(intx,inty);
该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。
C++提供了C连接交换指定符号extern“C”来解决名字匹配问题。
思考9:
如何判断一段程序是由C编译程序还是由C++编译程序编译的?
(1)如果是要你的代码在编译时发现编译器类型,就判断_cplusplus或_STDC_宏。
#ifdef__cplusplus
cout<<"c++";
#else
cout<<"c";
#endif
如果要判断已经编译的代码的编译类型,就用nm查一下输出函数符号是否和函数名相同。
(2)注意,因为mian函数是整个程序的入口,所以mian是不能有重载的,所以,如果一个程序只有main函数,是无法确认是c还是c++编译器
编译的可以通过nm来查看函数名入口
如一个函数
intfoo(inti,floatj)
c编译的程序通过nm查看
foo 0x567xxxxxx (地址)
c++编译程序,通过nm查看
foo(int,float) 0x567xxxxxx
另外,如果要在c++编译器里使用通过c编译的目标文件,必须通知c++编译器,extern "c" foo;
预处理命令(宏)
试题10:
什么是预编译,何时需要预编译?
答:
就是指程序执行前的一些预处理工作,主要指#表示的.
1)、总是使用不经常改动的大型代码体。
2)、程序由多个模块组成,所有模块都使用一组标准的包含文件和相同的编译选项。
在这种情况下,可以将所有包含文件预编译为一个预编译头。
试题11.说出以下预处理器标识的目的意义
指令用途
#
#include
#define
#undef
#if
#ifdef
#ifndef
#elif
#endif
#error
答案:
指令用途
#空指令,无任何效果
#include包含一个源代码文件
#define定义宏
#undef取消已定义的宏
#if如果给定条件为真,则编译下面代码
#ifdef如果宏已经定义,则编译下面代码
#ifndef如果宏没有定义,则编译下面代码
#elif如果前面的#if给定条件不为真,当前条件为真,则编译下面代码
#endif结束一个#if……#else条件编译块
#error停止编译并显示错误信息编译时检测错误,
使程序员更好的掌握代码-
试题12:
头文件中的ifndef/define/endif干什么用?
答:
防止该头文件被重复引用。
试题13:
#include“filename.h”和#include
答案:
对于#include
对于#include“filename.h”编译器从用户工作路径开始搜索filename.h
试题14:
用预处理指令#define声明一个常数,用以表明1年中有多少秒(忽略闰年问题)
答案:
#defineSECONDS_PER_YEAR(60*60*24*365UL)
剖析:
1)#define语法的基本知识(例如:
不能以分号结束,括号的使用,等等)
2)懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多少秒而不是计算出实际的值,是更清晰而没有代价的。
3)意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。
4)再思考进一步用到UL(表示无符号长整型)
试题15.写一个"标准"宏MIN,这个宏输入两个参数并返回较小的一个。
答案:
#defineMIN(A,B)((A)<=(B)?
(A):
(B))
剖析:
1)标识#define在宏中应用的基本知识。
这是很重要的。
因为在嵌入(inline)操作符变为标准C的一部分之前,
宏是方便产生嵌入代码的唯一方法,对于嵌入式系统来说,为了能达到要求的性能,嵌入代码经常是必须的方法。
2)三重条件操作符的知识。
这个操作符存在C语言中的原因是它使得编译器能产生比if-then-else更优化的代码,了解这个用法是很重要的。
3)懂得在宏中小心地把参数用括号括起来
注:
谨慎地将宏定义中的“参数”和整个宏用用括弧括起来。
所以,严格地讲,下述解答是错误的:
#defineMIN(A,B)(A)<=(B)?
(A):
(B)
#defineMIN(A,B)(A<=B?
A:
B)
#defineMIN(A,B)((A)<=(B)?
(A):
(B));//这个解答在宏定义的后面加“;”
试题16:
#defineMIN(A,B)((A)<=(B)?
(A):
(B))对MIN(*p++,b)的有什么后果?
答案:
((*p++)<=(b)?
(*p++):
(*p++))
这个表达式会产生副作用,指针p会作多次++自增操作。
试题17.分析以下代码,说出输出结果
#defineswap(a,b)a=a+b;b=a-b;a=a-b;
voidmain()
{
intx=5,y=10;
swap(x,y);
printf(“%d%dn”,x,y);
}
答案:
10,5
试题18:
宏定义的多语句错误,分析以宏定义
#defineD(a,b)a+b;\
a++;
分析:
应用时:
if(XXX)
D(a.b);
else
解决办法用do{}while(0)
#defineD(a,b)do{a+b;\
a++;}while(0)
思考while(0)后没有分号
试题19:
分析一下两个定义,哪种方法更好呢?
(如果有的话)为什么?
#definedPSstructs*
typedefstructs*tPS;
分析:
以上两种情况的意图都是要定义dPS和tPS作为一个指向结构s指针。
答案是:
typedef更好。
思考下面的例子:
dPSp1,p2;
tPSp3,p4;
第一个扩展为
structs*p1,p2;
上面的代码定义p1为一个指向结构的指,p2为一个实际的结构,这也许不是你想要的。
第二个例子正确地定义了p3和p4两个指针。
假如定义函数指针:
typedefvoid(*fun)(void);
#defineFUN(x)void(*x)(void)
运算符·表达式·数据类型·优先级
试题20:
分别给出BOOL,int,float,指针变量与“零值”比较的if语句(假设变量名为var)
分析:
BOOL型变量:
if(!
var)
int型变量:
if(var==0)
float型变量:
constfloatEPSINON=0.00001;
if((x>=-EPSINON)&&(x<=EPSINON)
指针变量:
if(var==NULL)
分析:
考查对0值判断的“内功”,BOOL型变量的0判断完全可以写成if(var==0),而int型变量也可以写成if(!
var),
指针变量的判断也可以写成if(!
var),上述写法虽然程序都能正确运行,但是未能清晰地表达程序的意思。
一般的,如果想让if判断一个变量的“真”、“假”,应直接使用if(var)、if(!
var),表明其为“逻辑”判断;如果用if判断一个数值型变量(short、int、long等),
应该用if(var==0),表明是与0进行“数值”上的比较;而判断指针则适宜用if(var==NULL),这是一种很好的编程习惯。
浮点型变量并不精确,所以不可将float变量用“==”或“!
=”与数字比较,应该设法转化成“>=”或“<=”形式。
如果写成if(x==0.0),则判为错
试题21:
嵌入式系统总是要用户对变量或寄存器进行位操作。
给定一个整型变量a,写两段代码,第一个设置a的bit3,第二个清除a的bit3。
在以上两个操作中,要保持其它位不变。
分析:
#defineBIT3(0x1<<3)
staticinta;
voidset_bit3(void)
{
a|=BIT3;
}
voidclear_bit3(void)
{
a&=~BIT3;
}
补充22:
了解可以位操作的另一个知识位域
有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。
例如在存放一个开关量时,只有0和1两种状态,用一位二进位即可。
为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。
所谓“位域”是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。
每个域有一个域名,允许在程序中按域名进行操作。
这样就可以把几个不同的对象用一个字节的二进制位域来表示。
一、位域的定义和位域变量的说明位域定义与结构定义相仿,其形式为:
struct位域结构名{位域列表};其中位域列表的形式为:
类型说明符位域名:
位域长度
例如:
structbs
{
inta:
8;
intb:
2;
intc:
6;
};
位域变量的说明与结构变量说明的方式相同。
可采用先定义后说明,同时定义说明或者直接说明这三种方式。
例如:
structbs
{
inta:
8;
intb:
2;
intc:
6;
}data;
说明data为bs变量,共占两个字节。
其中位域a占8位,位域b占2位,位域c占6位。
对于位域的定义尚有以下几点说明:
一个位域必须存储在同一个字节中,不能跨两个字节。
如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。
也可以有意使某位域从下一单元开始。
例如:
structbs
{
unsigneda:
4
unsigned:
0/*空域*/
unsignedb:
4/*从下一单元开始存放*/
unsignedc:
4
}
在这个位域定义中,a占第一字节的4位,后4位填0表示不使用,b从第二字节开始,占用4位,c占用4位。
由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过8位二进位。
位域可以无位域名,这时它只用来作填充或调整位置。
无名的位域是不能使用的。
例如:
structk
{
inta:
1
int:
2/*该2位不能使用*/
intb:
3
intc:
2
};
从以上分析可以看出,位域在本质上就是一种结构类型,不过其成员是按二进位分配的。
位域的使用位域的使用和结构成员的使用相同,其一般形式为:
位域变量名?
位域名位域允许用各种格式输出。
试题23:
分析以下代码打印输出结果
typedefstruct
{
inta:
2;
intb:
2;
intc:
1;
}test;
testt;
t.a=1;
t.b=3;
t.c=1;
printf("%d",t.a);
printf("%d",t.b);
printf("%d",t.c);
分析:
t.a为01,输出就是1
t.b为11,输出就是-1
t.c为1,输出也是-1
字节对齐
试题31:
typedefunion{longi;intk[5];charc;}DATE;
structdata{intcat;DATEcow;doubledog;}too;
DATEmax;
则语句printf("%d",sizeof(structdate)+sizeof(max));的执行结果是:
______
分析:
编译器自动对齐的原因:
为了提高程序的性能,数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;然而,对齐的内存访问仅需要一次访问
DATE是一个union,变量公用空间.里面最大的变量类型是int[5],占用20个字节.所以它的大小是20
data是一个struct,每个变量分开占用空间.依次为int4+DATE20+double8=32.
所以结果是20+32=52.
当然...在某些16位编辑器下,int可能是2字节,那么结果是int2+DATE10+double8=20
数据类型提升(隐形数据转换)
试题25:
voidfoo(void)
{
unsignedinta=6;
intb=-20;
(a+b>6)?
puts(">6"):
puts("<=6");
}
分析:
这无符号整型问题的答案是输出是">6"。
原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。
因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。
这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。
试题26:
分析以下代码,写出打印结果
main()
{
intx=20,y=35;
x=y+++x++;
y=++y+++x;
printf(“%d%dn”,x,y);
}
答案:
5794
试题27:
分析以下代码的输出结果
inta=1,b=2,c=3
while(a
intt;
t=a;a=b;b=t;c--;
}
printf("%d\n",c)
答案:
为0;a
试题若w=1,x=2,y=3,z=4,则条件表达式w w: y y: z的值是 答案1 能力部分 内存章节 试题28: linux的进程内存分布 分析: 对于linux的进程内存分布,主要是由从小到大的地址空间分布,从低地址到高地址依次是: 文本段(text),数据段,BSS段,堆,栈。 各个区段详细如下: 文本段: 文本段中存放的是代码,只读数据,字符串常量(我们通常说保存在文字常量中,实际就是在文本段) 数据段: 数据段用来存放可执行文件中已经初始化的全局变量,全局变量又可细分为全局变量和程序分配的static静态变量 BSS: BSS段包含了程序中未初始化的全局变量,在内存中全局变量全部初始化为0 堆(heap): 堆主要用来存放进程中动态分配的内存段,其大小不固定,可动态扩张或缩减。 当进程使用malloc等函数分配内存时, 新分配的内存就被动态添加到堆上,相当于堆被扩张。 当利用free等函数释放内存时,被释放的内存被从堆中剔除,相应于堆被缩减 堆的物理内存是由程序申请,并由程序释放 栈: 栈是用户程序存放临时空间的局部变量,也就是我们所说的{}中定义的变量(但不包括static声明的变量,static意味着变量被存储到数据段)。 除此以外,在函数被调用时其参数也被压入发起调用的进程栈中,并且待到调用结束后, 函数的返回值也被压入栈中,由于栈的先进后出原则,所以栈特别方便用来保存或恢复调用现场, 从这个意义上讲,我们可以把堆栈看做一个寄存,交换临时数据的内存区 试题29: 描述内存分配方式以及它们的区别? 分析: 1)从静态存储区域分配。 内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。 例如全局变量,static变量。 2)在栈上创建。 在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。 栈内存分配运算内置于处理器的指令集。 3)从堆上分配,亦称动态内存分配。 程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。 动态内存的生存期由程序员决定,使用非常灵活,但问题也最多。 试题30: 简述变量的作用域,生存周期,内存的分配方式(全局变量,静态全局变量,局部变量,静态局部变量的区别) 分析: 全局变量具有全局作用域。 全局变量只需在一个源文件中定义,就可以作用于所有的源文件。 当然,其他不包括全局变量定义的源文件需要用extern关键字再次声明这个全局变量。 静态局部变量具有局部作用域。 它只被初始化一次,自从第一次初始化直到程序结束都一直存在,他和全局变量的区别在于全局变量对所有的函数都是可见的,而静态局部变量只对定义自己的函数体始终可见。 局部变量也只有局部
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 面试The interview questions and answers 面试 The