C6

 

1. 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)

#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL

我在这想看到几件事情:

1). #define 语法的基本知识(例如:不能以分号结束,括号的使用,等等)

2). 懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多少秒而不是计算出实际的值,是更清晰而没有代价的。

3). 意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。

4). 如果你在你的表达式中用到UL(表示无符号长整型),那么你有了一个好的起点。记住,第一印象很重要。

2. 写一个标准MIN,这个宏输入两个参数并返回较小的一个。

#define MIN(A,B) ((A) <= (B) (A) : ))

这个测试是为下面的目的而设的:

1). 标识#define在宏中应用的基本知识。这是很重要的,因为直到嵌入(inline)操作符变为标准C的一部分,宏是方便产生嵌入代码的唯一方法,对于嵌入式系统来说,为了能达到要求的性能,嵌入代码经常是必须的方法。

2). 三重条件操作符的知识。这个操作符存在C语言中的原因是它使得编译器能产生比if-then-else更优化的代码,了解这个用法是很重要的。

3). 懂得在宏中小心地把参数用括号括起来

4). 我也用这个问题开始讨论宏的副作用,例如:当你写下面的代码时会发生什么事?

least = MIN(*p++, b);

3. 预处理器标识#error的目的是什么?

如果你不知道答案,请看参考文献1。这问题对区分一个正常的伙计和一个书呆子是很有用的。只有书呆子才会读C语言课本的附录去找出象这种

问题的答案。当然如果你不是在找一个书呆子,那么应试者最好希望自己不要知道答案。

死循环(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 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

人们经常声称这里有几个问题是那种要翻一下书才能回答的问题,我同意这种说法。当我写这篇文章时,为了确定语法的正确性,我的确查了一下书。

但是当我被面试的时候,我期望被问到这个问题(或者相近的问题)。因为在被面试的这段时间里,我确定我知道这个问题的答案。应试者如果不知道

所有的答案(或至少大部分答案),那么也就没有为这次面试做准备,如果该面试者没有为这次面试做准备,那么他又能为什么出准备呢?

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的值可能被意想不到地该变,因此ab可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:

long square(volatile int *ptr)

{ int a;

a = *ptr;

return a * a;

}

位操作(Bit manipulation

9. 嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a,写两段代码,第一个设置abit 3,第二个清除a bit 3。在以上两个操作中,要保持其它位不变。

对这个问题有三种基本的反应

1). 不知道如何下手。该被面者从没做过任何嵌入式系统的工作。

2). bit fieldsBit 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;

} 一些人喜欢为设置和清除值而定义一个掩码同时定义一些说明常数,这也是可以接受的。我希望看到几个要点:说明常数、|=&=~操作。

10. 嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。

这一问题测试你是否知道为了访问一绝对地址把一个整型数强制转换(typecast)为一指针是合法的。这一问题的实现方式随着个人风格不同而不同。典型的类似代码如下:

int *ptr;

ptr = (int *)0x67a9;

*ptr = 0xaa55;

一个较晦涩的方法是:

*(int * const)(0x67a9) = 0xaa55;

即使你的品味更接近第二种方案,但我建议你在面试时使用第一种方案。

中断(Interrupts

11. 中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展让标准C支持中断。具代表事实是,产生了一个新的关键字 __interrupt。下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码的。

__interrupt double compute_area (double radius)

{ double area = PI * radius * radius;

printf(" Area = %f", area);

return area;

}

这个函数有太多的错误了,以至让人不知从何说起了:

1). ISR 不能返回一个值。如果你不懂这个,那么你不会被雇用的。

2). ISR 不能传递参数。如果你没有看到这一点,你被雇用的机会等同第一项。

3). 在许多的处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额处的寄存器入栈,有些处理器/编译器就是不允许在ISR中做浮点运算。此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明智的。

4). 与第三点一脉相承,printf()经常有重入和性能上的问题。如果你丢掉了第三和第四点,我不会太为难你的。不用说,如果你能得到后两点,那么你的被雇用前景越来越光明了。

代码例子(Code examples

12 . 下面的代码输出是什么,为什么?

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

15. 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 两个指针。

晦涩的语法

16. C语言同意一些令人震惊的结构,下面的结构是合法的吗,如果是它做些什么?

int a = 5, b = 7, c;

c = a+++b;

这个问题将做为这个测验的一个愉快的结尾。不管你相不相信,上面的例子是完全合乎语法的。问题是编译器如何处理它?水平不高的编译作者实际上会争论这个问题,根据最处理原则,编译器应当能处理尽可能所有合法的用法。因此,上面的代码被处理成:

c = a++ + b;

因此, 这段代码持行后a = 6, b = 7, c = 12

如果你知道答案,或猜出正确答案,做得好。如果你不知道答案,我也不把这个当作问题。我发现这个问题的最大好处是:这是一个关于代码编写风格,代码的可读性,代码的可修改性的好的话题

What will print out?

main()

{ char *p1=“name”;

char *p2;

p2=(char*)malloc(20);

memset (p2, 0, 20);

while(*p2++ = *p1++);

printf(“%sn”,p2);

}

Answer:empty string.

What will be printed as the result of the operation below:

main()

{ int x="20",y=35;

x=y++ + x++;

y= ++y + ++x;

printf(“%d%dn”,x,y);

}

Answer : 5794

What will be printed as the result of the operation below:

main()

{ int x="5";

printf(“%d,%d,%dn”,x,x< <2,x>>2);

}

Answer: 5,20,1

What will be printed as the result of the operation below:

#define swap(a,b) a="a"+b;b=a-b;a=a-b;

void main()

{ int x="5", y="10";

swap (x,y);

printf(“%d %dn”,x,y);

swap2(x,y);

printf(“%d %dn”,x,y);

}

int swap2(int a, int b)

{ int temp;

temp=a;

b=a;

a=temp;

return 0;

}

Answer: 10, 5

10, 5

What will be printed as the result of the operation below:

main()

{ char *ptr = ” Cisco Systems”;

*ptr++; printf(“%sn”,ptr);

ptr++;

printf(“%sn”,ptr);

}

Answer:Cisco Systems

isco systems

What will be printed as the result of the operation below:

main()

{ char s1[]=“Cisco”;

char s2[]= “systems”;

printf(“%s”,s1);

} Answer: Cisco

What will be printed as the result of the operation below:

main()

{ char *p1;

char *p2;

p1=(char *)malloc(25);

p2=(char *)malloc(25);

strcpy(p1,”Cisco”);

strcpy(p2,“systems”);

strcat(p1,p2);

printf(“%s”,p1);

}

Answer: Ciscosystems

The following variable is available in file1.c, who can access it?:

static int average;

Answer: all the functions in the file1.c can access the variable.

WHat will be the result of the following code?

#define TRUE 0 // some code

while(TRUE)

{

// some code

}

Answer: This will not go into the loop as TRUE is defined as 0.

What will be printed as the result of the operation below:

int x;

int modifyvalue()

{ return(x+=10);

} int changevalue(int x)

{ return(x+=1);

}

void main()

{ int x="10";

x++;

changevalue(x);

x++;

modifyvalue();

printf("First output:%dn",x);

x++;

changevalue(x);

printf("Second output:%dn",x);

modifyvalue();

printf("Third output:%dn",x);

}

Answer: 12 , 13 , 13

What will be printed as the result of the operation below:

main()

{ int x="10", y="15";

x = x++;

y = ++y;

printf(“%d %dn”,x,y);

}

Answer: 11, 16

What will be printed as the result of the operation below:

main()

{ int a="0";

if(a==0)

printf(“Cisco Systemsn”);

printf(“Cisco Systemsn”);

}

Answer: Two lines with “Cisco Systems” will be printed

 

1. . release版本的可执行程序为什么非常大?

程序一般分为Debug版本和Release版本,Debug版本用于内部调试,Release版本发行给用户使用

ReleaseDebug有什么不同

Release版称为发行版,Debug版称为调试版。

Debug中可以单步执行、跟踪等功能,但生成的可执行文件比较大,代码运行速度较慢。Release版运行速度较快,可执行文件较小,但在其编译条件下无法执行调试功能。

Release的exe文件链接的是标准的MFC DLL(Use MFC in a shared or static dll)。这些DLL在安装Windows的时候,已经配置,所以这些程序能够在没有安装Visual C++ 6.0的机器上运行。而Debug版本的exe链接了调试版本的MFC DLL文件,在没有安装Visual C++6.0的机器上不能运行,因为缺相应的DLL,除非选择use static dll when link。

sizeof(d)? 为什么在不同的平台上得到的值不一样?

C++拷贝构造函数和赋值运算符有那些不同和相同点。

拷贝构造函数和赋值号的异同

同:都可以对对象的成员进行赋值

异:拷贝构造函数首先是一个构造函数,它调用的时候产生一个对象,是通过参数传进来的那个对象来初始化,产生的对象。赋值是把一个对象赋值给一个原有的对象,而且还要检查一下两个对象是不是同一个对象,如果是的话就不做任何操作。

软件开发过程包含哪些阶段?各阶段质量保证措施是什么?

1.需求分析 :需求分析是开发人员对系统需要做什么和如何做的定义过程。

b、系统设计 :优良的体系结构应当具备可扩展性和可配置性,而好的体系结构则需要好的设计方法,自然设计选型成为了系统设计首要的工作,究竟是采用哪种设计方法好呢?

c、实现    d、文档管理 :文档维护主要是配置管理小组的工作。   3、系统维护质量保证

2. 使用C++赋值运算符应注意什么地方?

如果函数的返回值是一个对象,有些场合用引用传递替换值传递可以提高效率。而有些场合只能用值传递而不能用引用传递,否则会出错。

对于赋值函数,应当用引用传递的方式返回String对象。如果用值传递的方式,虽然功能仍然正确,但由于return语句要把 *this拷贝到保存返回值的外部存储单元之中,增加了不必要的开销,降低了赋值函数的效率。

对于相加函数,应当用值传递的方式返回String对象。如果改用引用传递,那么函数返回值是一个指向局部对象temp引用。由于temp在函数结束时被自动销毁,将导致返回的引用无效

3.exit()_exit() 的区别。

     exit()’‘_exit()’的基本区别在于前一个调用实施与调用库里用户状态结构(user-mode constructs)有关的清除工作(clean-up)而且调用用户自定义的清除程序在退出程序前关闭文件清除缓存。后一个函数只为进程实施内核清除工作。不关闭文件,不清楚缓存。

832位系统中,出现结构字节对齐的问题和大小端的问题的避免?

指定对齐值#pragma pack (value)时指定的对齐value

9.写一个函数判断系统是大端还是小端。若处理器是Big_endian的,则返回0;若是Little_endian的,则返回1.

大端格式:在这种格式中,字数据的高字节存储在低地址中,而字数据的低字节则存放在高地址中

小端格式:与大端存储格式相反,在小端存储格式中,低地址中存放的是字数据的低字节,高地址存放的是字数据的高字节

联合体union的存放顺序是所有成员都从低地址开始存放。

Int checkCPU ()

{

Union w

{

Int a;

Char b;

}c;

c.a=1;

return (c.b==1);

}

14 如何在Release版本中查找以下问题

a 内存泄漏 b 段错误导致非法操作  c 程序CPU占用100%
9
、如何查出内存泄漏和非法操作的BUG(在Release版本下)?

检查window release下的内存泄漏

1、 放置关键字 assert()

2、 生成map 文件。它并不往可执行文件exe 中添加任何东西,只是在编译的时候将各个函数入口地址记录在后缀为.map的文件中,程序崩溃的时候可以得到一个EIP地址,通过地址知道崩溃所在函数

3、 可以设置断点,在希望设置断点的地方加入 _ASM int 3

4、 可以通过编译时的汇编程序看出

5、 采用第三方工具

查找段错误导致的非法操作用

ptrace系统调用跟踪调试运行中的进程(trussstraceltrace的原理都是根据ptrace系统调用跟踪调试运行中的进程)

truss跟踪clint的系统调用来找出错误,clintc++静态源码分析工具。通过ports安装好之后利用调试工具truss即可。
11.
核心太与用户太的区别,x86如何转换。中断调用,从ring3转到ring0

现代的操作系统一般都有核心模式和用户模式之分,操作系统核心代码运行在核心模式下,具有较高的运行级别,具有较高的运行效率和较强的底层控制权力,系统硬件为其提供了尽可能多的内存保护以及其他措施,而用户进程则一般运行在用户模式下,其运行级别较低。

x86平台下,核心态和用户态的区分主要是通过段选择子确定的,具体来说,在Linux环境下,核心态的代码段选择子为0x10,数据段选择子为0x18;而用户态的代码段选择子为0x23,数据段选择子为0x2B,因此核心态程序工作在ring0,而用户态程序工作在ring3。在用户模式下只能访问用户空间而在核心模式下可以访问系统空间和用户空间

从用户态进入核心态的最常用的方法是在寄存器eax填一个功能码,然后执行int 2e。这有点像DOS时代的DOSBIOS系统调用。在NT架构中这种机制被称作system service

const 有什么用途?(请至少说明两种)

Const用了定义一个常量,

1、用在变量前面的时候可以避免变量被修改   2、用在函数声明部分允许const 的类对象成员访问const 成员函数,如果类的成员函数不会对数据成员进行修改的话最好把该函数定义为const类型,这样无论是const的类对象还是非const 的类对象都可以访问该函数

Spinlock(自旋锁),mutex,(互斥)semaphore(信号量),vitical section(critical section的作用与区别?

都是操作系统中内核同步实现数据访问,资源共享的作用

自旋锁是在cpu之间,只能运行一个对象,自旋锁可以在任何时刻防止多于一个的内核任务(进程)同时进入临界区,因此这种锁可有效地避免多处理器上并发运行的内核任务竞争共享资源。

互斥只能运行一个对象,可以使一个进程下的线程间同步。也可以是不同进程间同步,互斥能保证公共资源不会同时被多个线程访问。互斥不仅能实现同一应用程序的公共资源安全共享,还能实现不同应用程序的公共资源安全共享

信号量可以运行多个对象,但是为线程间同步它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目

临界区同一个进程下的线程间同步,只能运行一个对象。保证在某一个时间只有一个线程可以访问数据的方法

事 件: 通过通知操作的方式来保持线程的同步,还可以方便实现对多个线程的优先级比较的操作

 正则表达式字符串匹配问题。

 比较哈希表和平衡二叉树的特点,它们分别如用那些场合。

哈希表:哈希表查找速度比较快,可以时间复杂度为O(1)的情况下找到实现查找。但是要耗比较多的内存。所以比较适用于对查找速度要求比较高、且内存空间足够的时候

平衡二叉树:如果想在以后用二分法查找的时候查找速度比较快的话用建立平衡二叉树的方法()平衡二叉树支持动态的插入和查找,保证操作在O(height)时间,这就是完成了哈希表不便完成的工作,动态性。平衡二叉树/红黑树就是为了将查找的时间复杂度保证在O(logN)范围内。所以如果输入结合确定,所需要的仅仅是查询,则可以考虑使用哈希表,如果输入集合不确定,则考虑使用平衡二叉树/红黑树,保证达到最大效率。

recv函数如何在阻塞模式下没有收到数据就返回

Rev(buff,sizeof(buff),flag)

可以将rev的标志位定义为无需等待返回,不管其是否收到数据都立即返回,也可以定义为等待时间,如果超过等待时间还没有接受到数据就立即返回,

用锁效率低,有那些方法可以避免或减少锁的使用?

将表建立表级锁,减少锁数量的使用

 

Main函数中两个参数的作用

第一个形参argc是一个整型变量,第二个形参argv是一个指针数组,其元素指向字符型

数据。用带参数的main函数可以直接从命令行得到参数值(这些值是字符串),在程序运行

时,可以根据输入的命令行中的不同情况进行相应的处理。利用main函数中的参数可以使

程序从系统得到所需的数据,增加了处理问题的灵活性。

 

进程的几个基本状态:就绪、执行、阻塞

1. 匹配"[10]:dddddd"和"[9]:abcdegf"但不匹配"[a]:xfdf"的正则表达式。/b/w{11}/b精确匹配只有11个字符的字符串

5 c++中虚函数如何定义,使用时应该注意什么?

虚函数是在类中被声明为virtual的成员函数,虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异而采用不同的策略。

多态指同一个方法根据其所属的不同对象可以有不同的行为(根据自己理解,不知这么说是否严谨)。

 

6用C/C++编程,从1到100中取出10个不同的数,要求打印出所有可能的组合;

#include <iostream.h>

int source[100];

int dest[10]={0};

int index_source=0;

int index_dest=0;

int k=10;

int i=0;

int j=0;

void composition(int source[], int index_source, int dest[], int index_dest, int k)

{

     if(100-index_source==k)

     {

        for(i=0; i<index_dest; i++)

        {

              cout<<dest<<" ";

        }

        for(i=index_source; i<100; i++)

        {

              cout<<source<<" ";

        }

        cout<<endl;

        return;

     }

     if(index_source<100 && k==1)

     {

        for(i=index_source; i<100; i++)

        {

              for(j=0; j<index_dest; j++)

              {

                     cout<<dest[j]<<" ";

              }

              cout<<source<<endl;

        }

        return;

     }

     composition(source, index_source+1, dest, index_dest, k);

     dest[index_dest++]=source[index_source];

     composition(source, index_source+1, dest, index_dest, k-1);

}

 

void main()

{

     for(int i=0;i<100;i++)

        source=i;

     composition(source,0, dest, 0, 10);

}

 

8 如何判断两个单向链表是否有相交,并找出交点。

给出两个链表的头指针pHead1 和 pHead2 ,写一个函数判断两条链表有没交叉点

Node* checkLink(Node* pHead1,Node* pHead2)

{

     Node* p1=pHead1,p2=pHead2;

     int i=1,j=1;

     if(p1==NULL || p2==NULL)

        return NULL;

     if(p1==p2)

        return p1;

     while(p1->pNext!=NULL)

     {

        p1=p1->pNext;

        i++;

     }

     while(p2->pNext!=NULL)

     {

        p2=p2->pNext;

        j++;

     }

     if(p1==p2)

        return NULL;

     else

     {

        for(int k=0;k<fabs(a-b);k++)

        {

              if(i>j)

                     p1=p1->pNext;

              else

                     p2=p2->pNext;

        }

        while(p1!=p2)

        {

              p1=p1->pNext;

              p2=p2->pNext;

        }

        return p1;

     }

}

 

 

1001个球,两个人轮流拿球,且每次只能拿124个球,规定拿到最后一个球的人为输。如果让你先拿,你是否有必胜的把握。如果有,该如何操作?

解:先拿4个,然后每轮保证两个人所拿球数之和为36

【编程】用最简单的函数实现功能:判断一个int数据是否是2x次幂(不能使用循环)。

bool Juge(int dat, int x)

{

       return !(dat & ~(1 << x));

}

【编程】对于一个给定的整形数组int array[n]。编程实现:将数组中所有小于或等于0的元素都放在数组前面,大于0的元素放在数组后面。要求时间复杂度为o(n)

void Divide(int array[], int n)

{

       int i = 0;

       for (int j = 0; j < n; j++)

       {

              if (array[j] < 0)

              {

                     int temp;

                     temp = array[i];

                     array[i] = array[j];

                     array[j] = temp;

                     i++; 

              }

       }

}

【简答】用一个程序示意常见的错误能够导致栈破坏如何检查

 

#include "iostream.h"

#include "string.h"

void main()

{

     char str[5];

     cout<<"input: ";

     cin>>str;

     while(strlen(str)>5)

     {

        cout<<"too long!"<<endl;

        cin>>str;

     }

     cout<<str<<endl;

}

 

如果系统堆栈很小,不能处理超过4级的函数调用,如何解决八皇后问题

 :#include <iostream>

#include <math.h>

#include <malloc.h>

using namespace std;

int *position; //放置的位置

int queen; //皇后数目

int count; //N种可能性//判断第n行是否放置皇后

bool SignPoint(int n)

{

for (int i=0;i<n;i++)

{

  if (*(position+i) == *(position+n)) //该列已经放置过皇后了

   return false;

  if (abs(*(position+i) - *(position+n)) == n-i) //对角线已经放置过了

   return false;

}

return true;

}//设置皇后

void SetQueen(int n=0)

{

if (queen==n)

{

  //该处可以改成自己想要的显示方式

  printf("NO.%d: ",++count);

  printf("/n");

  for (int i=0;i<queen;i++)

  {

   for (int j=0;j<queen;j++)

   {

    if (j == position[i])

    {

     printf("* ");

    }

    else

    {

     printf("0 ");

    }

   }

   printf("/n");

  }

  printf("/n");

  return;

}

else

{

  for (int i=0;i<queen;i++)

  {

   position[n] = i;   if(SignPoint(n))//如果该位置放置皇后正确的话,则到下一行

   {

    SetQueen(n+1);

   }

  }

}

}int _tmain(int argc, _TCHAR* argv[])

{

cout<<"请输入皇后的总数:"<<endl;

cin>>queen;

position = (int*)malloc(sizeof(int));

SetQueen();

cout<<"摆放完毕"<<endl;

cin.get();

cin.get();

return 0;

}

%   &   .  &&   <= = 那个优先级别最高

.  &    %   <=   &&    =

【知识点】数据类型占字节数(32位机)

Sizeof(long)=4;   *

Sizeof(int) = 4;

Sizeof(short)=2;

Sizeof(char)=1;

Sizeof(double)=8;

Sizeof(float)=4;

 

*)对于类(包括结构体),其大小为size=Maxsizeof(除静态类型的成员变量)}的整数倍。且满足size>=sumsizeof(除静态类型的成员变量)

 

【填空】下面的程序在那行崩溃:

struct {

       char c;

       char *pc;

} a;

int main(int argc, char* argv[])

{

       char *p=&a.c;

       p[0]=0;

       p[1]=0;

       p[2]=0;

       p[3]=0;

       p[4]=0;

       p[5]=0;

       a.pc=p;

       a.pc[5]=0;    *

       a.pc[4]=0;

       a.pc[3]=0;

       a.pc[2]=0;

       a.pc[1]=0;

       a.pc[0]=0;

       return 0;

}

 

【编程】实现函数atoiitoa

int atoi(const char *s)

{

       int dat = 0;

       bool neg = false;

 

       if (*s == '-')

       {

              s++;

              neg = true;

       }

 

       while(*s)

       {

              dat *= 10;

              if (*s >= '0' || *s <= '9')

                     dat += *s - '0';

              else

                     return -1;

              s++;

       }

       return neg ? (-1*dat) : dat;

}

// num:待转换的整型变量   str:指向存放结果    radix:基数

char *itoa(int num, char *str, int radix) 

{

   Const char table[]

= "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

       char *ptr = str;

 

       if (num == 0)

       {     //数值为0

              *ptr++ = '0';

              *ptr = '/0';

              return str;

       }

 

       bool neg = false;

       if (num < 0)

       {     //数值小于0时,添加负号,并将指针后移

              neg = true;

              num *= -1;

              *ptr++ = '-';

       }

 

    while (num)

    {

        *ptr++ = table[num % radix];

        num /= radix;

    }

       *ptr = '/0';

       //反转字符串

       char *beg = (neg ? str +1 : str);

       ptr--;

       while (beg < ptr)

    {

        int temp = *beg;

        *beg  = *ptr;

        *ptr  = temp;

              beg++;

              ptr--;

    }

    return str;

}

【知识点】reinterpret_cast

  reinterpret_castC++里的强制类型转换符。

  操作符修改了操作数类型,但仅仅是重新解释了给出的对象的比特模型而没有进行二进制转换。

  例如:int *n= new int ;

  double *d=reinterpret_cast<double*> (n);

  在进行计算以后, d 包含无用值. 这是因为 reinterpret_cast 仅仅是复制 n 的比特位到 d, 没有进行必要的分析。

  因此, 需要谨慎使用 reinterpret_cast.

  并且:reinterpret_cast 只能在指针之间转换。

  == ===========================================

  == static_cast .vs. reinterpret_cast

  == ================================================

  reinterpret_cast是为了映射到一个完全不同类型的意思,这个关键词在我们需要把类型映射回原有类型时用到它。我们映射到的类型仅仅是为了故弄玄虚和其他目的,这是所有映射中最危险的。(这句话是C++编程思想中的原话)

  static_cast reinterpret_cast 操作符修改了操作数类型。它们不是互逆的; static_cast 在编译时使用类型信息执行转换,在转换执行必要的检测(诸如指针越界计算, 类型检查). 其操作数相对是安全的。另一方面;reinterpret_cast 仅仅是重新解释了给出的对象的比特模型而没有进行二进制转换, 例子如下:

  int n=9; double d=static_cast < double > (n);

  上面的例子中, 我们将一个变量从 int 转换到 double 这些类型的二进制表达式是不同的。 要将整数 9 转换到 双精度整数 9static_cast 需要正确地为双精度整数 d 补足比特位。其结果为 9.0。而reinterpret_cast 的行为却不同:

  int n=9;

  double d=reinterpret_cast<double & > (n);

  这次, 结果有所不同. 在进行计算以后, d 包含无用值. 这是因为 reinterpret_cast 仅仅是复制 n 的比特位到 d, 没有进行必要的分析.

  因此, 你需要谨慎使用 reinterpret_cast.

【知识点】dynamic_cast

  用法:dynamic_cast < type-id > ( expression )

  该运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void *

  如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。

  dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。

  在类层次间进行上行转换时,dynamic_caststatic_cast的效果是一样的;

  在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。

  class B{

  public:

  int m_iNum;

  virtual void foo();

  };

  class D:public B{

  public:

  char *m_szName[100];

  };

  void func(B *pb){

  D *pd1 = static_cast<D *>(pb);

  D *pd2 = dynamic_cast<D *>(pb);

  }

  在上面的代码段中,如果pb指向一个D类型的对象,pd1pd2是一样的,并且对这两个指针执行D类型的任何操作都是安全的;

  但是,如果pb指向的是一个B类型的对象,那么pd1将是一个指向该对象的指针,对它进行D类型的操作将是不安全的(如访问m_szName),

  而pd2将是一个空指针。

  另外要注意:B要有虚函数,否则会编译出错;static_cast则没有这个限制。

  这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表(

  关于虚函数表的概念,详细可见<Inside c++ object model>)中,只有定义了虚函数的类才有虚函数表,

  没有定义虚函数的类是没有虚函数表的。

  另外,dynamic_cast还支持交叉转换(cross cast)。如下代码所示。

  class A{

  public:

  int m_iNum;

  virtual void f(){}

  };

  class B:public A{

  };

  class D:public A{

  };

  void foo(){

  B *pb = new B;

  pb->m_iNum = 100;

  D *pd1 = static_cast<D *>(pb); //compile error

  D *pd2 = dynamic_cast<D *>(pb); //pd2 is NULL

  delete pb;

  }

  在函数foo中,使用static_cast进行转换是不被允许的,将在编译时出错;而使用 dynamic_cast的转换则是允许的,结果是空指针。

 

【知识点】const_cast

  用法:const_cast<type_id> (expression)

  该运算符用来修改类型的constvolatile属性。除了const volatile修饰之外, type_idexpression的类型是一样的。

  常量指针被转化成非常量指针,并且仍然指向原来的对象;

  常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象被转换成非常量对象。

  Voiatileconst类试。举如下一例:

  class B{

  public:

  int m_iNum;

  }

  void foo(){

  const B b1;

  b1.m_iNum = 100; //comile error

  B b2 = const_cast<B>(b1);

  b2. m_iNum = 200; //fine

  }

  上面的代码编译时会报错,因为b1是一个常量对象,不能对它进行改变;

  使用const_cast把它转换成一个常量对象,就可以对它的数据成员任意改变。注意:b1b2是两个不同的对象。

【知识点】static_cast

  用法:static_cast < type-id > ( expression )

  该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法:

  ①用于类层次结构中基类和子类之间指针或引用的转换。

  进行上行转换(把子类的指针或引用转换成基类表示)是安全的;

  进行下行转换(把基类指针或引用转换成子类表示)时,由于没有动态类型检查,所以是不安全的。

  ②用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。

  ③把空指针转换成目标类型的空指针。

  ④把任何类型的表达式转换成void类型。

  注意:static_cast不能转换掉expressionconstvolitale、或者__unaligned属性。

  C++static_castreinterpret_cast的区别

  C++primer第五章里写了编译器隐式执行任何类型转换都可由static_cast显示完成;reinterpret_cast通常为操作数的位模式提供较低层的重新解释

  1C++中的static_cast执行非多态的转换,用于代替C中通常的转换操作。因此,被做为隐式类型转换使用。比如:

  int i;

  float f = 166.7f;

  i = static_cast<int>(f);

  此时结果,i的值为166

  2C++中的reinterpret_cast主要是将数据从一种类型的转换为另一种类型。所谓通常为操作数的位模式提供较低层的重新解释也就是说将数据以二进制存在形式的重新解释。比如:

  int i;

  char *p = "This is a example.";

  i = reinterpret_cast<int>(p);

  此时结果,ip的值是完全相同的。reinterpret_cast的作用是说将指针p的值以二进制(位模式)的方式被解释为整型,并赋给i,一个明显的现象是在转换前后没有数位损失。

 

【简答】release版本的可执行文件。如何获知生成的文件是否过大。

1是数制转换1512进制和九进制。10010111          177

2已知0ASCII码为0x40,那么int   120;在内存中的表示形式是0x__  780ASCII码为0x40,应该为0x30

3

1、在linux下,查看目录大小的命令是:du $ du -sm

    2、修改文件属性的命令是:chmod [options] mode files

    3、切换为其他用户身份的命令是:su-filemane

 

4还有一道指针交换数值 int i=0,j=10,int* p=&i, int* q=&j,

int fun (**a,*b)

{int* temp=a;

*a*=10;

*b*=10;

a=b;

b=temp;

}最后问调用fun(&p,q)ijpq的最终值(具体形式大概如此,但中间指针肯定记的错误)

此题主要考察指针指向一个整数,然后利用指针改变变量,最后交换指针

·         5有道填插入排序的算法。有一个数组a[0]   a[i-1]为从小到大排序,a[i]  a[count-1]没有排序,请您添加3条语句使它们按照从小到大排序

int insert_sort(int a[],int count)

{

        for(int i=1;i<count;++i)

        {

        int j,t;

        t=a[i];

        (j=i-1;)

        while(j>=0&&t<a[j])

               {

               (a[j+1]=a[j];)

               j--;

               }

               (a[j+1]=t;)

        }

        return 0;

}

三,编程与逻辑题

1自己写一个strstr

(单链表判断有无环,)

char* strstr(char* buf, char* sub)

{

char* bp;

char* sp;

If(!*sub)

return  buf;

while(*buf)

{bf=buf;

sp=sub;

do{    if(!*sp)

return buf;

}

while(*bp++==*sp++)

buf+=1;

}

return 0;

}

2遍历文本找单词并删掉出现频率最少的单词,fun (char* pText)

 #include <stdio.h>
#include <stdarg.h> 
//定义av_listav_startav_arg等宏

·         3实现一个与printf功能相似的函数

#include <iostream>
#include <conio.h>
#include <stdio.h>
#include <stdarg.h> 
//定义av_listav_startav_arg等宏

/*******************************************************************
此函数的作用:
    
实现一个参数个数可变的函数,此函数的功能与printf类似,
    
但在格式处理上,不如printf丰富
    
无异常,返回一个true,否则返回false
format
字符串的合法情况如下:

    1."%%zyk%%zyk%%",OUTPUT:%zyk%zyk%
    2."%dzyk%fzyk%s",OUTPUT:(int)zyk(float)zyk(string)
    3."zyk",    OUTPUT:zyk
非法情况如下:
    1."%zyk%"    ERROR:
不存在%z格式、%后面必须跟一个格式字符
*******************************************************************/

bool zykPrintf(const char * format,...)
{
    
//定义一个可用于指向参数的指针(实为char *),

    va_list argPtr;

    
//把函数的第一个参数format的地址传给
argPtr
    va_start(argPtr,format);
        
    
const int
 size = strlen(format)+1;
    
char *tmp = new char
[size];
    memset(tmp, 0, size);

 

·         while (*format != 0)
    {
        
int
 i;
        
for
 (i=0; i<size && *format!='%' && *format!=0; i++)
        {
            tmp[i]=*format++;
        }
        tmp[i] = 0; 
//在有效的字符串末尾作0值防护


        printf("%s",tmp);

        
if (*format == 0)
            
return true
;

        
switch
(*++format)
        {
        
//按指定类型读取下一个参数,并打印

        case 'd': { printf("%d", va_arg(argPtr, int)); break;}
        
case 's': { printf("%s", va_arg(argPtr, char *)); break
; }
        
case 'c': { printf("%c", va_arg(argPtr, char)); break
;}
        
case 'f': { printf("%f", va_arg(argPtr, float)); break
;}
        
        
//%%的处理

        case '%': { printf("%%"); break; }

        
//格式错误

        default : { printf(" Error Ocurr!Please Check the Format!"); return false;}
        }

        ++format;

    }
    
    delete[] tmp;
    
return true
;
}

int main(int argc,char
 * argv[])
{

    zykPrintf("%zyk"); 
//error
    zykPrintf("zyk%"); 
//error

    zykPrintf("%%zyk%%zyk%%"); 
//OUTPUT: %zyk%zyk%
    zykPrintf("/nzyk is a pretty boy! His age is %d and %s",5,"I love zyk^_^!");

                     getch();
                     
return 0;

}


1
strstr的实现原型。

char *my_strstr(const char *str, const char *strSearch)
{
while (*str != '/0')
{
char *p = (char *)str;
char *ps = (char *)strSearch;
while ( ps && *p == *ps )
p , ps ;
if ('/0' == *ps)
return (char *)str;
str ;
}
return NULL;
}

3
printf的实现。

int printf(const char *format, ...)
{
va_list arglist;
int buffing;
int retval;

va_start(arglist, format);

_ASSERTE(format != NULL);

#ifdef _MT
_lock_str2(1, stdout);
__try {
#endif /* _MT */

buffing = _stbuf(stdout);

retval = _output(stdout,format,arglist);

_ftbuf(buffing, stdout);

#ifdef _MT
}
__finally {
_unlock_str2(1, stdout);
}
#endif /* _MT */

return(retval);
}

8 如何判断两个单向链表是否有相交,并找出交点。

给出两个链表的头指针pHead1 和 pHead2 ,写一个函数判断两条链表有没交叉点

Node* checkLink(Node* pHead1,Node* pHead2)

{

     Node* p1=pHead1,p2=pHead2;

     int i=1,j=1;

     if(p1==NULL || p2==NULL)

        return NULL;

     if(p1==p2)

        return p1;

     while(p1->pNext!=NULL)

     {

        p1=p1->pNext;

        i++;

     }

     while(p2->pNext!=NULL)

     {

        p2=p2->pNext;

        j++;

     }

     if(p1==p2)

        return NULL;

     else

     {

        for(int k=0;k<fabs(a-b);k++)

        {

              if(i>j)

                     p1=p1->pNext;

              else

                     p2=p2->pNext;

        }

        while(p1!=p2)

        {

              p1=p1->pNext;

              p2=p2->pNext;

        }

        return p1;

     }

}

17.面向对象的三个基本特征并简单叙述之

1. 封装将客观事物抽象成类每个类对自身的数据和方法实行protection(private, protected,public)

2. 继承:广义的继承有三种实现形式:实现继承(指使用基类的属性和方法而无需额外编码的能力)、可视继承(子窗体使用父窗体的外观和实现代码)、接口继承(仅使用属性和方法,实现滞后到子类实现)。前两种(类继承)和后一种(对象组合=>接口继承以及纯虚函数)构成了功能复用的两种方式。

3. 多态:是将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。

18. 重载(overload)和重写(overried,有的书也叫做“覆盖”)的区别?

常考的题目。从定义上来说:

重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。

重写:是指子类重新定义复类虚函数的方法。

从实现原理上来说:

重载:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:function func(p:integer):integer;和function func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的:int_func、str_func。对于这两个函数的调用,在编译器间就已经确定了,是静态的。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关

重写:和多态真正相关。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚绑定)。

19. 多态的作用?

主要是两个:1. 隐藏实现细节,使得代码能够模块化;扩展代码模块,实现代码重用;2. 接口重用:为了类在继承和派生的时候,保证使用家族中任一类的实例的某一属性时的正确调用

21. New delete 与malloc free 的联系与区别?

答案:都是在堆(heap)上进行动态的内存操作。用malloc函数需要指定内存分配的字节数并且不能初始化对象,new 会自动调用对象的构造函数。delete 会调用对象的destructor,而free 不会调用对象的destructor.

23. 有哪几种情况只能用intialization list 而不能用assignment?

答案:当类中含有const、reference 成员变量;基类的构造函数都需要初始化表。

24. C++是不是类型安全的?

答案:不是。两个不同类型的指针之间可以强制转换(用reinterpret cast)。C#是类型安全的。

25. main 函数执行以前,还会执行什么代码?

答案:全局对象的构造函数会在main 函数之前执行。

26. 描述内存分配方式以及它们的区别?

1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量

2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。

3) 从堆上分配亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多。

27.struct 和 class 的区别

答案:struct 的成员默认是公有的,而类的成员默认是私有的。struct 和 class 在其他方面是功能相当的。

从感情上讲,大多数的开发者感到类和结构有很大的差别。感觉上结构仅仅象一堆缺乏封装和功能的开放的内存位,而类就象活的并且可靠的社会成员,它有智能服务,有牢固的封装屏障和一个良好定义的接口。既然大多数人都这么认为,那么只有在你的类有很少的方法并且有公有数据(这种事情在良好设计的系统中是存在的!)时,你也许应该使用 struct 关键字,否则,你应该使用 class 关键字。

28.当一个类A 中没有生命任何成员变量与成员函数,这时sizeof(A)的值是多少,1

29.如果不是零,请解释一下编译器为什么没有让它为零。(Autodesk)

答案:肯定不是零。举个反例,如果是零的话,声明一个class A[10]对象数组,而每一个对象占用的空间是零,这时就没办法区分A[0],A[1]…了。

29. 在8086 汇编下,逻辑地址和物理地址是怎样转换的?(Intel)

答案:通用寄存器给出的地址,是段内偏移地址,相应段寄存器地址*10H+通用寄存器内地址,就得到了真正要访问的地址。

31.分别写出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)

 

32.请说出const#define 相比有何优点

答案1const 常量有数据类型而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。

     2) 有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。

33.简述数组与指针的区别?

数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。

(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 字节

}

34.类成员函数的重载、覆盖和隐藏区别

答案

a.成员函数被重载的特征:

(1)相同的范围(在同一个类中);

(2)函数名字相同;

(3)参数不同;

(4)virtual 关键字可有可无。

b.覆盖是指派生类函数覆盖基类函数,特征是:

(1)不同的范围(分别位于派生类与基类);

(2)函数名字相同;

(3)参数相同;

(4)基类函数必须有virtual 关键字。

c.“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:

(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。

(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)

35. There are two int variables: a and b, dont use if, ? :, switchor other judgement statements, find out the biggest one of the two numbers.

答案( ( a + b ) + abs( a - b ) ) / 2

36. 如何打印出当前源文件的文件名以及源文件的当前行号?

答案

cout << __FILE__ ;

cout<<__LINE__ ;

__FILE__和__LINE__是系统预定义宏,这种宏并不是在某个文件中定义的,而是由编译器定义的。

37. main 主函数执行完毕后,是否可能会再执行一段代码,给出说明?

答案:可以,可以用_onexit 注册一个函数,它会在main 之后执行int fn1(void), fn2(void), fn3(void), fn4 (void);

void main( void )

{

String str("zhanglin");

_onexit( fn1 );

_onexit( fn2 );

_onexit( fn3 );

_onexit( fn4 );

printf( "This is executed first./n" );

}

int fn1()

{

printf( "next./n" );

return 0;

}

int fn2()

{

printf( "executed " );

return 0;

}

int fn3()

{

printf( "is " );

return 0;

}

int fn4()

{

printf( "This " );

return 0;

}

The _onexit function is passed the address of a function (func) to be called when the program terminates normally. Successive calls to _onexit create a register of functions that are executed in LIFO (last-in-first-out) order. The functions passed to _onexit cannot take parameters.

38. 如何判断一段程序是由C 编译程序还是由C++编译程序编译的

答案

#ifdef __cplusplus

cout<<"c++";

#else

cout<<"c";

#endif

39.文件中有一组整数要求排序后输出到另一个文件中

答案

i nclude<iostream>

i nclude<fstream>

using namespace std;

 

void Order(vector<int>& data) //bubble sort

{

int count = data.size() ;

int tag = false ; // 设置是否需要继续冒泡的标志位

for ( int i = 0 ; i < count ; i++)

{

for ( int j = 0 ; j < count - i - 1 ; j++)

{

if ( data[j] > data[j+1])

{

tag = true ;

int temp = data[j] ;

data[j] = data[j+1] ;

data[j+1] = temp ;

}

}

if ( !tag )

break ;

}

}

 

void main( void )

{

vector<int>data;

ifstream in("c://data.txt");

if ( !in)

{

cout<<"file error!";

exit(1);

}

int temp;

while (!in.eof())

{

in>>temp;

data.push_back(temp);

}

in.close(); //关闭输入文件流

Order(data);

ofstream out("c://result.txt");

if ( !out)

{

cout<<"file error!";

exit(1);

}

for ( i = 0 ; i < data.size() ; i++)

out<<data[i]<<" ";

out.close(); //关闭输出文件流

}

 

40. 链表题一个链表的结点结构

struct Node

{

int data ;

Node *next ;

};

typedef struct Node Node ;

 

(1)已知链表的头结点head,写一个函数把这个链表逆序 ( Intel)

Node * ReverseList(Node *head) //链表逆序

{

if ( head == NULL || head->next == NULL )

return head;

Node *p1 = head ;

Node *p2 = p1->next ;

Node *p3 = p2->next ;

p1->next = NULL ;

while ( p3 != NULL )

{

p2->next = p1 ;

p1 = p2 ;

p2 = p3 ;

p3 = p3->next ;

}

p2->next = p1 ;

head = p2 ;

return head ;

}

(2)已知两个链表head1 head2 各自有序请把它们合并成一个链表依然有序。(保留所有结点,即便大小相同)

Node * Merge(Node *head1 , Node *head2)

{

if ( head1 == NULL)

return head2 ;

if ( head2 == NULL)

return head1 ;

Node *head = NULL ;

Node *p1 = NULL;

Node *p2 = NULL;

if ( head1->data < head2->data )

{

head = head1 ;

p1 = head1->next;

p2 = head2 ;

}

else

{

head = head2 ;

p2 = head2->next ;

p1 = head1 ;

}

Node *pcurrent = head ;

while ( p1 != NULL && p2 != NULL)

{

if ( p1->data <= p2->data )

{

pcurrent->next = p1 ;

pcurrent = p1 ;

p1 = p1->next ;

}

else

{

pcurrent->next = p2 ;

pcurrent = p2 ;

p2 = p2->next ;

}

}

if ( p1 != NULL )

pcurrent->next = p1 ;

if ( p2 != NULL )

pcurrent->next = p2 ;

return head ;

}

(3)已知两个链表head1 head2 各自有序请把它们合并成一个链表依然有序这次要求用递归方法进行。 (Autodesk)

答案

Node * MergeRecursive(Node *head1 , Node *head2)

{

if ( head1 == NULL )

return head2 ;

if ( head2 == NULL)

return head1 ;

Node *head = NULL ;

if ( head1->data < head2->data )

{

head = head1 ;

head->next = MergeRecursive(head1->next,head2);

}

else

{

head = head2 ;

head->next = MergeRecursive(head1,head2->next);

}

return head ;

}

41. 分析一下这段程序的输出 (Autodesk)

class B

{

public:

B()

{cout<<"default constructor"<<endl;}

~B()

{cout<<"destructed"<<endl;}

B(int i):data(i)   //B(int) works as a converter ( int -> instance of  B)

{cout<<"constructed by parameter " << data <<endl;}

private:

int data;

};

B Play( B b)

{return b ;}

(1)                                           results:

int main(int argc, char* argv[])      constructed by parameter 5

{                                    destructed B(5)形参析构

B t1 = Play(5); B t2 = Play(t1);    destructed t1形参析构

return 0;               destructed t2 注意顺序

}                                     destructed t1

(2)                                   results:

int main(int argc, char* argv[])      constructed by parameter 5

{                                    destructed B(5)形参析构

B t1 = Play(5); B t2 = Play(10);    constructed by parameter 10

return 0;               destructed B(10)形参析构

}                                    destructed t2 注意顺序

                                      destructed t1

 

深信服科技公司2008校园招聘笔试题

(转载)

1 变量和值的存储位置(//代码段/数据段等)?

2 sizeof

struct

{

  short a;

  long b;

  char c;

}d;

sizeof(d)? 为什么在不同的平台上得到的值不一样

3 找规律

2

1 2

1 1 1 2

3 1 1 2

1 3 1 1 2

————— 问下一行应该填什么为什么

4 linux(Redhat)的启动顺序?

5 c++中虚函数如何定义,使用时应该注意什么?

6 1100的数中取出10个数的不同种取法,打印所有的取法。

7 如何用最简单的程序实现大端小端的判断。

8 如何判断两个单向链表是否有相交,并找出交点。

9 1000个球和10个箱子,将所有的球装入10箱子中,问如何装球,使得你可以取出不同箱子的组合便可  得到11000球。

10 八皇后问题,堆栈很少,要求不能用递归。

11 订票系统

12 用一个程序示意常见的错误能够导致栈破坏,如何检查?

13 用锁效率低,有那些方法可以避免或减少锁的使用?

14 如何在Release版本中查找以下问题,

a 内存泄漏  b 段错误导致非法操作

c 程序CPU占用100%

15 C++拷贝构造函数和赋值运算符有那些不同和相同点。

16 比较哈希表和平衡二叉树的特点,它们分别如用那些场合。

17 spinlockmutex,semaphorevitical section的作用与区别?

18 正则表达式

19 字符串匹配问题。要求在s1中删除所有s2的字符,要用最快的算法

20 函数前的staticvolatile变量中关键字的作用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值