C语言面试题及答案.docx
- 文档编号:28188513
- 上传时间:2023-07-09
- 格式:DOCX
- 页数:27
- 大小:326.21KB
C语言面试题及答案.docx
《C语言面试题及答案.docx》由会员分享,可在线阅读,更多相关《C语言面试题及答案.docx(27页珍藏版)》请在冰豆网上搜索。
C语言面试题及答案
1、关键字static的作用是什么?
这个简单的问题很少有人能回答完全。
在C语言中,关键字static有三个明显的作用:
1).在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2).在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。
它是一个本地的全局变量。
3).在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。
那就是,这个函数被限制在声明它的模块的本地范围内使用。
大多数应试者能正确回答第一部分,一部分能正确回答第二部分,同是很少的人能懂得第三部分。
这是一个应试者的严重的缺点,因为他显然不懂得本地化数据和代码范围的好处和重要性。
2、.h头文件中的ifndef/define/endif的作用?
答:
防止该头文件被重复引用。
3、描述实时系统的基本特性
答:
在特定时间内完成特定的任务,实时性与可靠性。
4、什么是平衡二叉树?
答:
左右子树都是平衡二叉树且左右子树的深度差值的绝对值不大于1。
5、冒泡排序算法的时间复杂度是什么?
答:
O(n^2)
6、队列和栈有什么区别?
答:
队列先进先出,栈后进先出
7、局部变量能否和全局变量重名?
答:
能,局部会屏蔽全局。
要用全局变量,需要使用":
:
"局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。
对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义一个同名的局部变量,而那个局部变量的作用域就在那个循环体内
8、全局变量可不可以定义在可被多个.C文件包含的头文件中?
为什么?
答、可以,在不同的C文件中以static形式来声明同名全局变量。
可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量赋初值,此时连接不会出错。
9、do……while和while……do有什么区别?
答前一个循环一遍再判断,后一个判断以后再循环。
10、程序的内存分配
答:
一个由c/C++编译的程序占用的内存分为以下几个部分:
1、栈区(stack)—由编译器自动分配释放,存放函数的参数值,局部变量的值等。
其操作方式类似于数据结构中的栈。
2、堆区(heap)—一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。
注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。
程序结束后由系统释放。
4、文字常量区—常量字符串就是放在这里的。
程序结束后由系统释放。
5、程序代码区—存放函数体的二进制代码
11、什么是预编译,何时需要预编译?
答:
预编译又称为预处理,是做些代码文本的替换工作。
处理#开头的指令,比如拷贝#include包含的文件代码,#define宏定义的替换,条件编译等,就是为编译做的预备工作的阶段,主要处理#开始的预编译指令,预编译指令指示了在程序正式编译前就由编译器进行的操作,可以放在程序中的任何位置。
c编译系统在对程序进行通常的编译之前,先进行预处理。
c提供的预处理功能主要有以下三种:
1)宏定义2)文件包含3)条件编译
12、关键字volatile有什么含意并给出三个不同的例子。
答:
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。
精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。
下面是volatile变量的几个例子:
1).并行设备的硬件寄存器(如:
状态寄存器)
2).一个中断服务子程序中会访问到的非自动变量(Non-automaticvariables)3).多线程应用中被几个任务共享的变量回答不出这个问题的人是不会被雇佣的。
我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。
嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile变量。
不懂得volatile内容将会带来灾难。
假设被面试者正确地回答了这是问题(嗯,怀疑这否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。
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;
}
13、结构与联合有和区别?
答:
(1).结构和联合都是由多个不同的数据类型成员组成,但在任何同一时刻,联合中只存放了一个被选中的成员(所有成员共用一块地址空间),而结构的所有成员都存在(不同成员的存放地址不同)。
(2).对于联合的不同成员赋值,将会对其它成员重写,原来成员的值就不存在了,而对于结构的不同成员赋值是互不影响的
14、请说出const与#define相比,有何优点?
答:
Const作用:
定义常量、修饰函数参数、修饰函数返回值三个作用。
被Const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
1)const常量有数据类型,而宏常量没有数据类型。
编译器可以对前者进行类型安全检查。
而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
2)有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。
15、分别写出BOOL,int,float,指针类型的变量a与“零”的比较语句。
答:
BOOL:
if(!
a)orif(a)
int:
if(a==0)
float:
constEXPRESSIONEXP=0.000001
if(a
pointer:
if(a!
=NULL)orif(a==NULL)
16、如何判断一段程序是由C编译程序还是由C++编译程序编译的?
答:
#ifdef__cplusplus
cout<<"c++";
#else
cout<<"c";
#endif
17、论述含参数的宏与函数的优缺点
答:
带参宏
函数
处理时间
编译时
程序运行时
参数类型
没有参数类型问题
定义实参、形参类型
处理过程
不分配内存
分配内存
程序长度
变长
不变
运行速度
不占运行时间
调用和返回占用时间
18、用两个栈实现一个队列的功能?
要求给出算法和思路!
答、设2个栈为A,B,一开始均为空.入队:
将新元素push入栈A;出队:
(1)判断栈B是否为空;
(2)如果不为空,则将栈A中所有元素依次pop出并push到栈B;
(3)将栈B的栈顶元素pop出;这样实现的队列入队和出队的平摊复杂度都还是O
(1),比上面的几种方法要好
19、嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢?
答:
这个问题用几个解决方案。
我首选的方案是:
while
(1)
{
}
一些程序员更喜欢如下方案:
for(;;){}这个实现方式让我为难,因为这个语法没有确切表达到底怎么回事。
如果一个应试者给出这个作为方案,我将用这个作为一个机会去探究他们这样做的基本原理。
如果他们的基本答案是:
我被教着这样做,但从没有想到过为什么。
‛这会给我留下一个坏印象。
第三个方案是用gotoLoop:
...gotoLoop;应试者如给出上面的方案,这说明或者他是一个汇编语言程序员(这也许是好事)或者他是一个想进入新领域的BASIC/FORTRAN程序员。
20、访问固定的内存位置(Accessingfixedmemorylocations)
答:
嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。
在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。
编译器是一个纯粹的ANSI编译器。
写代码去完成这一任务。
这一问题测试你是否知道为了访问一绝对地址把一个整型数强制转换(typecast)为一指针是合法的。
这一问题的实现方式随着个人风格不同而不同。
典型的类似代码如下:
int*ptr;
ptr=(int*)0x67a9;
*ptr=0xaa66;
Amoreobscureapproachis:
一个较晦涩的方法是:
*(int*const)(0x67a9)=0xaa55;
即使你的品味更接近第二种方案,但我建议你在面试时使用第一种方案。
21、动态内存分配(Dynamicmemoryallocation)
答:
尽管不像非嵌入式计算机那么常见,嵌入式系统还是有从堆(heap)中动态分配内存的过程的。
那么嵌入式系统中,动态分配内存可能发生的问题是什么?
这里,我期望应试者能提到内存碎片,碎片收集的问题,变量的持行时间等等。
这个主题已经在ESP杂志中被广泛地讨论过了(主要是P.J.Plauger,他的解释远远超过我这里能提到的任何解释),所有回过头看一下这些杂志吧!
让应试者进入一种虚假的安全感觉后,我拿出这么一个小节目:
下面的代码片段的输出是什么,为什么?
char*ptr;
if((ptr=(char*)malloc(0))==NULL)
puts("Gotanullpointer");
else
puts("Gotavalidpointer");
这是一个有趣的问题。
最近在我的一个同事不经意把0值传给了函数malloc,得到了一个合法的指针之后,我才想到这个问题。
这就是上面的代码,该代码的输出是"Gotavalidpointer"。
我用这个来开始讨论这样的一问题,看看被面试者是否想到库例程这样做是正确。
得到正确的答案固然重要,但解决问题的方法和你做决定的基本原理更重要些。
22、用变量a给出下面的定义
答:
a)一个整型数(Aninteger)
b)一个指向整型数的指针(Apointertoaninteger)c)一个指向指针的的指针,它指向的指针是指向一个整型数(Apointertoapointertoaninteger)
d)一个有10个整型数的数组(Anarrayof10integers)
e)一个有10个指针的数组,该指针是指向一个整型数的(Anarrayof10pointerstointegers)
f)一个指向有10个整型数数组的指针(Apointertoanarrayof10integers)
g)一个指向函数的指针,该函数有一个整型参数并返回一个整型数(Apointertoafunctionthattakesanintegerasanargumentandreturnsaninteger)
h)一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数(Anarrayoftenpointerstofunctionsthattakeanintegerargumentandreturnaninteger)
答案是:
a)inta;//Aninteger
b)int*a;//Apointertoaninteger
c)int**a;//Apointertoapointertoaninteger
d)inta[10];//Anarrayof10integers
e)int*a[10];//Anarrayof10pointerstointegers
f)int(*a)[10];//Apointertoanarrayof10integers
g)int(*a)(int);//Apointertoafunctionathattakesanintegerargumentandreturnsaninteger
h)int(*a[10])(int);//Anarrayof10pointerstofunctionsthattakeanintegerargumentandreturnaninteger
24、一个单向链表,不知道头节点,一个指针指向其中的一个节点,问如何删除这个指针指向的节点?
答:
将这个指针指向的next节点值copy到本节点,将next指向next->next,并随后删除原next指向的节点。
25、联合体/共用体的使用
C语言中,联合体/共用体可以实现同一个内存空间中存储不同的数据类型(不是同时存储)。
利用这个特性可以在一些情况下给我们提供便利。
比如验证大小端有很多种方法,使用联合体也可以:
#include
union test
{
int a;
char b;
}test_u;
int main(void)
{
test_u.a = 0x12345678;
if (0x78 == test_u.b)
{
printf("Little endian\n");
}
else
{
printf("Big endian\n");
}
return 0;
}
再比如前两天群里有朋友问:
在寄存器操作上,位域看起来似乎挺合适,但为什么库代码都基本没有使用呢?
其实TI的库中有使用,是结合共用体来使用的:
所有的寄存器被封装成联合体类型的,联合体里边的成员是一个32bit的整数及一个结构体,该结构体以位域的形式体现。
这样就可以达到直接操控寄存器的某些位了。
比如,我们要设置PA0引脚的GPAQSEL1寄存器的[1:
0]两位都为1,则我们只操控两个bit就可以很方便的这么设置:
GpioCtrlRegs.GPAQSEL1.bit.GPIO0 = 3
或者直接操控整个寄存器:
GpioCtrlRegs.GPAQSEL1.all |=0x03
再比如当系统中有很多用于描述同一功能的状态变量时,传统做法可能是:
volatile uint16 iuvp = 0; // 输入欠压
volatile uint16 iovp = 0; // 输入过压
volatile uint16 iocp = 0; // 输入过流
volatile uint16 motp = 0; // 功率模块过温
volatile uint16 oovp = 0; // 输出过流
/* 判断异常及处理 */
if ((iuvp !
= 0) ||
(iovp !
= 0) ||
(iocp !
= 0) ||
(motp !
= 0) ||
(oovp !
= 0)
)
{
// do something
}
当我们在做状态判断时,这样写下来就感觉很冗余。
换种方式:
可以把这些状态变量规整到一个联合体中,用位域来描述。
(以下这段代码来自:
typedef union SYSTEM_FAULT{
uint16 all;
struct {
uint16 iuvp:
1; // 输入欠压
uint16 iovp:
1; // 输入过压
uint16 iocp:
1; // 输入过流
uint16 motp:
1; // 功率模块过温
uint16 oovp:
1; // 输出过流
uint16 oocp:
1; // 输出过流
uint16 oopp:
1; // 过功率故障
uint16 excu:
1; // 电流采样零漂过大
uint16 cotp:
1; // CPU过温
uint16 ilrv:
1; // 输入极性错误
}bit;
}SYSTEM_FAULT_STRUCT;
然后状态判断可以这么做:
volatile SYSTEM_FAULT_STRUCT gPSM_FAULT;
if(gPSM_FAULT.all !
= 0)
{
// do something
}
26、下面的代码输出是什么,为什么?
voidfoo(void)
{
unsignedinta=6;
intb=-20;
(a+b>6)?
puts(">6"):
puts("<=6");
}
这个问题测试你是否懂得C语言中的整数自动转换原则,我发现有些开发者懂得极少这些东西。
不管如何,这无符号整型问题的答案是输出是">6"。
原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。
因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。
这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。
如果你答错了这个问题,你也就到了得不到这份工作的边缘。
27、评价下面的代码片断:
unsignedintzero=0;
unsignedintcompzero=0xFFFF;/*1'scomplementofzero*/
对于一个int型不是16位的处理器为说,上面的代码是不正确的。
应编写如下:
unsignedintcompzero=~0;
这一问题真正能揭露出应试者是否懂得处理器字长的重要性。
在我的经验里,好的嵌入式程序员非常准确地明白硬件的细节和它的局限,然而PC机程序往往把硬件作为一个无法避免的烦恼。
28、设有以下说明和定义:
typedefunion{longi;intk[5];charc;}DATE;
structdata{intcat;DATEcow;doubledog;}too;
DATEmax;
则语句printf("%d",sizeof(structdate)+sizeof(max));的执行结果是?
答、结果是:
52。
DATE是一个union,变量公用空间.里面最大的变量类型是int[5],占用20个字节.所以它的大小是20。
data是一个struct,每个变量分开占用空间.依次为int4+DATE20+double8=32.所以结果是20+32=52.
当然...在某些16位编辑器下,int可能是2字节,那么结果是int2+DATE10+double8=20
29、写出下列代码的输出内容
#include
intinc(inta)
{
return(++a);
}
intmulti(int*a,int*b,int*c)
{
return(*c=*a**b);
}
typedefint(FUNC1)(intin);
typedefint(FUNC2)(int*,int*,int*);
voidshow(FUNC2fun,intarg1,int*arg2)
{
INCp=&inc;
inttemp=p(arg1);
fun(&temp,&arg1,arg2);
printf("%d\n",*arg2);
}
main()
{
inta;
show(multi,10,&a);
return0;
}
答:
110
30、请问下面程序有什么错误?
inta[60][250][1000],i,j,k;
for(k=0;k<=1000;k++)
for(j=0;j<250;j++)
for(i=0;i<60;i++)
a[i][j][k]=0;
答案:
把循环语句内外换一下
31、以下3个有什么区别
char*constp;//常量指针,p的值不可以修改
charconst*p;//指向常量的指针,指向的常量值不可以改
constchar*p;//和charconst*p
32、以下代码中的两个sizeof用法有问题吗?
voidUpperCase(charstr[])//将str中的小写字母转换成大写字母
{
for(size_ti=0;i if('a'<=str[i]&&str[i]<='z') str[i]-=('a'-'A'); } charstr[]="aBcDe"; cout<<"str字符长度为: "< UpperCase(str);cout< 答: 函数内的sizeof有问题。 根据语法,sizeof如用于数组,只能测出静态数组的大小,无法检测动态分配的或外部数组大小。 函数外的str是一个静态定义的数组,因此其大小为6,函数内的
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 语言 试题 答案