第一篇:嵌入式程序员C语言笔试经典题
新一篇: 存储过程,无限级分类 | 旧一篇: 类继承中构造函数和析构函数的调用
这个测试适于不同水平的应试者,大多数初级水平的应试者的成绩会很差,经验丰富的程序员应该有很好的成绩。为了让你能自己决定某些问题的偏好,每个问题没有分配分数,如果选择这些考题为你所用,请自行按你的意思分配分数。
预处理器(Preprocessor).用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)#define SECONDS_PER_YEAR(60 * 60 * 24 * 365)UL 我在这想看到几件事情:
1)#define 语法的基本知识(例如:不能以分号结束,括号的使用,等等)
2)懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多少秒而不是计算出实际的值,是更清晰而没有代价的。
3)意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。
4)如果你在你的表达式中用到UL(表示无符号长整型),那么你有了一个好的起点。记住,第一印象很重要。.写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个。#define MIN(A,B)((A)<=(B)?(A):(B))这个测试是为下面的目的而设的:
1)标识#define在宏中应用的基本知识。这是很重要的。因为在嵌入(inline)操作符 变为标准C的一部分之前,宏是方便产生嵌入代码的唯一方法,对于嵌入式系统来说,为了能达到要求的性能,嵌入代码经常是必须的方法。
2)三重条件操作符的知识。这个操作符存在C语言中的原因是它使得编译器能产生比if-then-else更优化的代码,了解这个用法是很重要的。3)懂得在宏中小心地把参数用括号括起来
4)我也用这个问题开始讨论宏的副作用,例如:当你写下面的代码时会发生什么事? least = MIN(*p++, b);
3.预处理器标识#error的目的是什么?
如果你不知道答案,请看参考文献1。这问题对区分一个正常的伙计和一个书呆子是很有用的。只有书呆子才会读C语言课本的附录去找出象这种问题的答案。当然如果你不是在找一个书呆子,那么应试者最好希望自己不要知道答案。指令 用途
# 空指令,无任何效果
#include 包含一个源代码文件
#define 定义宏
#undef 取消已定义的宏
#if 如果给定条件为真,则编译下面代码
#ifdef 如果宏已经定义,则编译下面代码
#ifndef 如果宏没有定义,则编译下面代码
#elif 如果前面的#if给定条件不为真,当前条件为真,则编译下面代码 #endif 结束一个#if……#else条件编译块
#error 停止编译并显示错误信息
死循环(Infinite loops)
4.嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢? 这个问题用几个解决方案。我首选的方案是:
while(1){ }
一些程序员更喜欢如下方案:
for(;;){ }
这个实现方式让我为难,因为这个语法没有确切表达到底怎么回事。如果一个应试者给出这个作为方案,我将用这个作为一个机会去探究他们这样做的基本原理。如果他们的基本答案是:“我被教着这样做,但从没有想到过为什么。”这会给我留下一个坏印象。
第三个方案是用 goto Loop:...goto Loop;应试者如给出上面的方案,这说明或者他是一个汇编语言程序员(这也许是好事)或者他是一个想进入新领域的BASIC/FORTRAN程序员。
数据声明(Data declarations)
5.用变量a给出下面的定义 a)一个整型数(An integer)
//int a me b)一个指向整型数的指针(A pointer to an integer)
//int *a me c)一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a pointer to an intege)r
//int ** a me
d)一个有10个整型数的数组(An array of 10 integers)//int a[10] me e)一个有10个指针的数组,该指针是指向一个整型数的。(An array of 10 pointers to integers)
// int * a[10] me f)一个指向有10个整型数数组的指针(A pointer to an array of 10 integers)
//in t(*a)[10] me g)一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer)
int(*fun)(int)
h)一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数(An array of ten pointers to functions that take an integer argument and return an integer)
//int(*fun[10])(int)答案是:
a)int a;// An integer b)int *a;// A pointer to an integer c)int **a;// A pointer to a pointer to an integer d)int a[10];// An array of 10 integers e)int *a[10];// An array of 10 pointers to integers f)int(*a)[10];// A pointer to an array of 10 integers g)int(*a)(int);// A pointer to a function a that takes an integer argument and returns an integer h)int(*a[10])(int);// An array of 10 pointers to functions that take an integer argument and return an integer
人们经常声称这里有几个问题是那种要翻一下书才能回答的问题,我同意这种说法。当我写这篇文章时,为了确定语法的正确性,我的确查了一下书。但是当我被面试的时候,我期望被问到这个问题(或者相近的问题)。因为在被面试的这段时间里,我确定我知道这个问题的答案。应试者如果不知道所有的答案(或至少大部分答案),那么也就没有为这次面试做准备,如果该面试者没有为这次面试做准备,那么他又能为什么出准备呢?
Static
6.关键字static的作用是什么?
这个简单的问题很少有人能回答完全。在C语言中,关键字static有三个明显的作用: 1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2)在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
3)在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。
大多数应试者能正确回答第一部分,一部分能正确回答第二部分,同是很少的人能懂得第三部分。这是一个应试者的严重的缺点,因为他显然不懂得本地化数据和代码范围的好处和重要性。
Const
7.关键字const有什么含意?
我只要一听到被面试者说:“const意味着常数”,我就知道我正在和一个业余者打交道。去年Dan Saks已经在他的文章里完全概括了const的所有用法,因此ESP(译者:Embedded Systems Programming)的每一位读者应该非常熟悉const能做什么和不能做什么.如果你从没有读到那篇文章,只要能说出const意味着“只读”就可以了。尽管这个答案不是完全的答案,但我接受它作为一个正确的答案。(如果你想知道更详细的答案,仔细读一下Saks的文章吧。)
如果应试者能正确回答这个问题,我将问他一个附加的问题: 下面的声明都是什么意思?
const int a;int const a;const int *a;int * const a;int const * a const;
/******/ 前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意识a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。如果应试者能正确回答这些问题,那么他就给我留下了一个好印象。顺带提一句,也许你可能会问,即使不用关键字 const,也还是能很容易写出功能正确的程序,那么我为什么还要如此看重关键字const呢?我也如下的几下理由:
1)关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。)2)通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。
3)合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。
Volatile
8.关键字volatile有什么含意?并给出三个不同的例子。
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子: 1)并行设备的硬件寄存器(如:状态寄存器)
2)一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)3)多线程应用中被几个任务共享的变量
回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。搞嵌入式的家伙们经常同硬件、中断、RTOS等等打交道,所有这些都要求用到volatile变量。不懂得volatile的内容将会带来灾难。
假设被面试者正确地回答了这是问题(嗯,怀疑是否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。
1)一个参数既可以是const还可以是volatile吗?解释为什么。2);一个指针可以是volatile 吗?解释为什么。3);下面的函数有什么错误:
int square(volatile int *ptr){ return *ptr * *ptr;}
下面是答案:
1)是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
2);是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。3)这段代码有点变态。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:
int square(volatile int *ptr){ int a,b;a = *ptr;b = *ptr;return a * b;}
由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:
long square(volatile int *ptr){ int a;a = *ptr;return a * a;}
位操作(Bit manipulation)
9.嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a,写两段代码,第一个设置a的bit 3,第二个清除a 的bit 3。在以上两个操作中,要保持其它位不变。对这个问题有三种基本的反应
1)不知道如何下手。该被面者从没做过任何嵌入式系统的工作。
2)用bit fields。Bit fields是被扔到C语言死角的东西,它保证你的代码在不同编译器之间是不可移植的,同时也保证了的你的代码是不可重用的。我最近不幸看到 Infineon为其较复杂的通信芯片写的驱动程序,它用到了bit fields因此完全对我无用,因为我的编译器用其它的方式来实现bit fields的。从道德讲:永远不要让一个非嵌入式的家伙粘实际硬件的边。
3)用 #defines 和 bit masks 操作。这是一个有极高可移植性的方法,是应该被用到的方法。最佳的解决方案如下:
#define BIT3(0x1 << 3)static int a;
void set_bit3(void){ a |= BIT3;} void clear_bit3(void){ a &= ~BIT3;}
一些人喜欢为设置和清除值而定义一个掩码同时定义一些说明常数,这也是可以接受的。我希望看到几个要点:说明常数、|=和&=~操作。
访问固定的内存位置(Accessing fixed memory locations)
10.嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。这一问题测试你是否知道为了访问一绝对地址把一个整型数强制转换(typecast)为一指针是合法的。这一问题的实现方式随着个人风格不同而不同。典型的类似代码如下: int *ptr;ptr =(int *)0x67a9;*ptr = 0xaa55;
A more obscure approach is: 一个较晦涩的方法是:
*(int * const)(0x67a9)= 0xaa55;
即使你的品味更接近第二种方案,但我建议你在面试时使用第一种方案。
中断(Interrupts)
11.中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展—让标准C支持中断。具代表事实是,产生了一个新的关键字 __interrupt。下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码的。
__interrupt double compute_area(double radius){ double area = PI * radius * radius;printf(“nArea = %f”, area);return area;}
这个函数有太多的错误了,以至让人不知从何说起了:
1)ISR 不能返回一个值。如果你不懂这个,那么你不会被雇用的。
2)ISR 不能传递参数。如果你没有看到这一点,你被雇用的机会等同第一项。
3)在许多的处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额处的寄存器入栈,有些处理器/编译器就是不允许在ISR中做浮点运算。此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明智的。
4)与第三点一脉相承,printf()经常有重入和性能上的问题。如果你丢掉了第三和第四点,我不会太为难你的。不用说,如果你能得到后两点,那么你的被雇用前景越来越光明了。
代码例子(Code examples).下面的代码输出是什么,为什么?
void foo(void){ unsigned int a = 6;int b =-20;(a+b > 6)? puts(“> 6”): puts(“<= 6”);} 这个问题测试你是否懂得C语言中的整数自动转换原则,我发现有些开发者懂得极少这些东西。不管如何,这无符号整型问题的答案是输出是 “>6”。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。如果你答错了这个问题,你也就到了得不到这份工作的边缘。
13.评价下面的代码片断:
unsigned int zero = 0;unsigned int compzero = 0xFFFF;/*1's complement of zero */
对于一个int型不是16位的处理器为说,上面的代码是不正确的。应编写如下:
unsigned int compzero = ~0;这一问题真正能揭露出应试者是否懂得处理器字长的重要性。在我的经验里,好的嵌入式程序员非常准确地明白硬件的细节和它的局限,然而PC机程序往往把硬件作为一个无法避免的烦恼。
到了这个阶段,应试者或者完全垂头丧气了或者信心满满志在必得。如果显然应试者不是很好,那么这个测试就在这里结束了。但如果显然应试者做得不错,那么我就扔出下面的追加问题,这些问题是比较难的,我想仅仅非常优秀的应试者能做得不错。提出这些问题,我希望更多看到应试者应付问题的方法,而不是答案。不管如何,你就当是这个娱乐吧...动态内存分配(Dynamic memory allocation)
14.尽管不像非嵌入式计算机那么常见,嵌入式系统还是有从堆(heap)中动态分配内存的过程的。那么嵌入式系统中,动态分配内存可能发生的问题是什么?
这里,我期望应试者能提到内存碎片,碎片收集的问题,变量的持行时间等等。这个主题已经在ESP杂志中被广泛地讨论过了(主要是 P.J.Plauger, 他的解释远远超过我这里能提到的任何解释),所有回过头看一下这些杂志吧!让应试者进入一种虚假的安全感觉后,我拿出这么一个小节目: 下面的代码片段的输出是什么,为什么?
char *ptr;if((ptr =(char *)malloc(0))== NULL)puts(“Got a null pointer”);else puts(“Got a valid pointer”);
这是一个有趣的问题。最近在我的一个同事不经意把0值传给了函数malloc,得到了一个合法的指针之后,我才想到这个问题。这就是上面的代码,该代码的输出是“Got a valid pointer”。我用这个来开始讨论这样的一问题,看看被面试者是否想到库例程这样做是正确。得到正确的答案固然重要,但解决问题的方法和你做决定的基本原理更重要些。
Typedef Typedef 在C语言中频繁用以声明一个已经存在的数据类型的同义字。也可以用预处理器做类似的事。例如,思考一下下面的例子:
#define dPS struct s * typedef struct s * tPS;
以上两种情况的意图都是要定义dPS 和 tPS 作为一个指向结构s指针。哪种方法更好呢?(如果有的话)为什么?
这是一个非常微妙的问题,任何人答对这个问题(正当的原因)是应当被恭喜的。答案是:typedef更好。思考下面的例子:
dPS p1,p2;tPS p3,p4;第一个扩展为
struct s * p1, p2;.上面的代码定义p1为一个指向结构的指,p2为一个实际的结构,这也许不是你想要的。第二个例子正确地定义了p3 和p4 两个指针。
晦涩的语法.C语言同意一些令人震惊的结构,下面的结构是合法的吗,如果是它做些什么?
int a = 5, b = 7, c;c = a+++b;
这个问题将做为这个测验的一个愉快的结尾。不管你相不相信,上面的例子是完全合乎语法的。问题是编译器如何处理它?水平不高的编译作者实际上会争论这个问题,根据最处理原则,编译器应当能处理尽可能所有合法的用法。因此,上面的代码被处理成:
c = a++ + b;
因此, 这段代码持行后a = 6, b = 7, c = 12。
如果你知道答案,或猜出正确答案,做得好。如果你不知道答案,我也不把这个当作问题。我发现这个问题的最大好处是这是一个关于代码编写风格,代码的可读性,代码的可修改性的好的话题。
第二篇:嵌入式程序员C语言笔试题目
华硕_嵌入式程序员C语言笔试题目
预处理器(Preprocessor).用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)
#define SECONDS_PER_YEAR(60 * 60 * 24 * 365)UL
我在这想看到几件事情:
1)#define 语法的基本知识(例如:不能以分号结束,括号的使用,等等)
2)懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多少秒而不是计算出实际的值,是更清晰而没有代价的。
3)意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。
4)如果你在你的表达式中用到UL(表示无符号长整型),那么你有了一个好的起点。记住,第一印象很重要。.写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个。
#define MIN(A,B)((A)<=(B)?(A):(B))
这个测试是为下面的目的而设的:
1)标识#define在宏中应用的基本知识。这是很重要的。因为在 嵌入(inline)操作符 变为标准C的一部分之前,宏是方便产生嵌入代码的唯一方法,对于嵌入式系统来说,为了能达到要求的性能,嵌入代码经常是必须的方法。
2)三重条件操作符的知识。这个操作符存在C语言中的原因是它使得编译器能产生比if-then-else更优化的代码,了解这个用法是很重要的。
3)懂得在宏中小心地把参数用括号括起来
4)我也用这个问题开始讨论宏的副作用,例如:当你写下面的代码时会发生什么事?
least = MIN(*p++, b);
3.预处理器标识#error的目的是什么?
Error directives produce compiler-time error messages.死循环(Infinite loops)
4.嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢?
这个问题用几个解决方案。我首选的方案是:
while(1){ }
一些程序员更喜欢如下方案:
for(;;){ }
这个实现方式让我为难,因为这个语法没有确切表达到底怎么回事。如果一个应试者给出这个作为方案,我将用这个作为一个机会去探究他们这样做的基本原理。如果他们的基本答案是:“我被教着这样做,但从没有想到过为什么。”这会给我留下一个坏印象。
第三个方案是用 goto Loop:...goto Loop;
应试者如给出上面的方案,这说明或者他是一个汇编语言程序员(这也许是好事)或者他是一个想进入新领域的BASIC/FORTRAN程序员。
数据声明(Data declarations)
5.用变量a给出下面的定义
a)一个整型数(An integer)
b)一个指向整型数的指针(A pointer to an integer)c)一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a pointer to an intege)r
d)一个有10个整型数的数组(An array of 10 integers)e)一个有10个指针的数组,该指针是指向一个整型数的。(An array of 10 pointers to integers)
f)一个指向有10个整型数数组的指针(A pointer to an array of 10 integers)
g)一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer)
h)一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数(An array of ten pointers to functions that take an integer argument and return an integer)
答案是:
a)int a;// An integer
b)int *a;// A pointer to an integer
c)int **a;// A pointer to a pointer to an integer
d)int a[10];// An array of 10 integers
e)int *a[10];// An array of 10 pointers to integers
f)int(*a)[10];// A pointer to an array of 10 integers
g)int(*a)(int);// A pointer to a function a that takes an integer argument and returns an integer
h)int(*a[10])(int);// An array of 10 pointers to functions that take an integer argument and return an integer
人们经常声称这里有几个问题是那种要翻一下书才能回答的问题,我同意这种说法。当我写这篇文章时,为了确定语法的正确性,我的确查了一下书。但是当我被面试的时候,我期望被问到这个问题(或者相近的问题)。因为在被面试的这段时间里,我确定我知道这个问题的答案。应试者如果不知道所有的答案(或至少大部分答案),那么也就没有为这次面试做准备,如果该面试者没有为这次面试做准备,那么他又能为什么出准备呢? Static
6.关键字static的作用是什么?
这个简单的问题很少有人能回答完全。在C语言中,关键字static有三个明显的作用:
1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2)在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
3)在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。
大多数应试者能正确回答第一部分,一部分能正确回答第二部分,同是很少的人能懂得第三部分。这是一个应试者的严重的缺点,因为他显然不懂得本地化数据和代码范围的好处和重要性。
Const
7.关键字const有什么含意?
我只要一听到被面试者说:“const意味着常数”,我就知道我正在和一个业余者打交道。去年Dan Saks已经在他的文章里完全概括了const的所有用法,因此ESP(译者:Embedded Systems Programming)的每一位读者应该非常熟悉const能做什么和不能做什么.如果你从没有读到那篇文章,只要能说出const意味着“只读”就可以了。尽管这个答案不是完全的答案,但我接受它作为一个正确的答案。(如果你想知道更详细的答案,仔细读一下Saks的文章吧。)
如果应试者能正确回答这个问题,我将问他一个附加的问题:
下面的声明都是什么意思?
const int a;
int const a;
const int *a;
int * const a;
int const * const a=new int(1);
/******/
前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意识a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。如果应试者能正确回答这些问题,那么他就给我留下了一个好印象。顺带提一句,也许你可能会问,即使不用关键字 const,也还是能很容易写出功能正确的程序,那么我为什么还要如此看重关键字const呢?我也如下的几下理由:
1)关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。)
第三篇:2012年最新C和C++程序员笔试题
以下是整理自8月下旬至10月份内的各大公司的笔试面试三十题(注:所有题目基本上全部为软件开发方向,题目来源:网络收集),相信一定能给正在参加各种校招的诸多朋友多少帮助,学习参考或借鉴
九月十月百度人搜,阿里巴巴,腾讯华为小米搜狗笔/面试五十题
9月11日,京东:
谈谈你对面向对象编程的认识
8月20日,金山面试,题目如下:
数据库1中存放着a类数据,数据库2中存放着以天为单位划分的表30张(比如table_20110909,table_20110910,table_20110911),总共是一个月的数据。表1中的a类数据中有一个字段userid来唯一判别用户身份,表2中的30张表(每张表结构相同)也有一个字段userid来唯一识别用户身份。如何判定a类数据库的多少用户在数据库2中出现过? 百度实习笔试题(2012.5.6)
简答题1
一个单词单词字母交换,可得另一个单词,如army->mary,成为兄弟单词。提供一个单词,在字典中找到它的兄弟。描述数据结构和查询过程。评点:同去年9月份的一道题,见此文第3题:简答题2 线程和进程区别和联系。什么是“线程安全”
简答题3
C和C++怎样分配和释放内存,区别是什么
算法题1
一个url指向的页面里面有另一个url,最终有一个url指向之前出现过的url或空,这两种情形都定义为null。这样构成一个单链表。给两条这样单链表,判断里面是否存在同样的url。url以亿级计,资源不足以hash。
算法题2
数组al[0,mid-1] 和 al[mid,num-1],都分别有序。将其merge成有序数组al[0,num-1],要求空间复杂度O(1)系统设计题
百度搜索框的suggestion,比如输入北京,搜索框下面会以北京为前缀,展示“北京爱情故事”、“北京公交”、“北京医院”等等搜索词。
如何设计使得空间和时间复杂度尽量低。评点:老题,直接上Trie树+Hash,Trie树的介绍见:从Trie树(字典树)谈到后缀树。
人搜笔试
1.快排每次以第一个作为主元,问时间复杂度是多少?(O(N*logN))
2.T(N)= N + T(N/2)+T(2N), 问T(N)的时间复杂度是多少? 点评:O(N*logN)or O(N)? 3.从(0,1)中平均随机出几次才能使得和超过1?(e)4.编程题:
一棵树的节点定义格式如下: struct Node{ Node* parent;Node* firstChild;// 孩子节点 Node* sibling;// 兄弟节点 } 要求非递归遍历该树。
思路:采用队列存储,来遍历节点。5.算法题:
有N个节点,每两个节点相邻,每个节点只与2个节点相邻,因此,N个顶点有N-1条边。每一条边上都有权值wi,定义节点i到节点i+1的边为wi。求:不相邻的权值和最大的边的集合。 人搜面试,所投职位:搜索研发工程师:面试题回忆
1、删除字符串开始及末尾的空白符,并且把数组中间的多个空格(如果有)符转化为1个。
2、求数组(元素可为正数、负数、0)的最大子序列和。
3、链表相邻元素翻转,如a->b->c->d->e->f-g,翻转后变为:b->a->d->c->f->e->g
4、链表克隆。链表的结构为: typedef struct list { int data;//数据字段
list *middle;//指向链表中某任意位置元素(可指向自己)的指针 list *next;//指向链表下一元素 } list;5、100万条数据的数据库查询速度优化问题,解决关键点是:根据主表元素特点,把主表拆分并新建副表,并且利用存储过程保证主副表的数据一致性。(不用写代码)
6、求正整数n所有可能的和式的组合(如;4=1+1+1+1、1+1+2、1+3、2+1+1、2+2)。
7、求旋转数组的最小元素(把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个排好序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3, 4, 5, 1, 2}为{1, 2, 3, 4, 5}的一个旋转,该数组的最小值为1)
8、找出两个单链表里交叉的第一个元素
9、字符串移动(字符串为*号和26个字母的任意组合,把*号都移动到最左侧,把字母移到最右侧并保持相对顺序不变),要求时间和空间复杂度最小
10、时间复杂度为O(1),怎么找出一个栈里的最大元素
11、线程、进程区别
12、static在C和C++里各代表什么含义
13、const在C/C++里什么意思
14、常用linux命令
15、解释Select/Poll模型 网易有道二面:
判断一个数字序列是BST后序遍历的结果,现场写代码。
8月30日,网易有道面试题
var tt = 'aa';function test(){ alert(tt);var tt = 'dd';alert(tt);} test(); 8月31日,百度面试题:不使用随机数的洗牌算法,9月6日,阿里笔试题:平面上有很多点,点与点之间有可能有连线,求这个图里环的数目。
9月7日,一道华为上机题:
题目描述: 选秀节目打分,分为专家评委和大众评委,score[] 数组里面存储每个评委打的分数,judge_type[] 里存储与 score[] 数组对应的评委类别,judge_type == 1,表示专家评委,judge_type == 2,表示大众评委,n表示评委总数。打分规则如下:专家评委和大众评委的分数先分别取一个平均分(平均分取整),然后,总分 = 专家评委平均分 * 0.6 + 大众评委 * 0.4,总分取整。如果没有大众评委,则 总分 = 专家评委平均分,总分取整。函数最终返回选手得分。
函数接口 int cal_score(int score[], int judge_type[], int n)
上机题目需要将函数验证,但是题目中默认专家评委的个数不能为零,但是如何将这种专家数目为0的情形排除出去。
9月8日,腾讯面试题:
假设两个字符串中所含有的字符和个数都相同我们就叫这两个字符串匹配,比如:abcda和adabc,由于出现的字符个数都是相同,只是顺序不同,所以这两个字符串是匹配的。要求高效!
又是跟上述第3题中简单题一的兄弟节点类似的一道题,我想,你们能想到的, 阿里云,搜索引擎中5亿个url怎么高效存储;
一道C++笔试题,求矩形交集的面积:
在一个平面坐标系上,有两个矩形,它们的边分别平行于X和Y轴。
其中,矩形A已知,ax1(左边), ax2(右边), ay1(top的纵坐标), ay2(bottom纵坐标).矩形B,类似,就是 bx1, bx2, by1, by2。这些值都是整数就OK了。要求是,如果矩形没有交集,返回-1,有交集,返回交集的面积。int area(rect const& a, rect const& b){...} 点评: healer_kx:
补齐代码,最好是简洁的,别用库。你可以写你的辅助函数,宏定义,代码风格也很重要。ri_aje:struct rect {
// axis alignment assumed // bottom left is(x[0],y[0]), top right is(x[1],y[1])double x [2];double y [2];};
template
// return type changed to handle non-integer rects double area(rect const& a, rect const& b){
// perfectly adjacent rects are considered having an intersection of 0 area double const dx = min(a.x[1],b.x[1])max(a.y[0],b.y[0]);return dx>=0&&dy>=0 ? dx*dy :-1; } 下面是一个简短的证明。
对于平行于坐标轴的矩形 r,假设其左下角点坐标为(rx0,ry0),右上角点坐标为(rx1,ry1),那么由 r 定义的无限有界点集为:{(x,y)|x in [rx0,rx1] && y in [ry0,ry1]}。
根据交集的定义,则任意二维点(x,y)在矩形 a,b 的交集内等价于 {(x,y)|(x,y)in a 并且(x,y)in b} <==>
{(x,y)|x in [ax0,ax1] && x in [bx0,bx1] 并且 y in [ay0,ay1] && y in [by0,by1]} <==> {(x,y)|x in [max(ax0,bx0),min(ax1,bx1)] 并且 y in [max(ay0,by0),min(ay1,by1)]}
因此,交集矩形的边长分别为 min(ax1,bx1)-max(ax0,bx0)和 min(ay1,by1)-max(ay0,by0)。注意当交集为空时(a,b 不相交),则经此法计算出来的交集边长为负值,此事实可用于验证 a,b 的相交性。
鉴于笛卡尔积各个维度上的不相关性,此方法可扩展到任意有限维线性空间,比如,三维空间中平行于坐标轴的长方体的交集体积可以用类似的方法计算。
2012年创新工场校园招聘最后一道笔试题:工场很忙
创新工场每年会组织同学与项目的双选会,假设现在有M个项目,编号从1到M,另有N名同学,编号从1到N,每名同学能选择最多三个、最少一个感兴趣的项目。选定之后,HR会安排项目负责人和相应感兴趣的同学一对一面谈,每次面谈持续半小时。由于大家平时都很忙,所以咱们要尽量节约时间,请你按照以下的条件设计算法,帮助HR安排面试。
1)同学很忙。项目负责人一次只能与一名同学面谈,而同学会在自己第一个面试开始时达到工场,最后一个面试结束后离开工场,如果参加一个项目组的面试后不能立即参加下一个项目组的面试,就必须在工场等待。所以请尽可能让同学的面试集中在某一时间段,减少同学在工场等待的时间。
2)项目负责人很忙。众所周知,创业团队的负责人会有很多事情要做,所以他们希望能够将自己参与的面试集中在某一段时间内,请在保证1)的情况下,使得项目负责人等待的时间最少。
3)HR很忙。从第一轮面试开始以后,所有HR都必须等到最后一轮面试结束,所以需要在保证1)和2)的同时,也能尽快解放掉所有的HR,即让第一轮面试到最后一轮面试之间持续的时间最短。
输入(以文件方式输入,文件名为iw,例如iw.in):
第1行...第n行:同学的编号 项目的编号
样例(数据间用空格隔开,两个0表示输入结束):
112
0 0
表示M=3,N=3,编号为1的同学选择了项目1,2和3,编号为2的同学选择了项目1,编号为3的同学选了项目1和2
输出(以文件方式输出,文件名为iw,例如iw.out):
第1行:编号为1的项目依次面试新同学的编号序列
第2行:编号为2的项目依次面试新同学的编号序列...第n行:编号为n的项目依次面试新同学的编号序列
样例(数据间用空格隔开,0表示没有面试):3 21 0 0 0 1
表示编号为1的项目在第一轮面试编号为1的同学,第二轮面试编号为3的同学,第三轮面试编号为2的同学
编号为2的项目在第一轮面试编号为3的同学,第二轮面试编号为1的同学,第二轮不用面试
编号为3的项目在第一轮和第二轮都不用面试,第三轮面试编号为1的同学
4**9 的笔试题,比较简单: 1.求链表的倒数第二个节点
2.有一个整数数组,求数组中第二大的数 阿里巴巴二道题 第一道:
对于给定的整数集合S,求出最大的d,使得a+b+c=d。a,b,c,d互不相同,且都属于S。集合的元素个数小于等于2000个,元素的取值范围在[-2^28,2^281)^2)。最后我给出了一个巧妙的证明。然后发现如果是m*n的矩阵也是类似的答案,不局限于方阵。此外,题目具体描述可以看看这里:
9月27日,小米两面:
一面:
除了聊研究,就一道题
数组里找到和最接近于0的两个值。二面:
行列有序的矩阵查找一个数
直方图最大矩形。点评:这里有此题的具体表述及一份答案: 3 next_permutation 字符串匹配 含有* ?(写代码)
实现strcpy memmove(必须写代码)//void * memmove(void * destination, const void * source, size_t num);) //是
//最简单的方法是直接复制,但是由于它们可能存在内存的重叠区,因此可能覆盖了原有数据。//比如当source+count>=dest&&source //解决办法是从后往前拷贝。//对于其它情况,则从前往后拷贝。 void* memmove(void* dest, void* source, size_t count){ void* ret = dest; if(dest <= source || dest >=(source + count)){ //正向拷贝 //copy from lower addresses to higher addresses while(count--)*dest++ = *source++;} else { //反向拷贝 //copy from higher addresses to lower addresses dest += count1; while(count--)*dest--= *source--;} return ret;} 6 读数(千万亿,百万亿„„)变为数字(说思路即可,字符串查找,填写各个权值的字段,然后判断是否合法,读前面那些×权值,累加)。 9月27日,Hulu 2013北京地区校招笔试题 填空题: 1、中序遍历二叉树,结果为ABCDEFGH,后序遍历结果为ABEDCHGF,那么前序遍历结果为? 2、对字符串HELL0_HULU中的字符进行二进制编码,使得字符串的编码长度尽可能短,最短长度为? 3、对长度12的有序数组进行二分查找,目标等概率出现在数组的每个位置上,则平均比较次数为? 4、一副扑克(去王),每个人随机的摸两张,则至少需要多少人摸牌,才能保证有两个人抽到同样的花色。 5、x个小球中有唯一一个球较轻,用天平秤最少称量y次能找出这个较轻的球,写出y和x的函数表达式y=f(x)6、3的方幂及不相等的3的方幂的和排列成递增序列1,3,4,9,10,12,13„„,写出数列第300项 7、无向图G有20条边,有4个度为4的顶点,6个度为3的顶点,其余顶点度小于3,则G有多少个顶点 8、桶中有M个白球,小明每分钟从桶中随机取出一个球,涂成红色(无论白或红都涂红)再放回,问小明将桶中球全部涂红的期望时间是? 9、煤矿有3000吨煤要拿到市场上卖,有一辆火车可以用来运煤,火车最多能装1000吨煤,且火车本身需要烧煤做动力,每走1公里消耗1吨煤,如何运煤才能使得运到市场的煤最多,最多是多少? 10、1,2,3,4„..n,n个数进栈,有多少种出栈顺序,写出递推公式(写出通项公式不得分) 11、宇宙飞船有100,000位的存储空间,其中有一位有故障,现有一种Agent可以用来检测故障,每个Agent可以同时测试任意个位数,若都没有故障,则返回OK,若有一位有故障,则失去响应。如果有无限多个Agent可供使用,每个Agent进行一次检测需要耗费1小时,现在有2个小时时间去找出故障位,问最少使用多少个Agent就能找出故障。 (总共12道填空题,还有一道太复杂,题目很长,还有示意图,这里没有记录下来)大题: 1、n个数,找出其中最小的k个数,写出代码,要求最坏情况下的时间复杂度不能高于O(n logk) 2、写程序输出8皇后问题的所有排列,要求使用非递归的深度优先遍历 3、有n个作业,a1,a2„..an,作业aj的处理时间为tj,产生的效益为pj,最后完成期限为dj,作业一旦被调度则不能中断,如果作业aj在dj前完成,则获得效益pj,否则无效益。给出最大化效益的作业调度算法。 有道的一个笔试题,1-9,9个数组成三个三位数,且都是完全平方数(三个三位数 占据 9个数)求解法。 点评@林晚枫&归云见鸿:(a*10+b)(a*10+b)100a^2+20ab+b^2 a 属于 [1,2,3] a=3,b=1 31 961, a=2,b=3 23 529 400+40b+b^2 25 625 27 729 28 784 29 841 a=1,b=3 13 169 100+20b+b^2 14 196 16 256 17 289 18 324 19 361 =>最终唯一解 529 784 361 具体代码如下(3个for循环,然后hash): 9月28日,大众点评北京笔试题目: 1.一个是跳台阶问题,可以1次一级,1次两级,1次三级,求N级的跳法一共多少种? 点评:老题, 2.一个文件有N个单词,每行一个,其中一个单词出现的次数大于N/2,怎么样才能快速找出这个单词? 点评:还是老题, 大众点评前面还有30道逻辑题,15道文字推理,15道数学推理,一共只给20min。 9月28日,网易笔试题: 1、英雄升级,从0级升到1级,概率100%。 从1级升到2级,有1/3的可能成功;1/3的可能停留原级;1/3的可能下降到0级; 从2级升到3级,有1/9的可能成功;4/9的可能停留原级;4/9的可能下降到1级。每次升级要花费一个宝石,不管成功还是停留还是降级。求英雄从0级升到3级平均花费的宝石数目。 点评:题目的意思是,从第n级升级到第n+1级成功的概率是(1/3)^n(指数),停留原级和降级的概率一样,都为[1-(1/3)^n]/2)。 2、将一个很长的字符串,分割成一段一段的子字符串,子字符串都是回文字符串。有回文字符串就输出最长的,没有回文就输出一个一个的字符。例如: habbafgh 输出h,abba,f,g,h。 点评:一般的人会想到用后缀数组来解决这个问题, 10月9日,腾讯一面试题: 有一个log文件,里面记录的格式为: QQ号: 时间: flag: 如123456 14:00:00 0 123457 14:00:01 1 其中flag=0表示登录 flag=1表示退出 问:统计一天平均在线的QQ数。 点评:第8题后的腾讯面试题,读者可以参看之。 10月9日,腾讯面试题: 1.有一亿个数,输入一个数,找出与它编辑距离在3以内的书,比如输入6(0110),找出0010等数,数是32位的。2.每个城市的IP段是固定的,新来一个IP,找出它是哪个城市的,设计一个后台系统。 10月9日,YY笔试题: 输出一个字符串中没有重复的字符。如“baaca”输出“bac”。对于一个多叉树,设计TreeNode节点和函数,返回先序遍历情况下的下一个节点。函数定义为TreeNode* NextNode(TreeNode* node)3 分割字符串。 对于一个字符串,根据分隔符seperator,把字符串分割,如果存在多个分隔符连在一起,则当做一个分隔符。如果分隔符出现在“ ”符号之间,则不需要分割“ ”之间的字符。比如a++abc,分隔符为+,输出a abc a+“hu+” 输出a hu+ a++“HU+JI 输出a ”HU JI。 请根据上述需求完成函数:void spiltString(string aString,char aSeperator)。 10月9日,赶集网笔试 10月9日,阿里巴巴2013校园招聘全套笔试题(注:下图中所标答案不代表标准答案,有问题,欢迎留言评论) 上述第15题,填空:lower+(upper-lower)/2 lower mid upper 0 6 12 7 9 12 7 7 8 8 8 8 比较4次 上述第16题,解答如下图所示: 上述第17题,解答如下图所示: 18、甲包8个红球 2个蓝球,乙包2个红球 8个蓝球。抛硬币决定从哪个包取球,取了11次,7红4蓝。注,每次取后还放进去,只抛一次硬币。问选的是甲包的概率? 点评: 贝叶斯公式 + 全概率公式作答(参看链接:http://)。具体解答如下图所示: 注:上述第15~18的解答全部来自读者Lei Lei来信给出的解答,特此感谢。有任何问题,欢迎随时讨论&指正,同时,更欢迎其他朋友也一起来做这些题目(你的答案一经选用,我可以根据你的要求,贴出你的个人主页或微博地址或博客地址)。 19、已知一个n个元素的数组,第i个元素在排序后的位置在[i-k,i+k]区间,k< 10月10日,暴风影音笔试: 都是非常基础的题目,这是其中一道:一个整数转换成二进制后,问里面有多少个1。10月10日人人网面试题 第一面: 1、(1)++i 和 i++,那个效率高? (2)++++i,i++++,哪个是合法的? (3)实现int型的++i 和 i++操作。 2、一段程序,求输出。(考察静态变量和模版类) int g = 0;template int main(){ cout << B 3、(1)实现二进制转十进制。 (2)如果有下面这种能直接求二进制转十进制的代码,是怎么实现的? binary<1>::value;// 结果为1 binary<11>::value;// 结果为3 4、volatile、explicit、mutable表示的含义。 5、求整形数组的一个子数组,使得该子数组所有元素的和的绝对值最大。 6、(1)写求单链表是否有环的算法。(2)如果有环,如何找出环的第一个结点。 7、实现单例模式。二面: 1、一个文本,一万行,每行一个词,统计出现频率最高的前10个词(词的平均长度为Len)。并分析时间复杂度。 2、求数组中最长递增子序列。 10月10日,网易2013校园招聘全套笔试题: 10月10日,网易,数据挖掘工程师: 1,简述你对数据与处理的认识; 2,简述你对中文分词的理解,说明主要难点和常用算法; 3,常见的分类算法有哪些; 4,简述K-MEANS算法; 5,设计一个智能的商品推荐系统; 6,简述你对观点挖掘的认识。其它题目同 点评:其它题目与上述第56题第一部分所述相同。 一、选择题(1)~(10)每小题2分,(11)~(50)每小题1分,共60分) 下列各题A)、B)、C)、D)四个选项中,只有一个选项是正确的,请将正确的选项涂写在答题卡相应位置上,答在试卷上不得分。 (1)在数据结构中,从逻辑上可以把数据结构分为_______。 A)动态结构和静态结构 B)紧凑结构和非紧凑结构 C)线性结构和非线性结构 D)内部结构和外部结构 答案:C 评析:逻辑结构反映数据元素之间的逻辑关系,线性结构表示数据元素之间一对一的关系,非线性结构表示数据元素之间一对多或多对一的关系。 (2)若进栈序列为l,2,3,4,进栈过程中可以出栈,则下列不可能的一个出栈序列是_______。 A)1,4,3,2 B)2,3,4,l C)3,1,4,2 D)3,4, 2,1 答案:C 评析:栈是一种后进先出表,选项c中,先出栈的是3,说明此时栈内必然有1,2,由于l先于2进栈,所以l不可能在2之前出栈,故选项C这种出栈序列是不可能的。 (3)排序方法中,将整个无序序列分割成若干小的子序列并分别进行插入排序的方法,称为_______。 A)希尔排序 B)冒泡排序 C)插入排序 D)选择排序 答案:A 评析:希尔排序法的基本思想是:将整个无序序列分割成若干小的子序列分别进行插入排序。 (4)在顺序表(3,6,8,10,12,15,16,18,21,25,30)中,用二分法查找关键码值11,所需的关键码比较次数为_______。 A)2 B)3 C)4 D)5 答案:C 评析:二分法查找是用关键码与线性表的中间元素比较,然后根据比较结果来判断是结束查找,还是在左边或者右边子表按相同的方法继续查找。本题中,与ll比较的关键码分别为15,8,10,12四个。 (5)对于n个结点的单向链表(无表头结点),需要指针单元的个数至少为_______。 A)n-1 B)n C)n+l D)2n 答案:C 评析:在n个结点的单向链表(无表头结点)中,每个结点都有一个指针单元(即指针域),加上头指针,至少需要n+1个指针单元。 (6)在软件开发过程中,软件结构设计是描述_______。 A)数据存储结构 B)软件体系结构 C)软件结构测试 D)软件控制过程 答案:B 评析:从工程管理角度来看,软件设计分两步完成:概要设计和详细设计。概要设计(又称结构设计)将软件需求转化为软件体系结构、确定系统级接口、全局数据结构或数据库模式。 (7)模块本身的内聚是模块独立性的重要性度量因素之一。在7类内聚中,具有最强内聚 的一类是_______。 A)顺序性内聚 B)过程性内聚 C)逻辑性内聚 D)功能性内聚 答案:D 评析:内聚性是一个模块内部各元素间彼此结合的紧密程度的度量。内聚共有7类,它们之间的内聚性由弱到强排列顺序为:偶然内聚、逻辑内聚、时间内聚、过程内聚、通信内聚、顺序内聚和功能内聚。 (8)数据存储和数据流都是_______,仅仅是所处的状态不同。 A)分析结果 B)事件 C)动作 D)数据 答案:D 评析:数据流图有4种成分:源点或终点、处理、数据存储和数据流。数据存储是处于静止状态的数据,数据流是处于运动中的数据。 (9)数据的完整性是指数据的正确性、有效性和_______。 A)可维护性 B)独立性 C)安全性 D)相容性 答案:D 评析:数据模型的完整性规则是给定的数据模型中数据及其联系所具有的制约和依存规则,用以限定符合数据模型的数据库状态及其状态的变化,以保证数据的正确性、有效性和相容性。 (10)关系代数运算是以_______为基础的运算。 A)关系运算 B)谓词运算 C)集合运算 D)代数运算 答案:C 评析:关系代数运算是以关系代数作为运算对象的一组高级运算的集合。它的基本操作是并、交、差、笛卡尔积,另外还包垂直分割(投影)、水平分割(选择)、关系的结合(连接)等。 (11)能将高级语言程序转换成目标语言程序的是_______。 A)调试程序 B)解释程序 C)编译程序 D)编辑程序 答案:C 评析:用高级语言编写的程序称为“源程序”,而计算机只能识别和执行由0和l组成的二进制指令,所以高级语言必须先用一种称为“编译程序”的软件,把源程序翻译成二进制形式的“目标程序”。 (12)_______是构成c语言程序的基本单位。 A)函数 B)过程 C)子程序 D)子例程 答案:A 评析:c程序是由函数构成的。一个c源程序至少包含一个main函数,也可以包含一个main函数和若干个其他函数,因此,函数是c程序的基本单位。 (13)可以在C语言中用做用户标识符的是_______。 A)void B)as_b3 C)for D)2c define _123-abc Do WORD If cas SIG 答案:B 评析:c语言规定,标识符只能由字母、数字和下划线三种符号组成,而且第一个字符必须是字母或下划线。另外还需要注意的是关键字不能作标识符。选项A中void,C中for都为关键字,D中2c以字母开头。 (14)若有以下类型说明语句: char w;int x;float y,z; 则表达式w*x+z-y的结果为________类型。 A)float B)char C)int D)double 答案:A 评析:在进行运算时,不同类型的数据参加运算,需要先将其转换成同一类型的数据,然后再进行运算。转换的顺序由低到高为:char,short→int→unsigned→long→double→float,故结果为float型。 (15)main(() { float x=123A56; printf(“%-5.2fn”,x); } 以上程序输出的结果是________。 A)123.4 B)123.5 C)123.45 D)123.46 答案:D 评析:f格式符,用来输出实数,以小数形式输出。“%-m.nf”的含义是:输出数据共占m列,其中n位小数,如果输出位数小于m。则右端补空格。如果总长度大于列数,则按实际情况四舍五入输出。 (16)下面语句的输出结果是________。 Printf(“%d\n”,strlen(“\t\”\065\xff\n”)); A)14 B)8 C)5 D)输出项不合法,无正常输出 答案:C 评析:在c语言中,以“\”开头的字符均为转义字符,其中“\”后可跟l~3位八进制数或在“\”后跟字母x及l~2位十六进制数,以此来代表一个特定的字符。 (17)下列程序的输出结果是________。 main() { int a=0,b=0,c=0; if(++a>0lI++b>0)++c; printf(“\na=%d,b=%d,c=%d”,a,b,C); } A)a=0,b=0,c=0 B)a=l,b=l,c=1 C)a=l,b=O, c=I D)a=0, b=1.c=1 答案:C 评析: “︱︱”是或运算,它有个“短路”的特点需特别注意,当“︱︱”运算符左边的表达式的值为真时,则程序就不再对“︱︱”右边的表达式的值进行运算,而是使得整个表达式的值直接为真。 (18)下列程序的输出结果是_________。 Main() { int i; for(i=1;i+l;i++) { if(i>4){printlf(”%d”,i++);break;} } printf(“%d”,i++); } A)55 B)56 C)程序错误,没有输出 D)循环条件永远为真,死循环 答案:B 评析:本程序中有个for循环,但注意到for循环的条件是“i+l”,也就是只要i+l的值为真(非零值均为真),就执行循环。当i=l的时,i+l的值为真,判断if条件不成立,执行i++,输出i的值为5。 (19)下列程序的输出结果是_________。 #define A 100 main() { int i=O,sum=O; do{ if(I==(i/2)*2)continue; sum+=i; }while(++i printf(“%d\n”,sum); } A)2500 B)2050 C)4 D)O 答案:A 评析:本题程序的功能是求1到_99之问(包括1和99)所有奇数之和。程序中的while循环的终止条件为++i=100,在while循环体内,如果i是偶数,则执行continue,跳过这一次循环,执行下一次循环,否则求和。最后输出的值是1到99之间(包括l和99)所有奇数之和(1+99)*50/2=2500。 (20)下列程序的输出结果是_________。 main() { int I=3; switch(i) { case 1: case 2:printf(”%d”,i); case 3: case 4:break; default:printf(”OK”); } } A)0 B)3 C)OK D)没有任何输出 答案:D 评析:在题中,i的值为3,由于“case 3:”后面没有break语句,所以继续向下执行“case 4:”后面的语句,由于“case 4:”后面的语句为break强行退出switch语句,所以,本题没有任何输出。 (21)下列程序执行后的输出结果是________。 main() { int m[][3]={1,4,7,2,5,8,3,6,9}; int i,k=2: for(I=0;i<3;i++) {printf(”%d”,m[k][i]);} } A)456 B)258 C)369 D)789 答案:C 评析:根据二维数组的定义得出:m[O][O]=1,m[O][1]=4,m[O][2]=7,m[1][0]=2,rail][1]=5,m[1][2]=8,m[2][0]=3,m[2][l]=6,m[2][2]=9,所以本题的输出是第3行的值m[2][0],m[2][1],m[2][2],即369。 (22)设已定义洱口k为int类型变量,则以下for循环语句_________。 for(i=0;k=-1,k=1;i++,k++) printf(”****\n”); A)判断循环结束的条件不合法 B)是无限循环 C)循环一次也不执行 D)循环只执行一次 答案:B 评析:本题定义了一个for循环,循环变量是i,但由于本题并没有设置循环条件,所以循环的条件永远默认为真,即无限次执行循环。 (23)下面程序的输出结果是___________。 unsigned fun(unsigned num) { unsigned k=1; do{ k*=num%lO; num/=lO; }while(num); return(k); } main() { unsigned n。26; printf(”%d\n”,fun(n)); } A)0 B)4 C)12 D)无限次循环 答案:C 评析:本题定义了一个fun函数,用于num求和,具体执行过程如下: num=26:k=k*(num%10)=1*(26%10),所以k=6,num=num/10=2; num=2:k=k*(num%10)=6*(2%10),所以k=12,num=num/lO=O; num=O:while条件不成立,所以返回k的值12. (24)已知字母A的ASCII码值是65,字母a的ASCII码值是97,以下程序_______。 main() { char a=‘A’; int b=20; printf(“%d,%o”,(a=a+a,a+b,b),a+‘a’-‘A’,b); } A)表达式非法,输出零或不确定值 B)因输出项过多,无输出或输出不确定值 C)输出结果为20,141 D)输出结果为20,141,20 答案:C 评析:本题中首先输出逗号表达式“a=a+a,a+b,b”的值,即20。然后以八进制的形式输出a+‘a’-‘A’的值为97对应的八进制数141,由于最后一个表达式b没有对应输出格式的输出项表列就不会输出。 (25)C语言函数返回值的类型是由__________决定的。 A)return语句中的表达式类型 B)调用函数的主调函数类型 C)调用函数时临时 D)定义函数时所指定的函数类型 答案:D 评析:函数值的类型应当是在定义函数时指定的。在定义函数时对函数值说明的类型一般应该和return语句中的表达式类型一致,如果不_致,则以函数类型为准,即函数类型决定返回值的类型。 (26)下列程序执行后输出的结果是___________。 int d=l: fun(int p) { int d。5; d+=p++; printf(”%d,”,d); } main() { int a=3; fun(a); d+=a++: printf(”%d\n”,d); } A)8,12 B)9,13 C)8,4 D)9,5 答案:C 评析:本题执行过程如下:首先调用fun函数,使得实参a的值3传递给形参p,得到局部变量d=8,打印出局部变量d的值8;返回主函数执行“d+=a++”,此处的d为全局变量,所以d=1+3=4(由于本题是值传递,所以在函数fun中对p值的改变并不能引起a的改变),故本题的输出是8,4。 (27)已知下面的程序段,正确的判断是_________。 #define A 3 #define B(A)((_A+1)‘a) int a=3: …… X=3*(A+B(7)); A)程序错误,不允许嵌套定义 B)X=93 C)X=8l D)程序错误,宏定义不允许有参数 答案:C 评析:本题的宏定义是合法的,宏定义展开为3*(3+((A+1)*a))=3*(3+((7+1)*3))=81。 (28)定义int*swap()指的是_______。 A)一个返回整型值的函数swap() B)一个返回指向整型值指针的函数swap() C)一个指向函数swap()的指针,函数返回一个整型值 D)以上说法均错 答案:B 评析:一个函数可以带回一个整型值、字符值、实型值等,但也可以带回指针型数据,即地址。本题的定义中,包括括号和·号,由于f)优先级高于t。故它是一个返回整型指针的函数。 (29)以下程序段的输出结果是__________。 main() { char s1[10],s2[10],s3[10]; scanf(”%s”,s1);gets(s2);gets(s3); puts(s 1);puts(s2);puts(s3); } 输入数据如下:(此处代表回车符)aaa bbb A)aaa B)aaa C)aaa\0bbb\0 D)aaabbb bbb bbb 答案:B 评析:scanf是标准输入函数,在输入字符串aaa时,实际的内容为“aaa ”,“\0”是由系统自动加入的;gets的功能是从终端读入一行字符,即一直读到换行符为止,并由系统自动以“\0”代替换行符。 (30)下述函数功能是________。 Int fun(char*x) { char*y=x; while(*y++); return y-x-l; } A)求字符串的长度 B)求字符串存放的位置 C)比较两个字符串的大小 D)将字符串x连接到字符串y后面 答案:A 评析:在函数体内定义一字符型指针并指向形参,然后遍历其中各字符直到NULL,最后返回字符串首尾地址的差值,即字符串的长度。 (31)以下程序的输出结果是_________。 main() { char str[12]={‘s’,‘t’,‘r’,‘i’,‘n’,‘ g’}; printf(”%d\n”,strlen(str)); } A)6 B)7 C)ll D)12 答案:A 评析:在c语言中,字符串的长度是其首字符到NULL(不含)字符的总字符个数。本题定义字符数组str的同时,对第7个元素,由系统自动添加上“\0”,故字符串的长度为6。 (32)请读程序段: char str[]=”ABCD”,*p=str; printf(”%d\n”,*(p+4)); 程序段的输出结果是_________。 A)68 B)0 C)字符‘D’的地址 D)不确定的值 答案:B 评析:在对字符数组赋字符串值时,系统会自动在字符串的末尾加上一个字符串结束标志“\0”,故指向字符数组的指针p的+(p+4)的值为“\0”。由于“\0”的编码值就是0,所以本题输出为0。 (33)若有定义:int a[4][10];,则以下选项中对数组元素a[i][j]引用错误的是________。 (0<=i<4,0<=j<10) A)*(&a[O][O]+10*i+j)B)*(a+i)+j C)*(*(a+i)+j)D)*(a[i]+j) 答案:B 评析:本题中选项B是错误的引用,*(a+i)+j只代表了a[i][i]的地址。 (34)设有以下语句: char strl[]=”string”,str2[8]。str3。str4=”strin∥; 则__________不是对库函数的正确调用。 A)strcpy(strl,”HELLOl”); B)strcpy(str2,”HELL02”); C)strcpy(str3,”HELL03”); D)strcpy(str4,”HELL04”); 答案:C 评析:c语言中:sgcpy(stl,st2);,其两个参数均为字符指针或字符数组,选项c中的目的串指针str3没有指向具体有效的存储单元,故是错误的调用。 (35)请读程序: #include #include main(){ char*sl=”AbCdEf”,*s2=”aB”; s1++;s2++; printf(”%d\n”,strcmp(s 1,s2)); } 上面程序的输出结果是___________。 A)正数 B)负数 C)零 D)不确定的值 答案:A 评析:函数strcmp的功能是比较字符串s1和s2,如果sl>s2,则返回个正数;如果sls2,所以函数的值为正数。 (36)下面程序的输出是_________。 char s[]=”ABcD”; main() { char*p; for(p=s;p printf(”%s\n”,p); } A)ABCD B)A C)D D)ABCD BCD B C ABC CD C B AB D D A A 答案:A 评析:在第一次执行for循环时,字符数组的首地址赋给了指针变量p,使得指针变量p指向了s的首地址,输出p所指向的字符串;第二次执行for循环时,p值增加1,p指向了s的第二个元素输出BCD;第三次输出CD;第四次输出D;直到p指向字符串的结束字符“\0”,for循环终止执行。 (37)以下程序输出的结果为__________。 main() { char* alpha[6]={“ABCD”,EFGH”,”IJKL”,”MNOP”,”QRST”,”UVwX”}; char**p; int i: p=alpha; for(I=0;i<4;i++) printf(”%s”,p[I]); } A)ABCDEFGHIJKL B)ABCD C)ABCDEFGHIJKLMNOP D)AEIM 答案:C 评析:alpha[O]指向“ABCD”的首地址;alpha[1]指向“EFGH”的首地址;alpha[2]指向“IJKL”的首地址,依此类推。当执行p=alpha后,p指向指针数组alpha的首地址。for循环中输出了4个字符串。 (38)下面程序的输出结果是_________。 #include main() { char*p[]={”B00L”,”0PK”,”H”,”SP”}; int i: for(i=3;i>=0;i–,i–) printf(“%c”,*p[i]); printf(”\n”); } A)SO B)SP C)SPOPK D)SHOB 答案:A 评析:p[0]存放的是“BOOL\0”的首地址;p[1]存放的是“OPK\0”的首址等。 在printf语句中输出的+p[I]表示p[i]字符串的第一个字符。在for循环中,i的初值为3,那么输出的第一个字符为“s”,接着两次i–,则输出的值为+p[1],即字符“0”,所以本题的输出为SO。 (39)以下程序的输出结果是_________。 #include void prt(int*x,int*y,int*z) { printf(”%d,%d,%d\n”,++*x,++*y*(z++));} int a=10,b=40,c=20; main() { prt(&a,&b&C); prt(&a,&b,&C); } A)ll,42,3l B)ll,41,20 C)1l,21,40 D)11,41,2l 12,22,41 12,42,20 11,2l,41 12,42,22 答案:B 评析:由于实参传送的是变量的地址,所以对形参指针所指向的单元内容的改变,即对实参内容的改变。 (40)若一个外部变量的定义形式为static int x;,那么,其中static的作用应该是_______。 A)将变量存储在静态存储区 B)使变量x可以由系统自动初始化 C)使x只能在本文件内引用 D)使x的值可以永久保留 答案:C 评析:事实上,无论有无static修饰,外部变量都具有A、B和c三种特性。作为一种修饰,static仅是限制此类型外部变量的引用范围:只能在定义它的文件范围内使用。 (41)以下程序的输出结果是________。 #include #define SQR(x)x*x main() { int a,k=3; a=++SQR(k+1); printf(”%d\n”,a); } A)8 B)9 C)17 D)20 答案:B 评析:本题宏替换中遇到形参x以实参k+l代替,其它字符不变。sQR(k+1)展开后应为字符串k+l*k+l。 (42)下面是对宏定义的描述,不正确的是_______。 A)宏不存在类型问题,宏名无类型,它的参数也无类型 B)宏替换不占用运行时间 C)宏替换时先求出实参表达式的值,然后代入形参运算求值 D)宏替换只不过是字符替代而已 答案:C 评析:宏替换实质上就是字符替代,它不可能进行计算,故c错误。带参数的宏与函数相比,宏在程序编译之前已经将代码替换到程序内,执行时不会产生类似于函数调用的问题,可以说不占用运行时间。 (43)以下程序(程序左边的数字为附加的行号)________。 1#include 2#include 3main() 4{char s[]=”string”; puts(s); strcpy(s,”hello”); printf(”%3s\n”,s);} A)没有错 B)第l行有错 C)第6行有错 D)第7行有错 答案:B 评析:字符串复制函数strcpy包含在头文件string.h中,因此,程序中的第l行文件包含命令是错误的。 (44)若有如下说明,则__________的叙述是正确的。 struct st { int a; int b[2l; }a; A)结构体变量a与结构体成员a同名,定义是非法的 B)程序只在执行到该定义时才为结构体st分配存储单元 C)程序运行时为结构体st分配6个字节存储单元 D)类型名struct st可以通过extern关键字提前引用(即引用在前,说明在后) 答案:D 评析:结构体变量a与结构体成员a同名是合法的定义,引用成员a的方法是a.a,变量a处于不同的“层次”上,系统完全能够分清。st是一个结构体名,不会为结构体名分配存储空间,应该是在运行时为结构体变量a分配6个字节的存储单元,故选项B和选项C错误。 (45)若有以下结构体定义,则________是正确的引用或定义。 struct example { int x; int y; }v1; A)example.x=10 B)example v2.x=10 C)struct v2;v2.x=lO D)struct example v2={10}; 答案:D 评析:在定义结构体变量时,不能只用结构体名example或关键字strum进行定义,必需要用结构体类型名struct example定义,在引用结构体成员变量时,需要用结构体变量名进行引用,所以选D。 (46)下列程序的执行结果是_________。 #include union un { int i; char c[21; }; void main() { union un x; x.c[0]=10: x.c[1]=1: printf(“\n%d”,x.i); } A)266 B)ll C)265 D)138 答案:A 评析:由于本题定义的是共用体,所以成员表列中的整型变量x与字符数组c共占用同一个存储单元,且此存储单元为2个字节,通常c[O]位于低字节,c[1]位于高字节,所以x.i的值为266。 (47)已知形成链表的存储结构如下图所示,则下述类型描述中的空白处应填_______。 struct link Datanext{ char data; ___________ }node; A)struct link next B)link*next C)struct next link D)struct link*next 答案:D 评析:在单向链表中,由于每个结点需要存储下一个结点的地址,且下一个结点的数据类型与前一个结点的数据类型完全相同,故应为struct link*next。 (48)已知小写字母a的ASCII码为97,大写字母A的ASCII.码为65,以下程序的结果是__________。 main() { unsigned int a=32,b=66; printf(“%c\n”,atb); } A)66 B)98 C)b D)B 答案:C 评析:位运算符“l”的作用是按位或,即两个二进制数的相应位中只要有一个为1,该位的结果值为l。最后以字符型输出,98对应的字符“b”。 (49)C语言库函数龟ets(str,n,fp)的功能是_________。 A)从fp指向的文件中读取长度n的字符串存入str指向的内存 B)从fp指向的文件中读取长度不超过n-l的字符串存入str指向的内存 C)从fp指向的文件中读取n个字符串存/Xstr指向的内存 D)从str读取至多n个字符到文件fp 答案:B 评析:fgets函数的作用是从指定的文件读入一个字符串。fgets(str,n,fp);中的n为要求得到的字符的个数,但只从fb指向的文件输入n-1个字符,然后在最后加一个‘\O’字符,因此得到的字符串共有n个字符。 (50)下述程序向文件输出的结果是__________。 #include void main() { FILE*fp=fopen(“TEST”,”wb”); fprintf(fp,”%d%5.0f%c%d”,58,76273.0,’-',2278); fclose(fp); } A)58 76273-2278 B)5876273。.000000-2278 C)5876273-2278 D)因文件为二进制文件而不可读 答案:C 评析:fprintf函数工作时,多个数据间不会自动加分隔符,选项A错误;浮点数的输出格式是“%5.0f”表明其小数部分输出O位,即没有输出,所以选项B也是错误的。 二、填空题(每空2分,共40分) 请将每空的正确答案写在【l】至【20】序号的横线上,答在试卷上不得分。(1)对于长度为n的顺序存储的线性表,当随机插入和删除一个元素时,需平均移动元素 的个数为 【l】。 答案:【1】n/2 评析:删除一个元素,平均移动的元素个数为(n-l+n-2+……+0)n=(n-1)/2;插入一个元素,平均移动元素个数为(n+n-l+n-2+……+1)n=(n+1)/2;所以总体平均移动元素个数为n/2。 (2)注释说明了程序的功能,它分为 【2】 注释和功能性注释。 答案:【2】序言性 评析:注释一般分为序言性注释和功能性注释。 (3)软件测试中路径覆盖测试是整个测试的基础,它是对软件 【3】 进行测试。 答案:【3】结构 评析:路径测试是白盒测试方法中的一种,它要求对程序中的每条路径最少检查一次,目的是对软件的结构进行测试。 (4)数据库技术的主要特点为数据的集成性、数据的高 【4】 和低冗余性、数据独立性和数据统一管理与控制。 答案:【4】共享性 评析:数据库技术的主要特点有以下几个方面:数据的集成性,数据的高共享性与低冗余性,数据韵独立性,数据统一管理与控制。 (5)数据元素之间 【5】 的整体称为逻辑结构。 答案:【5】逻辑关系 评析:数据元素之间逻辑关系的整体称为逻辑结构。数据的逻辑结构就是数据的组织形式。 (6)若有定义int m=5,y=2,则执行表达式y+=y-=m*=y后,y的值为 【6】。 答案:【6】.16 评析:将赋值运算符右侧的“表达式”的值赋给左侧的变量,并且赋值运算符按照“白右而左”的结合顺序,本题表达式应先算m的值为10,再运算y的值为8,最后计算y=y+(-8)=-8+(-8)=-16。 (7)若x和y都是double型变量,]ix的初值为3.0,y的初值为2.0,则表达式pow(y,fabs(x))的值为 【7】。 答案:【7】8.000000 评析: fabs函数功能是求x的绝对值,计算结果为double型。pow功能是计算x的y次方的值,计算结果同样为double型。所以本题表达式相当于2.0的3.0次方,结果为8.000000。 (8)设有char a,b;,若要通过a&b运算屏蔽掉a中的其它位,只保留第2位和第8位(右起为第1位),则b的二进制是 【8】。 答案:【8】10000010 评析:运算“&”的规则是只有当两个相应的二进制位都为1时,该位的结果才为1。要保留第2、8位,只要将其与二进制数10000010相与。 (9)下列程序执行后输出的结果是 【9】。 f(int a) { static c=0; C=a+C++: return(C); } main() { int a=2,i,k; for(i=O;i<2;i++) k=f(a++); printf(”%d\n”,k); } 答案:【9】7 评析:在程序执行时,static变量仅初始化一次,下次使用时将使用上次保存的值。 (10)下面程序执行后输出的结果是 【10】。 int m=13: int fun(int x,int y) { int m=3; return(x*y-m); } main() { int a=7,b=5; printf(”%d\n”,fun(a,b)/m); } 答案:【10】2 评析:本题变量m既是外部变量(值是13),又是fun函数的局部变量(值为3)。函数fun(x*y-m)的值为7*5-3=32,在main函数中,ftm(a,b)/m中的m应取外部变量的值13,因此输出2。 (11)下列程序执行后输出的结果是 【11】。 main() { nt arr[10],i,k=0; for(I=0;i for(I=1;i<4;i++)k+=arr[i]+i; printf(”%d\n”,k); } 答案:【11】12 评析:本题的第一个fur循环用于给数组arr赋初值,第二个for循环用于求和运算。由于第二个fur循环初始值为1,而循环条件为i<4,所以求的是art[1]到arr[3]及i的和,所以输出结果为12。 (12)下列程序执行后输出的结果是 【12】。 struct s {int x,y;}data[2]={10,100,20,200}; main(){ struct s*p=data; printf(”%dn”,++(p->x)); } 答案:【12】11 评析:程序中结构体数组data首地址(即&data[0])赋值给结构体指针变量p,++(p->x)表示先将p所指向的结构体变量的成员x值加1,然后将此x(即data[01.x]输出。 (13)下面程序有两个printf语句,如果第一个printf语句输出的是194,则第二个print语句的输出结果是 【13】。 main() { int a [10]={1,2,3,4,5,6,7,8,9,0},*p; p=a; printf(”%X\n”,p); printf(”%x\n”,p+9); } 答案:【13】la6 评析:对于指针变量的运算,就是对地址的运算。本题中由于指针指向的是整型变量,所以,使指针变量移动9个位置也就是移动18个字节。注意,本题是以16进制输出的。 (14)以下函数的功能是计算s=l+l/2!+l/3!+…+l/n!,请填空.double fun(int n) { double s=O.O,fac=1.O; int i,k=1; for(i=l;i<=n;i++) { 【14】 ; fat=fat/k; s=s+fac; } } 答案:【14】k=k*i 评析:本题中通过for循环求s表达式中每一项的和,表达式“fac=fac/k;”求的是每一项的值,所以k的值应为n!,在求n!的时候,可以用上次循环阶乘的值乘i,就可以直接得此次n!,故本题填k=k*i。 (15)下面程序的运行结果是 【15】。 main() { unsigned a=0112,x; x=a>>3: printf(”x=%o”,x); } 答案:【15】x=11 评析:在对无符号数的右移是高位补0。 (16)函数delete(s,i,n)是作用是从字符串s中删除从第i个字符开始的n个字符,请填空。 void delete(char s[],int i,int n) { int j,k,length=O; while(s[1ength]) 【16】 ; –i: j=i; } if(【17】) { k=i+n; if(i+n<=length) while(k s[j++]=s[k++]; s[j]=‘\0’;} 答案:【16】length++ 【17】i 评析:第一个循环极有可能是计算串的长度,在i<=length时字符才被删除,被删除的是从第i个到第i+n或最后一个间的所有字符。删除前,应判断i<=length。由于已经进行了一i运算,故实际应填入i (17)下述函数统计一个字符串中的单词个数,单词是指处在空格之间的字符序列,请填空。 int word(char*s) { int num=O,flag=O; while(*s) { if(【18】 ==”)flag=O; else if(【19】){flag=1;num++} } return 【20】 ; } 答案:【18】*s++ 【19】flag==O或*(s-1)==” 【20】num 评析:在统计字符串单词个数的算法中,本题的flag是为了记录一个单词是否结束。第18空应填*s++;如果某个字符不是空格,则必需判断它是否是单词,如是,则使得flag的标志为1,num的值加1。本题判断方法是:先判断s所指向的字符是否为空格,如果是则使得flag=O,否则判断前一个字符是否是空格,如果是则说明这个字符是一个单词的开始,将flag标志为1,num的值加1,如果不是,则不必记录。故第19空应flag==O或*(s-1)==”;最后一个空格需填写的是返回的单词的个数,即num。 网络安全应具有以下五个方面的特征: 保密性:信息不泄露给非授权用户、实体或过程,或供其利用的特性。 完整性:数据未经授权不能进行改变的特性。即信息在存储或传输过程中保持不被修改、不被破坏和丢失的特性。 可用性:可被授权实体访问并按需求使用的特性。即当需要时能否存取所需 的信息。例如网络环境下拒绝服务、破坏网络和有关系统的正常运行等都属于对可用性的攻击; 可控性:对信息的传播及内容具有控制能力。 可审查性:出现安全问题时提供依据与手段第一部分:基本概念及其它问答题 1、关键字static的作用是什么? 这个简单的问题很少有人能回答完全。在C语言中,关键字static有三个明显的作用: 1).在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。 2).在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。 3).在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。(本地化数据和代码范围的好处和重要性)。 2、“引用”与指针的区别是什么? 答、1)引用必须被初始化,指针不必。 2)引用初始化以后不能被改变,指针可以改变所指的对象。3)不存在指向空值的引用,但是存在指向空值的指针。 指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。 流操作符<<和>>、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数、其它情况都推荐使用引用。 3、.h头文件中的ifndef/define/endif 的作用? 答:防止该头文件被重复引用。 4、#include 与 #include “file.h”的区别? 答:前者是从Standard Library的路径寻找和引用file.h,而后者是从当前工作路径搜寻并引用file.h。 5、描述实时系统的基本特性 答 :在特定时间内完成特定的任务,实时性与可靠性。 6、全局变量和局部变量在内存中是否有区别?如果有,是什么区别? 答 :全局变量储存在静态数据区,局部变量在堆栈中。 7、什么是平衡二叉树? 答 :左右子树都是平衡二叉树 且左右子树的深度差值的绝对值不大于1。 8、堆栈溢出一般是由什么原因导致的? 答 :1.没有回收垃圾资源2.层次太深的递归调用 9、冒泡排序算法的时间复杂度是什么?答 :O(n^2) 10、什么函数不能声明为虚函数?答:constructor 11、队列和栈有什么区别?答:队列先进先出,栈后进先出 12、不能做switch()的参数类型答 :switch的参数不能为实型。 13、局部变量能否和全局变量重名?答:能,局部会屏蔽全局。要用全局变量,需要使用”::” 局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义一个同名的局部变量,而那个局部变量的作用域就在那个循环体内 14、如何引用一个已经定义过的全局变量? 答、可以用引用头文件的方式,也可以用extern关键字,如果用引用头文件方式来引用某个在头文件中声明的全局变量,假定你将那个变量写错了,那么在编译期间会报错,如果你用extern方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错。 15、全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么? 答、可以,在不同的C文件中以static形式来声明同名全局变量。可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量赋初值,此时连接不会出错。 16、语句for(;1 ;)有什么问题?它是什么意思?答、和while(1)相同,无限循环。 17、do„„while和while„„do有什么区别?答、前一个循环一遍再判断,后一个判断以后再循环。 18、statac 全局变量、局部变量、函数与普通全局变量、局部变量、函数 答、全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。从以上分析可以看出,把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。 static函数与普通函数作用域不同。仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件 static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他文件单元中被引用;static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值; static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝 19、程序的内存分配 答:一个由c/C++编译的程序占用的内存分为以下几个部分 1、栈区(stack)—由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 2、堆区(heap)—一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。 3、全局区(静态区)(static)—全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。 4、文字常量区—常量字符串就是放在这里的。程序结束后由系统释放。 5、程序代码区—存放函数体的二进制代码 例子程序 这是一个前辈写的,非常详细 //main.cpp int a=0;//全局初始化区 char *p1;//全局未初始化区 main(){ intb;栈 char s[]=”abc”;//栈 char *p2;//栈 char *p3=”123456″;//123456 在常量区,p3在栈上。static int c=0; //全局(静态)初始化区 p1 =(char*)malloc(10);p2 =(char*)malloc(20);//分配得来得10和20字节的区域就在堆区。 strcpy(p1,”123456″);//123456 放在常量区,编译器可能会将它与p3所向”123456″优化成一个地方。} 20、解释堆和栈的区别 答:堆(heap)和栈(stack)的区别(1)申请方式 stack:由系统自动分配。例如,声明在函数中一个局部变量int b;系统自动在栈中为b开辟空间 heap:需要程序员自己申请,并指明大小,在c中malloc函数如p1=(char*)malloc(10);在C++中用new运算符,如p2=(char*)malloc(10);但是注意p1、p2本身是在栈中的。(2)申请后系统的响应 栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。 (3)申请大小的限制 栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。 堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。(4)申请效率的比较: 栈:由系统自动分配,速度较快。但程序员是无法控制的。 堆:是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.另外,在WINDOWS下,最好的方式是用Virtual Alloc分配内存,他不是在堆,也不是在栈,而是直接在进程的地址空间中保留一块内存,虽然用起来最不方便。但是速度快,也最灵活。(5)堆和栈中的存储内容 栈:在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。 当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。 堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容由程序员安排。(6)存取效率的比较 char s1[]=”aaaaaaaaaaaaaaa”;aaaaaaaaaaa是在运行时刻赋值的; char *s2=”bbbbbbbbbbbbbbbbb”;而bbbbbbbbbbb是在编译时就确定的; 但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。比如: #include voidmain(){ char a=1;char c[]=”1234567890″;char *p=”1234567890″;a = c[1];a = p[1];return;} 对应的汇编代码 10:a=c[1];004010678A4DF1movcl,byteptr[ebp-0Fh] 0040106A884DFCmovbyteptr[ebp-4],cl 11:a=p[1];0040106D8B55ECmovedx,dwordptr[ebp-14h] 004010708A4201moval,byteptr[edx+1] 004010738845FCmovbyteptr[ebp-4],al 第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了。 21、什么是预编译,何时需要预编译? 答:预编译又称为预处理,是做些代码文本的替换工作。处理#开头的指令,比如拷贝#include包含的文件代码,#define宏定义的替换,条件编译等,就是为编译做的预备工作的阶段,主要处理#开始的预编译指令,预编译指令指示了在程序正式编译前就由编译器进行的操作,可以放在程序中的任何位置。c编译系统在对程序进行通常的编译之前,先进行预处理。 c提供的预处理功能主要有以下三种:1)宏定义 2)文件包含 3)条件编译 22、关键字const是什么含意? 答: “const意味着常数”业余者。“只读”正确的答案。const int a;a是一个常整型数 int const a;a是一个常整型数 const int *a;a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。int * const a;a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)int const * a const;a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的) 1).关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。) 2).通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。 3).合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现 23、关键字volatile有什么含意 并给出三个不同的例子。 答:一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子: 1).并行设备的硬件寄存器(如:状态寄存器) 2).一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)3).多线程应用中被几个任务共享的变量 1).一个参数既可以是const还可以是volatile吗?解释为什么。是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。 2).一个指针可以是volatile 吗?解释为什么。是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。3).下面的函数有什么错误: int square(volatile int *ptr){return *ptr * *ptr;} 这段代码的有个恶作剧。这段代码的目的是用来返指针*ptr指向值的平方,但是由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码 int square(volatile int *ptr){int a,b;a = *ptr;b = *ptr;return a * b;} 由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下: long square(volatile int *ptr){ int a;a = *ptr;return a * a;} 24、三种基本的数据模型 答:按照数据结构类型的不同,将数据模型划分为层次模型、网状模型和关系模型。 25、结构与联合有和区别? 答:(1).结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)。 (2)对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的 26、描述内存分配方式以及它们的区别? 答:1)从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。 2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。 3)从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多 27、请说出const与#define 相比,有何优点? 答:Const作用:定义常量、修饰函数参数、修饰函数返回值三个作用。被Const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。 1)const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。 2)有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。 28、简述数组与指针的区别? 答:数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。(1)修改内容上的差别 char a[] = “hello”;a[0] = ‘X’; char *p = “world”;// 注意p 指向常量字符串 p[0] = ‘X’;// 编译器不能发现该错误,运行时错误 (2)用运算符sizeof 可以计算出数组的容量(字节数)。sizeof(p),p 为指针得到的是一个 指针变量的字节数,而不是p 所指的内存容量。C++/C 语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。char a[] = “hello world”;char *p = a;cout<< sizeof(a)<< endl;// 12 字节 cout<< sizeof(p)<< endl;// 4 字节 计算数组和指针的内存容量 void Func(char a[100]){ cout<< sizeof(a)<< endl;// 4 字节而不是100 字节 } 29、分别写出BOOL,int,float,指针类型的变量a 与“零”的比较语句。答:BOOL : if(!a)or if(a)int : if(a == 0)float : const EXPRESSION EXP = 0.000001 if(a < EXP && a >-EXP)pointer : if(a!= NULL)or if(a == NULL) 30、如何判断一段程序是由C 编译程序还是由C++编译程序编译的? 答:#ifdef __cplusplus cout<<“c++”;#else cout<<“c”;#endif 31、论述含参数的宏与函数的优缺点 答: 带参宏 函数 处理时间 编译时 程序运行时 参数类型 没有参数类型问题 定义实参、形参类型 处理过程 不分配内存 分配内存 程序长度 变长 不变 运行速度 不占运行时间 调用和返回占用时间 32、用两个栈实现一个队列的功能?要求给出算法和思路!答:设2个栈为A,B, 一开始均为空.入队:将新元素push入栈A;出队:(1)判断栈B是否为空;(2)如果不为空,则将栈A中所有元素依次pop出并push到栈B;(3)将栈B的栈顶元素pop出; 这样实现的队列入队和出队的平摊复杂度都还是O(1), 比上面的几种方法要好 33、嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢? 答:这个问题用几个解决方案。我首选的方案是: while(1){ } 一些程序员更喜欢如下方案: for(;;){ } 第三个方案是用 goto Loop:...goto Loop;应试者如给出上面的方案,这说明或者他是一个汇编语言程序员(这也许是好事)或者他是一个想进入新领域的BASIC/FORTRAN程序员。 34、位操作(Bit manipulation) 答: 嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a,写两段代码,第一个设置a的bit 3,第二个清除a 的bit 3。在以上两个操作中,要保持其它位不变。对这个问题有三种基本的反应 1)不知道如何下手。该被面者从没做过任何嵌入式系统的工作。 2)用bit fields。Bit fields是被扔到C语言死角的东西,它保证你的代码在不同编译器之间是不可移植的,同时也保证了的你的代码是不可重用的。我最近不幸看到 Infineon为其较复杂的通信芯片写的驱动程序,它用到了bit fields因此完全对我无用,因为我的编译器用其它的方式来实现bit fields的。从道德讲:永远不要让一个非嵌入式的家伙粘实际硬件的边。 3)用 #defines 和 bit masks 操作。这是一个有极高可移植性的方法,是应该被用到的方法。最佳的解决方案如下: #define BIT3(0x1 << 3)static int a;void set_bit3(void){ a |= BIT3;} void clear_bit3(void){ a &= ~BIT3;} 一些人喜欢为设置和清除值而定义一个掩码同时定义一些说明常数,这也是可以接受的。我希望看到几个要点:说明常数、|=和&=~操作。 35、访问固定的内存位置(Accessing fixed memory locations) 答:嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。 这一问题测试你是否知道为了访问一绝对地址把一个整型数强制转换(typecast)为一指针是合法的。这一问题的实现方式随着个人风格不同而不同。典型的类似代码如下: int *ptr;ptr =(int *)0x67a9;*ptr = 0xaa66;A more obscure approach is: 一个较晦涩的方法是: *(int * const)(0x67a9)= 0xaa55;即使你的品味更接近第二种方案,但我建议你在面试时使用第一种方案。 36、中断(Interrupts) 答: 中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展—让标准C支持中断。具代表事实是,产生了一个新的关键字 __interrupt。下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码的。 __interrupt double compute_area(double radius){ double area = PI * radius * radius;printf(“nArea = %f”, area);return area;} 这个函数有太多的错误了,以至让人不知从何说起了: 1)ISR 不能返回一个值。如果你不懂这个,那么你不会被雇用的。 2)ISR 不能传递参数。如果你没有看到这一点,你被雇用的机会等同第一项。 3)在许多的处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额处的寄存器入栈,有些处理器/编译器就是不允许在ISR中做浮点运算。此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明智的。4)与第三点一脉相承,printf()经常有重入和性能上的问题。如果你丢掉了第三和第四点,我不会太为难你的。不用说,如果你能得到后两点,那么你的被雇用前景越来越光明了。 37、动态内存分配(Dynamic memory allocation) 答:尽管不像非嵌入式计算机那么常见,嵌入式系统还是有从堆(heap)中动态分配内存的过程的。那么嵌入式系统中,动态分配内存可能发生的问题是什么? 这里,我期望应试者能提到内存碎片,碎片收集的问题,变量的持行时间等等。这个主题已经在ESP杂志中被广泛地讨论过了(主要是 P.J.Plauger, 他的解释远远超过我这里能提到的任何解释),所有回过头看一下这些杂志吧!让应试者进入一种虚假的安全感觉后,我拿出这么一个小节目: 下面的代码片段的输出是什么,为什么? char *ptr;if((ptr =(char *)malloc(0))== NULL)puts(“Got a null pointer”);else puts(“Got a valid pointer”);//get this 这是一个有趣的问题。最近在我的一个同事不经意把0值传给了函数malloc,得到了一个合法的指针之后,我才想到这个问题。这就是上面的代码,该代码的输出是“Got a valid pointer”。我用这个来开始讨论这样的一问题,看看被面试者是否想到库例程这样做是正确。得到正确的答案固然重要,但解决问题的方法和你做决定的基本原理更重要些。 38、Typedef 答:Typedef 在C语言中频繁用以声明一个已经存在的数据类型的同义字。也可以用预处理器做类似的事。例如,思考一下下面的例子: #define dPS struct s * typedef struct s * tPS;以上两种情况的意图都是要定义dPS 和 tPS 作为一个指向结构s指针。哪种方法更好呢?(如果有的话)为什么? 这是一个非常微妙的问题,任何人答对这个问题(正当的原因)是应当被恭喜的。答案是:typedef更好。思考下面的例子: dPS p1,p2;tPS p3,p4;第一个扩展为 struct s * p1, p2;上面的代码定义p1为一个指向结构的指,p2为一个实际的结构,这也许不是你想要的。第二个例子正确地定义了p3 和p4 两个指针。 39、用变量a给出下面的定义 答:a)一个整型数(An integer) b)一个指向整型数的指针(A pointer to an integer) c)一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a pointer to an integer)d)一个有10个整型数的数组(An array of 10 integers) e)一个有10个指针的数组,该指针是指向一个整型数的(An array of 10 pointers to integers)f)一个指向有10个整型数数组的指针(A pointer to an array of 10 integers) g)一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer) h)一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数(An array of ten pointers to functions that take an integer argument and return an integer)答案是: a)int a;// An integer b)int *a;// A pointer to an integer c)int **a;// A pointer to a pointer to an integer d)int a[10];// An array of 10 integers e)int *a[10];// An array of 10 pointers to integers f)int(*a)[10];// A pointer to an array of 10 integers g)int(*a)(int);// A pointer to a function a that takes an integer argument and returns an integer h)int(*a[10])(int);// An array of 10 pointers to functions that take an integer argument and return an integer 40、解释局部变量、全局变量和静态变量的含义。答: 41、写一个“标准”宏 答:交换两个参数值的宏定义为:.#define SWAP(a,b)(a)=(a)+(b);(b)=(a)-(b);(a)=(a)-(b);输入两个参数,输出较小的一个:#define MIN(A,B)((A)<(B))?(A):(B))表明1年中有多少秒(忽略闰年问题):#define SECONDS_PER_YEAR(60 * 60 * 24 * 365)UL #define DOUBLE(x)x+x 与 #define DOUBLE(x)((x)+(x))i = 5*DOUBLE(5); i为30 i = 5*DOUBLE(5); i为50 已知一个数组table,用一个宏定义,求出数据的元素个数 #define NTBL #define NTBL(sizeof(table)/sizeof(table[0])) 42、A.c 和B.c两个c文件中使用了两个相同名字的static变量,编译的时候会不会有问题?这两个static变量会保存到哪里(栈还是堆或者其他的)? 答:static的全局变量,表明这个变量仅在本模块中有意义,不会影响其他模块。他们都放在数据区,但是编译器对他们的命名是不同的。 如果要使变量在其他模块也有意义的话,需要使用extern关键字。 43、一个单向链表,不知道头节点,一个指针指向其中的一个节点,问如何删除这个指针指向的节点? 答:将这个指针指向的next节点值copy到本节点,将next指向next->next,并随后删除原next指向的节点。将这个节点复制成下一个节点的值,然后删除下一个节点 node *p;// 当前节点 node *q;q = p-> next;p.data = q.data;// 复制q节点到p p-> next = q-> next;// 删除q free(q); 第二部分:程序代码评价或者找错 1、下面的代码输出是什么,为什么? void foo(void){ unsigned int a = 6;int b =-20;(a+b > 6)? puts(“> 6″): puts(“<= 6“);} 这个问题测试你是否懂得C语言中的整数自动转换原则,我发现有些开发者懂得极少这些东西。不管如何,这无符号整型问题的答案是输出是 ”>6″。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。如果你答错了这个问题,你也就到了得不到这份工作的边缘。 2、评价下面的代码片断: unsigned int zero = 0;unsigned int compzero = 0xFFFF;/*1′s complement of zero */ 对于一个int型不是16位的处理器为说,上面的代码是不正确的。应编写如下: unsigned int compzero = ~0;这一问题真正能揭露出应试者是否懂得处理器字长的重要性。在我的经验里,好的嵌入式程序员非常准确地明白硬件的细节和它的局限,然而PC机程序往往把硬件作为一个无法避免的烦恼。 3、C语言同意一些令人震惊的结构,下面的结构是合法的吗,如果是它做些什么? int a = 5, b = 7, c;c = a+++b;这个问题将做为这个测验的一个愉快的结尾。不管你相不相信,上面的例子是完全合乎语法的。问题是编译器如何处理它?水平不高的编译作者实际上会争论这个问题,根据最处理原则,编译器应当能处理尽可能所有合法的用法。因此,上面的代码被处理成: c = a++ + b;因此, 这段代码持行后a = 6, b = 7, c = 12。 如果你知道答案,或猜出正确答案,做得好。如果你不知道答案,我也不把这个当作问题。我发现这个问题的最大好处是这是一个关于代码编写风格,代码的可读性,代码的可修改性的好的话题。 4、设有以下说明和定义: typedef union {long i;int k[5];char c;} DATE;struct data { int cat;DATE cow;double dog;} too;DATE max;则语句 printf(“%d”,sizeof(struct date)+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 5、请写出下列代码的输出内容 #include main(){ int a,b,c,d;a=10;b=a++;c=++a;d=10*a++;printf(“b,c,d:%d,%d,%d”,b,c,d);return 0;} 答:10,12,120 6、写出下列代码的输出内容 #include int inc(int a){ return(++a);} int multi(int*a,int*b,int*c){ return(*c=*a**b);} typedef int(FUNC1)(int in);typedef int(FUNC2)(int*,int*,int*);void show(FUNC2 fun,int arg1, int*arg2){ INCp=&inc;//代码不完整 int temp =p(arg1);fun(&temp,&arg1, arg2);printf(“%dn”,*arg2);} main(){ int a;show(multi,10,&a);return 0;} 答:110 7、请找出下面代码中的所有错误 说明:以下代码是把一个字符串倒序,如“abcd”倒序后变为“dcba” 1、#include”string.h” 2、main() 3、{ 4、char*src=”hello,world”; 5、char* dest=NULL; 6、int len=strlen(src); 7、dest=(char*)malloc(len); 8、char* d=dest; 9、char* s=src[len]; 10、while(len–!=0) 11、d++=s–; 12、printf(“%s”,dest); 13、return 0; 14、} 答: 方法1: int main(){ char* src = “hello,world”;int len = strlen(src);char* dest =(char*)malloc(len+1);//要为 分配一个空间 char* d = dest;char* s = &src[len-1];//指向最后一个字符 while(len–!= 0)*d++=*s–;*d = 0;//尾部要加 printf(“%sn”,dest);free(dest);// 使用完,应当释放空间,以免造成内存汇泄露 return 0;} 方法2: #include #include main(){ char str[]=”hello,world”;int len=strlen(str);char t;for(int i=0;i { t=str[i];str[i]=str[len-i-1];str[len-i-1]=t;} printf(“%s”,str);return 0;} 8、请问下面程序有什么错误? int a[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;答案:把循环语句内外换一下 9、请问下面程序会出现什么情况?.#define Max_CB 500 void LmiQueryCSmd(Struct MSgCB * pmsg){ unsigned char ucCmdNum;......for(ucCmdNum=0;ucCmdNum 10、以下3个有什么区别 char * const p;//常量指针,p的值不可以修改 char const * p;//指向常量的指针,指向的常量值不可以改 const char *p; //和char const *p 11、写出下面的结果 char str1[] = “abc”;char str2[] = “abc”;const char str3[] = “abc”;const char str4[] = “abc”;const char *str5 = “abc”;const char *str6 = “abc”;char *str7 = “abc”;char *str8 = “abc”;cout <<(str1 == str2)<< endl;cout <<(str3 == str4)<< endl;cout <<(str5 == str6)<< endl;cout <<(str7 == str8)<< endl;结果是:0 0 1 1 解答:str1,str2,str3,str4是数组变量,它们有各自的内存空间; 而str5,str6,str7,str8是指针,它们指向相同的常量区域。 12、以下代码中的两个sizeof用法有问题吗? void UpperCase(char str[])// 将 str 中的小写字母转换成大写字母//这里这个str 是指针 { for(size_t i=0;i 度: ” << sizeof(str)/sizeof(str[0])<< endl;UpperCase(str);' cout << str << endl;答:函数内的sizeof有问题。根据语法,sizeof如用于数组,只能测出静态数组的大小,无法检测动态分配的或外部数组大小。函数外的str是一个静态定义的数组,因此其大小为6,函数内的str实际只是一个指向字符串的指针,没有任何额外的与数组相关的信息,因此sizeof作用于上只将其当指针看,一个指针为4个字节,因此返回4。 13、写出输出结果 main(){ int a[5]={1,2,3,4,5};int *ptr=(int *)(&a+1); printf(“%d,%d”,*(a+1),*(ptr-1));} 输出:2,5 *(a+1)就是a[1],*(ptr-1)就是a[4],执行结果是2,5 &a+1不是首地址+1,系统会认为加一个a数组的偏移,是偏移了一个数组的大小(本例是5个int) int *ptr=(int *)(&a+1);则ptr实际是&(a[5]),也就是a+5 原因如下:&a是数组指针,其类型为 int(*)[5];而指针加1要根据指针类型加上一定的值,不同类型的指针+1之后增加的大小不同,a是长度为5的int数组指针,所以要加 5*sizeof(int),所以ptr实际是a[5],但是prt与(&a+1)类型是不一样的(这点很重要),所以prt-1只会减去sizeof(int*)。a,&a的地址是一样的,但意思不一样,a是数组首地址,也就是a[0]的地址,&a是对象(数组)首地址,a+1是数组下一元素的地址,即a[1],&a+1是下一个对象的地址,即a[5].14、请问以下代码有什么问题: int main(){ char a;char *str=&a;strcpy(str,“hello”);printf(str);return 0;} 没有为str分配内存空间,将会发生异常 问题出在将一个字符串复制进一个字符变量指针所指地址。虽然可以正确输出结果,但因为越界进行内在读写而导致程序崩溃。char* s=“AAA”;printf(“%s”,s);s[0]='B';printf(“%s”,s);有什么错? “AAA”是字符串常量。s是指针,指向这个字符串常量,所以声明s的时候就有问题。cosnt char* s=“AAA”;然后又因为是常量,所以对是s[0]的赋值操作是不合法的。 15、有以下表达式: int a=248;b=4;int const c=21;const int *d=&a;int *const e=&b;int const *f const =&a;请问下列表达式哪些会被编译器禁止?为什么? *c=32;d=&b;*d=43;e=34;e=&a;f=0x321f; *c 这是个什么东东,禁止 *d 说了是const,禁止 e = &a 说了是const 禁止 const *f const =&a;禁止 16、交换两个变量的值,不使用第三个变量。即a=3,b=5,交换之后a=5,b=3;有两种解法, 一种用算术算法, 一种用^(异或)a = a + b;b = ab;or a = a^b;b = a^b;a = a^b;// 只能对int,char..or a ^= b ^= a; 17、下面的程序会出现什么结果.#include #include void getmemory(char *p){p=(char *)malloc(100);strcpy(p,”hello world”);} int main() {char *str=NULL;getmemory(str); printf(“%s/n”,str);free(str);return 0;} 程序崩溃,getmemory中的malloc 不能返回动态内存,free()对str操作很危险 18、下面的语句会出现什么结果? char szstr[10]; strcpy(szstr,”0123456789″); 答案:长度不一样,会造成非法的OS,应该改为char szstr[11]; 19、(void *)ptr 和(*(void**))ptr的结果是否相同? 答:其中ptr为同一个指针,(void *)ptr 和(*(void**))ptr值是相同的20、问函数既然不会被其它函数调用,为什么要返回1? int main(){ int x=3; printf(“%d”,x);return 1;} 答:mian中,c标准认为0表示成功,非0表示错误。具体的值是某中具体出错信息 21、对绝对地址0×100000赋值且想让程序跳转到绝对地址是0×100000去执行 (unsigned int*)0×100000 = 1234; 首先要将0×100000强制转换成函数指针,即:(void(*)())0×100000 然后再调用它: *((void(*)())0×100000)();用typedef可以看得更直观些: typedef void(*)()voidFuncPtr;*((voidFuncPtr)0×100000)(); 22、输出多少?并分析过程 unsigned short A = 10;printf(“~A = %un”, ~A);char c=128; printf(“c=%dn”,c); 第一题,~A =0xfffffff5,int值 为-11,但输出的是uint。所以输出4294967285 第二题,c=0×10,输出的是int,最高位为1,是负数,所以它的值就是0×00的补码就是128,所以输出-128。这两道题都是在考察二进制向int或uint转换时的最高位处理。 23、分析下面的程序: void GetMemory(char **p,int num){ *p=(char *)malloc(num);} int main(){ char *str=NULL;GetMemory(&str,100);strcpy(str,”hello”);free(str);if(str!=NULL){ strcpy(str,”world”);} printf(“n str is %s”,str);getchar();} 问输出结果是什么?希望大家能说说原因,先谢谢了 输出str is world。 free 只是释放的str指向的内存空间,它本身的值还是存在的.所以free之后,有一个好的习惯就是将str=NULL.此时str指向空间的内存已被回收,如果输出语句之前还存在分配空间的操作的话,这段存储空间是可能被重新分配给其他变量的, 尽管这段程序确实是存在大大的问题(上面各位已经说得很清楚了),但是通常会打印出world来。 这是因为,进程中的内存管理一般不是由操作系统完成的,而是由库函数自己完成的。 当你malloc一块内存的时候,管理库向操作系统申请一块空间(可能会比你申请的大一些),然后在这块空间中记录一些管理信息(一般是在你申请的内存前面一点),并将可用内存的地址返回。但是释放内存的时候,管理库通常都不会将内存还给操作系统,因此你是可以继续访问这块地址的,只不过。。。。楼上都说过了,最好别这么干。 sizeof()和初不初始化,没有关系; strlen()和初始化有关。 char(*str)[20];/*str是一个数组指针,即指向数组的指针.*/ char *str[20];/*str是一个指针数组,其元素为指针型数据.*/ 25、long a=0×801010;a+5=? 答:0×801010用二进制表示为:“1000 0000 0001 0000 0001 0000”,十进制的值为8392720,再加上5就是 8392725 27、下面的函数实现在一个数上加一个数,有什么错误?请改正。 int add_n(int n){ static int i = 100;i += n;return i;} 当你第二次调用时得不到正确的结果,难道你写个函数就是为了调用一次?问题就出在 static上 28、给出下面程序的答案 typedef struct AA { int b1:5;int b2:2;}AA; void main(){ AA aa; char cc[100]; strcpy(cc,”0123456789abcdefghijklmnopqrstuvwxyz”); memcpy(&aa,cc,sizeof(AA));cout << aa.b1 < 答案是-16和1 首先sizeof(AA)的大小为4,b1和b2分别占5bit和2bit.经过strcpy和memcpy后,aa的4个字节所存放的值是: 0,1,2,3的ASC码,即 00110000,00110001,00110010,00110011 所以,最后一步:显示的是这4个字节的前5位,和之后的2位 分别为:10000,和01 因为int是有正负之分 所以:答案是-16和1 29、求函数返回值,输入x=9999;int func(x){ int countx = 0;while(x){ countx ++;x = x&(x-1);} return countx;} 结果?这是统计9999的二进制数值中有多少个1的函数,且有9999=9×1024+512+256+159×1024中含1的个数为2;512中含有1的个数为1;256中含1的个数为1;15中含1的个数为4;故共有1的个数为8,结果为8。 1000k);return 0;} #include void main(){ unsigned long int a,i=1;scanf(“%d”,&a);if(a%2==0){ for(i=1;i printf(“%d”,a,a-i);} else for(i=1;i<=a/2;i++) printf(“ %d, %d”,i,a-i);} 3、递规反向输出字符串的例子,可谓是反序的经典例程.void inverse(char *p){ if(*p = = ' ')return; inverse(p+1);printf(“%c”, *p);} int main(int argc, char *argv[]){ inverse(“abc ”);return 0;} 对1的另一种做法: #include void test(FILE *fread, FILE *fwrite){ char buf[1024] = {0}; if(!fgets(buf, sizeof(buf), fread))return; test(fread, fwrite);fputs(buf, fwrite);} int main(int argc, char *argv[]){ FILE *fr = NULL;FILE *fw = NULL; fr = fopen(“data”, “rb”);fw = fopen(“dataout”, “wb”);test(fr, fw);fclose(fr);fclose(fw); return 0;} 4、写一段程序,找出数组中第k大小的数,输出数所在的位置。例如{2,4,3,4,7}中,第一大的数是7,位置在4。第二大、第三大的数都是4,位置在1、3随便输出哪一个均可。函数接口为:int find_orderk(const int* narry,const int n,const int k)要求算法复杂度不能是O(n^2)谢谢! 可以先用快速排序进行排序,其中用另外一个进行地址查找 //快速排序 /* 快速排序,*/ #include using namespace std; void Qsort(int a[], int low, int high){ if(low >= high){ return;} int first = low;int last = high;int key = a[first];/*用字表的第一个记录作为枢轴*/ while(first < last){ while(first < last && a[last] >= key) { --last;} a[first] = a[last];/*将比第一个小的移到低端*/ while(first < last && a[first] <= key) { ++first;} a[last] = a[first]; /*将比第一个大的移到高端*/ } a[first] = key;/*枢轴记录到位*/ Qsort(a, low, first1] = t;//记录第几个位置用 sum++; cin >> t; } sum-= 1; Qsort(narry, 1, sum); for(int i = 1;i <= sum;i++) cout << narry[i] << 't';cout << endl;int k; cout << “Please input place you want:” << endl;cin >> k;int aa = 1;int kk = sum;for(;;){ if(aa == k) break; if(narry[kk]!= narry[kk1, k); result.pop_back(); } } else { copy(result.begin(), result.end(), ostream_iterator cout << endl;} } int main(int argc, char *argv[]){ int n, k;scanf(“%d%d”, &n, &k);solve(n, k);return 0;} 12、用指针的方法,将字符串“ABCD1234efgh”前后对调显示 /* 用指针的方法,将字符串“ABCD1234efgh”前后对调显示 */ #include using namespace std; int main(){ char str[] = “ABCD1234efgh”;int length = strlen(str); char *p1, *p2; p1 = str; p2 = str + length*str2; } int main(){ char *a = “333”;char*b = “55555”;cout < 16、实现子串定位int FindSubStr(const char *MainStr, const char *SubStr) /* 实现子串定位int FindSubStr(const char *MainStr, const char *SubStr)*/ #include using namespace std; int FindSubStr(const char* MainStr, const char* SubStr){ assert(MainStr!= NULL&&SubStr!= NULL);//检查 const char *p;const char *q;const char *s= MainStr;while(*MainStr){ p = MainStr; q = SubStr; while(*p && *q && *p++ == *q++);//对子串进行全部的检查 if(!*q) { return MainStr1]; a[a[i]1;i>0;i--)//否则,开始循环计算 { for(j = 0;j <= strlen(shortstring)1] = 0;printf(“第%4d个出局的人是:%4d号n”, i, j);} free(arr);return j;} int main(){ int n, m; scanf(“%d%d”, &n, &m); printf(“最后胜利的是%d号!n”, Josephu(n, m));system(“pause”);return 0;} 链表实现: #include #include typedef struct Node { int index; struct Node *next;}JosephuNode; int Josephu(int n, int m){ int i, j; JosephuNode *head, *tail;head = tail =(JosephuNode *)malloc(sizeof(JosephuNode));for(i = 1;i < n;++i){ tail->index = i; tail->next =(JosephuNode *)malloc(sizeof(JosephuNode));tail = tail->next;} tail->index = i;tail->next = head; for(i = 1;tail!= head;++i){ for(j = 1;j < m;++j){ tail = head; head = head->next;} tail->next = head->next; printf(“第%4d个出局的人是:%4d号n”, i, head->index);free(head); head = tail->next;} i = head->index;free(head);return i;} int main(){ int n, m;scanf(“%d%d”, &n, &m); printf(“最后胜利的是%d号!n”, Josephu(n, m));system(“pause”);return 0;} 第四部分:附加部分 1、位域 : 有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态,用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。 一、位域的定义和位域变量的说明位域定义与结构定义相仿,其形式为: struct 位域结构名 { 位域列表 };其中位域列表的形式为: 类型说明符 位域名:位域长度 例如: struct bs { int a:8;int b:2;int c:6;};位域变量的说明与结构变量说明的方式相同。可采用先定义后说明,同时定义说明或者直接说明这三种方式。例如: struct bs { int a:8;int b:2;int c:6;}data;说明data为bs变量,共占两个字节。其中位域a占8 位,位域b占2位,位域c占6位。对于位域的定义尚有以下几点说明: 1.一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如: struct bs { unsigned a:4 unsigned :0 /*空域*/ unsigned b:4 /*从下一单元开始存放*/ unsigned c:4 } 在这个位域定义中,a占第一字节的4位,后4位填0表示不使用,b从第二字节开始,占用4位,c占用4位。 2.由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过8位二进位。3.位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如: struct k { int a:1 int :2 /*该2位不能使用*/ int b:3 int c:2 }; 从以上分析可以看出,位域在本质上就是一种结构类型,不过其成员是按二进位分配的。 二、位域的使用位域的使用和结构成员的使用相同,其一般形式为: 位域变量名•位域名 位域允许用各种格式输出。main(){ struct bs { unsigned a:1;unsigned b:3;unsigned c:4;} bit,*pbit;bit.a=1;bit.b=7;bit.c=15; printf(“%d,%d,%dn”,bit.a,bit.b,bit.c);pbit=&bit; pbit->a=0;//此后三行是什么意思 pbit->b&=3;pbit->c|=1; printf(“%d,%d,%dn”,pbit->a,pbit->b,pbit->c);} 上例程序中定义了位域结构bs,三个位域为a,b,c。说明了bs类型的变量bit和指向bs类型的指针变量pbit。这表示位域也是可以使用指针的。 程序的9、10、11三行分别给三个位域赋值。(应注意赋值不能超过该位域的允许范围)程序第12行以整型量格式输出三个域的内容。第13行把位域变量bit的地址送给指针变量pbit。第14行用指针方式给位域a重新赋值,赋为0。第15行使用了复合的位运算符“&=”,该行相当于: pbit-b=pbit-b3位域b中原有值为7,与3作按位与运算的结果为3(111011=011,十进制值为3)。同样,程序第16行中使用了复合位运算“|=”,相当于: pbit-c=pbit-c|1其结果为15。程序第17行用指针方式输出了这三个域的值。 改错: #include int main(void){ int **p;int arr[100];p = &arr;return 0;} 解答:搞错了,是指针类型不同, int **p;//二级指针 &arr;//得到的是指向第一维为100的数组的指针 #include int main(void){ int **p, *q;int arr[100];q = arr;p = &q;return 0;} 一、请填写BOOL , float, 指针变量 与“零值”比较的 if 语句。(10分) 请写出 BOOL flag 与“零值”比较的 if 语句。(3分) 标准答案: if(flag)if(!flag)如下写法均属不良风格,不得分。if(flag == TRUE)if(flag == 1)if(flag == FALSE)if(flag == 0)请写出 float x 与“零值”比较的 if 语句。(4分)标准答案示例: const float EPSINON = 0.00001; if((x >= – EPSINON)&&(x <= EPSINON) 不可将浮点变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”此类形式。如下是错误的写法,不得分。if(x == 0.0)if(x!= 0.0) 请写出 char *p 与“零值”比较的 if 语句。(3分)标准答案: if(p == NULL) if(p!= NULL)如下写法均属不良风格,不得分。if(p == 0)if(p!= 0)if(p)if(!) 二、以下为Windows NT下的32位C++程序,请计算sizeof的值(10分) char str[] = “Hello”;char *p = str;int n = 10;请计算 sizeof(str)= 6(2分)sizeof(p)= 4(2分)sizeof(n)= 4(2分) void Func(char str[100]){请计算 sizeof(str)= 4(2分)} void *p = malloc(100);请计算 sizeof(p)= 4(2分) 三、简答题(25分) 1、头文件中的 ifndef/define/endif 干什么用?(5分) 答:防止该头文件被重复引用。 2、#include 和 #include “filename.h” 有什么区别?(5分) 答:对于#include,编译器从标准库路径开始搜索 filename.h 对于#include “filename.h”,编译器从用户的工作路径开始搜索 filename.h 3、const 有什么用途?(请至少说明两种)(5分)答:(1)可以定义 const 常量 (2)const可以修饰函数的参数、返回值,甚至函数的定义体。被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。 4、在C++ 程序中调用被 C编译器编译后的函数,为什么要加 extern “C”?(5分) 答:C++语言支持函数重载,C语言不支持函数重载。函数被C++编译后在库中的名字与C语言的不同。假设某个函数的原型为: void foo(int x, int y);该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。 C++提供了C连接交换指定符号extern“C”来解决名字匹配问题。 5、请简述以下两个for循环的优缺点(5分)第一个 for(i=0;i 第二个 if(condition){ for(i=0;i 缺点:多执行了N-1次逻辑判断,并且打断了循环“流水线”作业,使得编译器不能对循环进行优化处理,降低了效率。 优点:循环的效率高 缺点:程序不简洁 四、有关内存的思考题(每小题5分,共20分) void GetMemory(char *p){p =(char *)malloc(100);} void Test(void){char *str = NULL;GetMemory(str);strcpy(str, “hello world”);printf(str);} 请问运行Test函数会有什么样的结果?答:程序崩溃。因为GetMemory并不能传递动态内存,Test函数中的 str一直都是 NULL。 strcpy(str, “hello world”);将使程序崩溃。 char *GetMemory(void){ char p[] = “hello world”;return p;} void Test(void){ char *str = NULL;str = GetMemory();printf(str);} 请问运行Test函数会有什么样的结果?答:可能是乱码。 因为GetMemory返回的是指向“栈内存”的指针,该指针的地址不是 NULL,但其原现的内容已经被清除,新内容不可知。 void GetMemory2(char **p, int num){ *p =(char *)malloc(num);} void Test(void){ char *str = NULL;GetMemory(&str, 100);strcpy(str, “hello”);printf(str);} 请问运行Test函数会有什么样的结果? 答:(1)能够输出hello(2)内存泄漏 void Test(void){ char *str =(char *)malloc(100);strcpy(str, “hello”);free(str); if(str!= NULL){ strcpy(str, “world”);printf(str);} } 请问运行Test函数会有什么样的结果?答:篡改动态内存区的内容,后果难以预料,非常危险。因为free(str);之后,str成为野指针,if(str!= NULL)语句不起作用。 五、编写strcpy函数(10分)已知strcpy函数的原型是char *strcpy(char *strDest, const char *strSrc); 其中strDest是目的字符串,strSrc是源字符串。(1)不调用C++/C的字符串库函数,请编写函数 strcpy char *strcpy(char *strDest, const char *strSrc);{assert((strDest!=NULL)&&(strSrc!=NULL));// 2分 char *address = strDest;// 2分 while((*strDest++ = * strSrc++)!= ‘ ’)// 2分 NULL;return address;// 2分 } 1.strcpy的实现代码 char * strcpy(char * strDest,const char * strSrc){if((strDest==NULL)||(strSrc==NULL))file://[/1] throw “Invalid argument(s)”;//[2] char * strDestCopy=strDest;file://[/3] while((*strDest++=*strSrc++)!=’ ′);file://[/4] return strDestCopy;}(2)strcpy能把strSrc的内容复制到strDest,为什么还要char * 类型的返回值? 答:为了实现链式表达式。// 2分 例如 int length = strlen(strcpy(strDest, “hello world”));错误的做法: [1](A)不检查指针的有效性,说明答题者不注重代码的健壮性。 (B)检查指针的有效性时使用((!strDest)||(!strSrc))或(!(strDest&&strSrc)),说明答题者对C语言中类型的隐式转换没有深刻认识。在本例中char *转换为bool即是类型隐式转换,这种功能虽然灵活,但更多的是导致出错概率增大和维护成本升高。所以C++专门增加了bool、true、false三个关键字以提供更安全的条件表达式。 (C)检查指针的有效性时使用((strDest==0)||(strSrc==0)),说明答题者不知道使用常量的好处。直接使用字面常量(如本例中的0)会减少程序的可维护性。0虽然简单,但程序中可能出现很多处对指针的检查,万一出现笔误,编译器不能发现,生成的程序内含逻辑错误,很难排除。而使用NULL代替0,如果出现拼写错误,编译器就会检查出来。[2](A)return new string(“Invalid argument(s)”);,说明答题者根本不知道返回值的用途,并且他对内存泄漏也没有警惕心。从函数中返回函数体内分配的内存是十分危险的做法,他把释放内存的义务抛给不知情的调用者,绝大多数情况下,调用者不会释放内存,这导致内存泄漏。 (B)return 0;,说明答题者没有掌握异常机制。调用者有可能忘记检查返回值,调用者还可能无法检查返回值(见后面的链式表达式)。妄想让返回值肩负返回正确值和异常值的双重功能,其结果往往是两种功能都失效。应该以抛出异常来代替返回值,这样可以减轻调用者的负担、使错误不会被忽略、增强程序的可维护性。[3] (A)忘记保存原始的strDest值,说明答题者逻辑思维不严密。[4] (A)循环写成while(*strDest++=*strSrc++);,同[1](B)。 (B)循环写成while(*strSrc!=’ ′) *strDest++=*strSrc++;,说明答题者对边界条件的检查不力。循环体结束后,strDest字符串的末尾没有正确地加上’ ′。 六、编写类String的构造函数、析构函数和赋值函数(25分) 已知类String的原型为: class String {public: String(const char *str = NULL);// 普通构造函数 String(const String &other);// 拷贝构造函数 ~ String(void);// 析构函数 String & operate =(const String &other);// 赋值函数 private: char *m_data;// 用于保存字符串 }; 请编写String的上述4个函数。标准答案: // String的析构函数 String::~String(void)// 3分 {delete [] m_data; // 由于m_data是内部数据类型,也可以写成 delete m_data;} // String的普通构造函数 String::String(const char *str)// 6分 {if(str==NULL) {m_data = new char[1];// 若能加 NULL 判断则更好 *m_data = ‘ ’;} else {int length = strlen(str); m_data = new char[length+1];// 若能加 NULL 判断则更好 strcpy(m_data, str);} } // 拷贝构造函数 String::String(const String &other)// 3分 {int length = strlen(other.m_data);m_data = new char[length+1];// 若能加 NULL 判断则更好 strcpy(m_data, other.m_data);} // 赋值函数 String & String::operate =(const String &other)// 13分 {//(1)检查自赋值 // 4分 if(this == &other)return *this;//(2)}第四篇:c语言笔试题总结
第五篇:C语言软件工程师笔试题精华