您的位置:首页 > 编程语言 > C语言/C++

c++面试题

2013-06-27 15:48 302 查看
1、C++中为什么用模板类。

(1)可用来创建动态增长和减小的数据结构

(2)复用性,可移植性,它在编译时而不是运行时检查数据类型,保证了类型安全.

(3)可用于基本数据类型

2、CSingleLock是干什么的。

答:同步多个线程对一个数据类的同时访问

3、函数模板与类模板有什么区别?

答:函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化必须由程序员在程序中显式地指定。

4、引用与指针有什么区别?

1) 引用必须被初始化,指针不必。

2) 引用初始化以后不能被改变,指针可以改变所指的对象。

3) 不存在指向空值的引用,但是存在指向空值的指针。

5、全局变量和局部变量在内存中是否有区别?如果有,是什么区别?

全局变量储存在静态数据库,局部变量在堆栈。

6、什么是平衡二叉树?

左右子树都是平衡二叉树 且左右子树的深度差值的绝对值不大于1。

7、堆栈溢出一般是由什么原因导致的?

没有回收垃圾资源。内存泄露

8、冒泡排序算法的时间复杂度是什么?

时间复杂度是O(n^2)。

9、写出float x 与“零值”比较的if语句。

if(x>0.000001&&x<-0.000001)

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

#defineSECONDS_PER_YEAR (60 * 60 * 24 * 365)UL

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

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

11、 写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个。

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

12、 预处理器标识#error的目的是什么?

#error:停止编译并显示错误信息

14、 用变量a给出下面的定义

a) 一个整型数(An integer)

b) 一个指向整型数的指针(A pointer to an integer)

c) 一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a pointer to aninteger)

d) 一个有10个整型数的数组(An array of 10integers)

e) 一个有10个指针的数组,该指针是指向一个整型数的(An array of 10 pointers tointegers)

f) 一个指向有10个整型数数组的指针(A pointer to an array of 10integers)

g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes aninteger as an argument and returns an integer)

h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数(Anarray 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 aninteger argument and returns an integer

h) int (*a[10])(int); // An array of 10 pointers to functions thattake an integer argument and return an integer

16、关键字const是什么含意?

const int a;

int const a;

const int *a;

int * const a;

int const * a const;各是什么意思?

答:前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。(const的作用:

17、关键字volatile有什么含意并给出三个不同的例子。

一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:

1). 状态寄存器

2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)

3). 多线程应用中被几个任务共享的变量

附加问题: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;

}

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

为了访问一绝对地址把一个整型数强制转换(typecast)为一指针是合法的。

int *ptr;

ptr = (int *)0x67a9;

*ptr = 0xaa55;

20、中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展—让标准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()经常有重入和性能上的问题。

21、下面的代码输出是什么,为什么?

void foo(void)

{

unsigned int a = 6;

int b = -20;

(a+b > 6) puts("> 6") :puts("<= 6");

}

这无符号整型问题的答案是输出是 “>6”。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。

22、评价下面的代码片断:(处理器字长的重要性)

unsigned int zero = 0;

unsigned int compzero = 0xFFFF;

对于一个int型不是16位的处理器为说,上面的代码是不正确的。应编写如下:

unsigned int compzero = ~0;

c/c++笔试题(2)(转载的)

1.尽管不像非嵌入式计算机那么常见,嵌入式系统还是有从堆(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

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

晦涩的语法

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

int a = 5, b = 7, c;

c = a+++b;

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

c = a++ + b;

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

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

4、用递归算法判断数组a
是否为一个递增数组。

递归的方法,记录当前最大的,并且判断当前的是否比这个还大,大则继续,否则返回false结束:

bool fun( int a[], int n )

{

if( n= =1 )

return true;

if( n= =2 )

return a[n-1] >= a[n-2];

return fun( a,n-1) && ( a[n-1]>= a[n-2] );

}

6.单连表的建立,把'a'--'z'26个字母插入到连表中,并且倒叙,还要打印!

方法1:

typedef struct val

{ int date_1;

struct val *next;

}*p;

void main(void)

{ char c;

for(c=122;c>=97;c--)

{ p.date=c;

p="p-">next;

}

p.next=NULL;

}

}

9、输出和为一个给定整数的所有组合

例如n=5

5=1+4;5=2+3(相加的数不能重复)

则输出

1,4;2,3。

答案:

#include<stdio.h>

void main()

{

unsigned long int a,i=1;

scanf("%d",&a);

if(a%2==0)

{

for(i=1;i<a/2;i++)

printf("%d,%d",i,a-i);

}

else

for(i=1;i<=a/2;i++)

printf(" %d, %d",i,a-i);

}

12、运行的结果为什么等于15

#include"stdio.h"

#include "string.h"

void main()

{

char aa[10];

printf("%d",strlen(aa));

}

答案:sizeof()和初不初始化,没有关系;strlen()和初始化有关。

16、改错:

#include<stdio.h>

int main(void) {

int **p;

int arr[100];

p = &arr;

return 0;

}

答案:搞错了,是指针类型不同,

int **p; //二级指针

&arr; //得到的是指向第一维为100的数组的指针

应该这样写

#include<stdio.h>

int main(void) {

int **p, *q;

int arr[100];

q = arr;

p = &q;

return 0;

17、在C++ 程序中调用被C编译器编译后的函数,为什么要加extern“C”?

答:C++语言支持函数重载,C语言不支持函数重载。函数被C++编译后在库中的名字与C语言的不同。假设某个函数的原型为:voidfoo(int x, int y);

该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。

C++提供了C连接交换指定符号extern“C”来解决名字匹配问题。

22、宏中"#"和"##"的用法

一、一般用法

我们使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起.

用法:

#include<cstdio>

#include<climits>

using namespace std;

#define STR(s) #s

#define CONS(a,b) int(a##e##b)

int main()

{

printf(STR(vck)); // 输出字符串"vck"

printf("%d/n", CONS(2,3)); // 2e3 输出:2000

return 0;

}

c/c++笔试题(3)(转载的)

第2题:考查自加操作(++)

-------------------------------------------------

8. 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

原因如下:

但是prt与(&a+1)类型是不一样的(这点很重要)

所以prt-1只会减去sizeof(int*)

  a,&a的地址是一样的,但意思不一样

a是数组首地址,也就是a[0]的地址,&a是对象(数组)首地址,

a+1是数组下一元素的地址,即a[1],&a+1是下一个对象的地址,即a[5].

--------------------------------------------

10.

char *s="AAA";

printf("%s",s);

s[0]='B';

printf("%s",s);

  有什么错?

答案:

"AAA"是字符串常量。s是指针,指向这个字符串常量,所以声明s的时候就有问题。

cosnt char* s="AAA";

然后又因为是常量,所以对是s[0]的赋值操作是不合法的。

---------------------------------------------

11. int (*s[10])(int)表示的是什么?

答案:int (*s[10])(int)函数指针数组,每个指针指向一个int func(intparam)的函数。

---------------------------------------------

------------------------------------------

13. #include<stdio.h>

#include<stdlib.h>

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操作很危险

博 主:getmemory中p是形参,是一个指针变量,getmemory(str)调用后,传入的是指针变量保存的对象地址,p=(char *)malloc(100)实际上是把申请的动态内存空间的首地址付给p指向的地址(即str指向的地址null),这个是错误的。应该修改成指向指针的指针 void getmemory(char **p),这样malloc返回的地址付给*p(即str变量本身)。

-----------------------------------------

14. char szstr[10];

  strcpy(szstr,"0123456789");

  产生什么结果?为什么?

答案:长度不一样,会造成非法的OS

------------------------------------------

16. 分析下面的程序:

void GetMemory(char **p,intnum)

{ //p,指向指针的指针,*p,p指向的指针(即str),**p,最终的对象,str指向的单元

*p=(char *)malloc(num); //申请空间首地址付给传入的被p指向的指针,即str

}

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。

--------------------------------------------

19.以下代码有什么问题?

struct Test

{

Test( int ) {}

Test() {}

void fun() {}

};

void main( void )

{

Test a(1);

a.fun();

Test b();

b.fun();

}

答:变量b定义出错。按默认构造函数定义对象,不需要加括号。

20.以下代码有什么问题?

cout <<(true?1:"1") <<endl;

答:三元表达式“?:”问号后面的两个操作数必须为同一类型。

21.以下代码能够编译通过吗,为什么?

unsigned int const size1 =2;

char str1[ size1];

unsigned int temp =0;

cin >>temp;

unsigned int const size2 =temp;

char str2[ size2];

答:str2定义出错,size2非编译器期间常量,而数组定义要求长度必须为编译期常量。

24. C++中的空类,默认产生哪些类成员函数?

答:

class Empty

{

public:

Empty(); //缺省构造函数

Empty( const Empty&); //拷贝构造函数

~Empty(); //析构函数

Empty& operator=(const Empty& ); //赋值运算符

Empty* operator&();//取址运算符

const Empty*operator&() const; //取址运算符const

};

25.以下两条输出语句分别输出什么?

float a = 1.0f;

cout <<(int)a << endl;

cout <<(int&)a <<endl;

cout <<boolalpha << ( (int)a ==(int&)a ) << endl;//输出什么?

float b = 0.0f;

cout <<(int)b << endl;

cout <<(int&)b <<endl;

cout <<boolalpha << ( (int)b ==(int&)b ) << endl;//输出什么?

答:分别输出false和true。注意转换的应用。(int)a实际上是以浮点数a为参数构造了一个整型数,该整数的值是1,(int&)a则是告诉编译器将a当作整数看(并没有做任何实质上的转换)。因为1以整数形式存放和以浮点形式存放其内存数据是不一样的,因此两者不等。对b的两种转换意义同上,但是0的整数形式和浮点形式其内存数据是一样的,因此在这种特殊情形下,两者相等(仅仅在数值意义上)。

注意,程序的输出会显示(int&)a=1065353216,这个值是怎么来的呢?前面已经说了,1以浮点数形式存放在内存中,按ieee754规定,其内容为0x0000803F(已考虑字节反序)。这也就是a这个变量所占据的内存单元的值。当(int&)a出现时,它相当于告诉它的上下文:“把这块地址当做整数看待!不要管它原来是什么。”这样,内容0x0000803F按整数解释,其值正好就是1065353216(十进制数)。

通过查看汇编代码可以证实“(int)a相当于重新构造了一个值等于a的整型数”之说,而(int&)的作用则仅仅是表达了一个类型信息,意义在于为cout<<及==选择正确的重载版本。

12.Linux有内核级线程么。

答:线程通常被定义为一个进程中代码的不同执行路线。从实现方式上划分,线程有两

种类型:“用户级线程”和“内核级线程”。用户线程指不需要内核支持而在用户程序

中实现的线程,用户线程不需要额外的内核开支,并且用户态线程的实现方式可以被定制或修改以适应特殊应用的要求,但是当一个线

程因I/O 而处于等待状态时,整个进程就会被调度程序切换为等待状态,其他线程得不

到运行的机会;而内核线程则没有各个限制,有利于发挥多处理器的并发优势,但却占

用了更多的系统开支。

Windows NT和OS/2支持内核线程。Linux 支持内核级的多线程

14.使用线程是如何防止出现大的波峰。

答:意思是如何防止同时产生大量的线程,方法是使用线程池,线程池具有可以同时提

高调度效率和限制资源使用的好处,线程池中的线程达到最大数时,其他线程就会排队

等候

22.TCP/IP建立连接的过程?(3-way shake)

答:在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。

第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状

态,等待服务器确认;

第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个

SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1)

,此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

13.浅谈bootloader,kelnel,filesystem三者之间的关系.嵌入式是linux启动过程如下。

bootloader->kernel->filesystem->application
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: