C和C++经典面试题(面试必备)

玛丽莲梦兔
627次浏览
2021年02月18日 12:23
最佳经验
本文由作者推荐

战国七雄地图-

2021年2月18日发(作者:鞭炮画法)


C/C++


经典面试题(面试必备)




面试题



1


:变量的声明和定义有什么区别


< /p>


为变量分配地址和存储空间的称为定义,


不分配地址的称为声明。



个变量可以在多个地方声明,



但是只在一个地方定义。加入



extern


修饰的是变量的声明,说明此

< br>变量将在文件以外或在文件后面部分



定义。



说明:很多时候一个变量,< /p>


只是声明不分配内存空间,直到具体使用


时才初始化,分配内存空 间,



如外部变量。



面试题


2


:写出


bool



int



float


、指针变量与


“零值”



比较的


if


语句



bool


型数据:



if( flag )


{


A;


}


else


{


B




}


int


型数据:



if( 0 != flag )


{


A;


}


else


{


B




}


指针型数:



if( NULL == flag )


{


A;


}


else


{


B




}


float


型数据:



if ( ( flag >= NORM ) && ( flag <= NORM ) )


{


A




} 2


注意:


应特别注意在


int



指针型变量和


“零值”


比较的时候,



“零


值”放在左边, 这样当把“


==




误写成“


=


”时,编译器可以报错, 否则这种逻辑错误不容易发现,


并且可能导致很严重的后果。



面试题


3



sizeof



strlen


的区




sizeof



strlen


有以下区别:



< /p>


是一个操作符,





是库函 数。






的参数可以 是数据的类型,也可以是变量,而





只能以结尾 为




0‘


的字 符串作参数。






编译器在编译时就计算出了



sizeof


的结果。而



strlen


函数必须


在运行时才能计算出来。并且



sizeof


计算的是数据类型占内存的大小,而



strlen


计算的是字符串实际的


长度。






数组做



sizeof


的参数不退化,传递给



strlen


就退化为指针了。


< /p>


注意:


有些是操作符看起来像是函数,


而 有些函数名看起来又像操作


符,这类容易混淆的名称一定



要加以区分,


否则遇到数组名这类特殊数据类型作参数时就很容易出


错。最容易混淆为函数的操作符就




sizeof




面试题


4



C


语言的关键字


static



C++


的关键字


static


有什


么区别





C




static

< br>用来修饰局部静态变量和外部静态变量、


函数。



C++


中除了上述功能外,还用来定



义类的成员变量和函数。即静态成员和静态成员函数。



注意:


编程时


static


的记忆性,


和全局性的特点可以让在不同时期


调用的函数进行通信,传递信息,




C++


的静态成员则可以在多个对象实例间进行通信,传递信息。



面试题


5




C中的


malloc


和C++中的


new


有什么区别



malloc



new


有以下不同:





1




new




delete


是操作符,可以重载,只能在



C++


中使用。





2




malloc




free


是函数,可以覆盖,



C




C++


中都可以使用。





3




new


可以调用对象的构造函数,对应的



delete


调用相应的


析构函数。





4




malloc


仅仅分配内存,



free


仅仅回收内存,并不执行构造


和析构函数





5




new




delete


返回的是某种数据类型指针,



malloc




free



回的是



void


指针。



注意:


malloc


申请的内存空间要用


free


释放,而


new


申请的内


存空间要用


delete


释放,不要混用。



因为两者实现的机理不同。



面试题


6




写一个“



标准”




MIN


#define min(a,b)((a)<=(b)?(a):(b))


注意:在调用时一定要注意这个宏定义的副作用,如下调用:



((++*p)<=(x)?(++*p):(x)




p


指针就自加了两次,违背了


MIN


的本意。



3


面试题


7




一个指针可以是


volatile




可以,因为指针和普通变量一样, 有时也有变化程序的不可控性。常


见例:子中断服务子程序修改



一个指向一个



buffer


的指针时,必须用



volatile


来修饰这个指针。



说明:

< p>
指针是一种普通的变量,


从访问上没有什么不同于其他变量的


特性。其保存的数值是个整型



数据,和整型变量不 同的是,这个整型数据指向的是一段内存地址。



面试题


8



a



&a


有什么区别



请写出以下代码的打印结果,主要目的是考察



a



&a


的区别。



#include


void main( void )


{


int a[5]={1,2,3,4,5};


int *ptr=(int *)(&a+1);


printf(


return;


}


输出结果:



2




5




注意:数组名


a


可以作数组的首地址,而


&a


是数组的指针。思考,


将原式的


int *ptr=(int *)(&a+1);


改为


int *ptr=(int *)(a+1);


时输出结果将是什么呢?



面试题


9




简述


C



C++


程序编译的内存分配情况



C



C++


中内存分配方式可以分为三种:





1


)从静态存储区域分配:



内存在程序编译时就已经分配好,


这块内存在程序的整个运行期间都


存在。速度快、不容易出错,



因为有系统会善后。例如全局变量,



static


变量等。





2


)在栈上分配:


< br>在执行函数时,


函数内局部变量的存储单元都在栈上创建,


函数执行


结束时这些存储单元自动被释


放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配


的内存容量有限 。





3


)从堆上分配:



即动态内存分配。程序在运行的时候用



malloc




new


申请任意大


小的内存,程序员 自己负责在何



时用



free




delete


释放内存。动态内存的生存期由程序员决定,< /p>


使用非常灵活。如果在堆上分配了空间,



就有责任回收它,


否则运行的程序会出现内存泄漏,


另外频繁 地分配


和释放不同大小的堆空间将会产生



堆内碎块。



一个



C




C++


程序编译时内存分为



5


大存储区:堆区、栈区、全局


区、 文字常量区、程序代码区。



4


面试题



10


:简述



strcpy




sprintf




memcpy


的区别



三者主要有以下不同之处:





1



操作对象不同,



strcpy


的两个操作对象均为字符串,



sprintf



操作源对象可以是多种数据类型,



目的操作对象是字符串,



memcpy


的两个对象就是两个任意可操作


的内存地址,并不限于何种数据类型。





2


)执行效率不同,



memcpy


最高,



strcpy


次之,



sprintf


的效


率最低。





3



实现功能不同,



strcpy


主要实现字符串变量间的拷贝,



sprintf


主要实现其他数据类型格式到字



符串的转化,



memcpy


主要是内存块间的拷贝。



说明:


strcpy



sprintf



memcpy


都可以实现拷贝的 功能,但是


针对的对象不同,根据实际需求,来



选择合适的函数实现拷贝功能。



面试题


11




设置地址为


0x67a9



整型变量的值为


0xaa66


int *ptr;


ptr = (int *)0x67a9;


*ptr = 0xaa66;


说明:


这道题就是强制类型转换的典型例子,


无论在什么平台地址长< /p>


度和整型数据的长度是一样的,



即一个 整型数据可以强制转换成地址指针类型,


只要有意义即可。




试题


12




面向对象的三大特征



面向对象的三大特征是封装性、继承性和多态性:


< p>



封装性:将客观事物抽象成类,每个类对自身的数据和方法实行


protection




private




protected




public








继承性:广义的继承有三种实现形式:实现继承(使用基类的属< /p>


性和方法而无需额外编码的能力


)


、可< /p>



视继承


(


子窗 体使用父窗体的外观和实现代码


)



接 口继承


(


仅使用属性


和方法

< p>
,


实现滞后到子类实现


)







多态性:

是将父类对象设置成为和一个或更多它的子对象相等的


技术。用子类对象给父类对象 赋值



之后,


父类对象就可以根据当前 赋值给它的子对象的特性以不同的方


式运作。



说明:


面向对象的三个特征是实现面向对象技术的关键,


每一个特征


的相关技术都非常的复杂,程



序员应该多看、多练。



面试题


13



C++


的空类有哪些成员函数






缺省构造函数。






缺省拷贝构造函数。






缺省析构函数。






缺省赋值运算符。






缺省取址运算符。






缺省取址运算符



const




注意:


有些书上只是简单的介绍了前四个函数。


没有提及后面 这两个


函数。但后面这两个函数也是



空类的默认函数。


另外需要注意的是,


只有当实际使用这些函数 的时


候,编译器才会去定义它们。



5


面试题


14




谈谈你对拷贝构造函数和赋值运算符的认识



拷贝构造函数和赋值运算符重载有以下两个不同之处:





1


)拷贝 构造函数生成新的类对象,而赋值运算符不能。





2



由于拷贝构造函数是直接构造一个新的类对象,


所以在初始化


这个对象之前不用检验源对象



是否和新建对 象相同。


而赋值运算符则需要这个操作,


另外赋值运算


中如果原来的对象中有内存分配要



先把内存释放掉



注意:


当有类中有指针类型的成员变量时,


一定要重写拷贝构造函数


和赋值运算符,不要使用默认



的。



面试题


15





C++


设计一个不能被继承的类



template class A


{


friend T;


private:


A() {}


~A() {}


};


class B : virtual public A


{


public:


B() {}


~B() {}


};


class C : virtual public B


{


public:


C() {}


~C() {}


};


void main( void )


{


B b;


//C c;


return;


}


注意:构造函数是继承实现的关键,每次子类对象构造时,首先调用

的是父类的构造函数,然后才



是自己的。



面试题


16




访问基类的私有虚函数



写出以下程序的输出结果:



#include


class A


{ 6


virtual void g()


{


cout <<


}


private:


virtual void f()


{


cout <<


}


};


class B : public A


{


void g()


{


cout <<


}


virtual void h()


{


cout <<


}


};


typedef void( *Fun )( void );


void main()


{


B b;


Fun pFun;


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


{


pFun = ( Fun )*( ( int* ) * ( int* )( &b ) + i );


pFun();


}


}


输出结果:



B::g


A::f


B::h


注意:


本题主要考察了面试者对虚函数的理解程度。


一个对虚函数不

< p>
了解的人很难正确的做出本题。



在学习面向对象 的多态性时一定要深刻理解虚函数表的工作原理。



面试题


17




简述


类成员函数的重写、重载和隐藏的区别





1


)重写和重载主要有以下几点不同。






范围的区别:


被重写的和重写的函 数在两个类中,而重载和被重


载的函数在同一个类中。






参数的区别:


被重写函数和重写函数的参 数列表一定相同,而被


重载函数和重载函数的参数列表一



定不同。






的区别:重写的基类中被重写的函数必须要有


< /p>


修饰,而重载函数和被重载函数可以被









修饰,也可以没有。




2


)隐藏和重写、重载有以下几点不同。





< br>与重载的范围不同:和重写一样,隐藏函数和被隐藏函数不在同


一个类中。






参数的区别:


隐藏函数和被隐藏的函数的参数列表可以相同,也


可不同,但是函数名肯定要相同。



当参数不相同时,无论基类中的参数是否被



virtual


修饰,基类的函


数都 是被隐藏,而不是被重写。



说明:


虽 然重载和覆盖都是实现多态的基础,


但是两者实现的技术完


全不 相同,达到的目的也是完



全不同的,


覆盖是动态态绑定的多态,


而重载是静态绑定的多态。




试题


18




简述多态实现的原理



编译器发现一个 类中有虚函数,


便会立即为此类生成虚函数表



vtable



虚函数表的各表项为指向对

< p>


应虚函数的指针。


编译器还会在此类中隐含插入 一个指针



vptr





vc


编译器来说,它插在类的第一个位



置 上)指向虚函数表。调用此类的构造函数时,在类的构造函数中,


编译器会隐含执行



vptr




vtable




关联代码,将



vptr


指向对应的



vtable


,将类与此类的



vtable


联系


了起来。另外在调 用类的构造函数时,



指向基础类的指针此时已经变成指向具体的类的



this


指针,这样依


靠此



this


指针即可得到正确的



vtable





如此才能真正与函数体进行连接,


这就是动态联编,

< p>
实现多态的基本


原理。



注意:一定要区分虚函数,纯虚函数、虚拟继承的关系和区别。牢记


虚函数实现原理,因 为多态



C++


面试的重要考点之一, 而虚函数是实现多态的基础。



面试题


19




链表和数组有什么区别



数组和链表有以下几点不同:





1


)存储 形式:数组是一块连续的空间,声明时就要确定长度。链


表是一块可不连续的动态空间,



长度可变,每个结点要保存相邻结点指针。





2


)数据 查找:数组的线性查找速度快,查找操作直接使用偏移地


址。链表需要按顺序检索结点,



效率低。





3


)数据 插入或删除:


链表可以快速插入和删除结点,而数组则可


能需要 大量数据移动。





4


)越界问题:链表不存在越界问题,数组有越界问题。



说明:


在选择数组或链表数据结构时,

一定要根据实际需要进行选择。


数组便于查询,链表便于插



入删除。


数组节省空间但是长度固定,


链表虽然变长但是占了更多的


存储空间。



面试题


20




怎样把一个单链表反序





1


)反转一个链表。循环算法。



List reverse(List n)


{


if(!n) //


判断链表是否为空,为空即退出。



{


return n;


}


list cur = //


保存头结点的下个结点



list pre = n; //


保存头结点



list tmp;


8


= null; //


头结点的指针指空,转换后变尾结点



while ( NULL != ) //


循环直到



为空



{


tmp = cur; //


实现如图


10.3


—图


10.5


所示



= pre


pre = tmp;


cur =


}


return tmp; //f


返回头指针



}




2


)反转一个链表。递归算法。



List *reverse( List *oldList, List *newHead = NULL )


{


List *next = oldList-> next; //


记录上次翻转后的链表



oldList-> next = newHead; //


将当前结点插入到翻转后链表的开头



newHead = oldList; //


递归处理剩余的链表



return ( next==NULL )? newHead: reverse( t, newHead );


}


说明:



循环算法就是图


10.2


—图


10.5


的移动过程 ,


比较好理解和


想到。递归算法的设计虽有一点难



度,但是理解了循环算法,再设计递归算法就简单多了。



面试题


21


:简述队列和栈的异同



队列和栈 都是线性存储结构,


但是两者的插入和删除数据的操作不同,


队 列是“先进先出”


,栈是



“后进先出”



注意:区别栈区和堆区。堆区的存取是“顺序随意”


,而栈区是“后


进先出”


。栈由编译器自动分



配释放



,存放函数的参数值,局部变 量的值等。其操作方式类似于


数据结构中的栈。



堆一般由程序员



分配释放,



若程序员不释放,程序结束时可能由


OS


回收。分配方


式类似于链表。



它与本题中的堆和栈是两回事。


堆栈只是一种数据结构,


而堆区和栈


区是程序的不同内存存储区域。



面试题


22




能否用两个栈实现一个队列的功能



结点结构体:



typedef struct node


{


int data;


node *next;


}node,*LinkStack;


创建空栈:



LinkStack CreateNULLStack( LinkStack &S)


{


S = (LinkStack)malloc( sizeof( node ) ); //


申请新结点



if( NULL == S)


{


printf(


9


return NULL;


} S


->data = 0; //


初始化新结点



S->next = NULL;


return S;


}


栈的插入函数:



LinkStack Push( LinkStack &S, int data)


{


if( NULL == S) //


检验栈



{


printf(


return NULL;


}


LinkStack p = NULL;


p = (LinkStack)malloc( sizeof( node ) ); //


申请新结点



if( NULL == p)


{


printf(


return S;


}


if( NULL == S->next)


{


p->next = NULL;


}


else


{


p->next = S->next;


} p


->data = data; //


初始化新结点



S->next = p; //


插入新结点



return S;


}


出栈函数:



node Pop( LinkStack &S)


{


node temp;


= 0;


= NULL;


if( NULL == S) //


检验栈



{


printf(


return temp;


}


temp = *S;


10


if( S->next == NULL )


{


printf(


return temp;


}


LinkStack p = S ->next; //


节点出栈



S->next = S->next->next;


temp = *p;


free( p );


p = NULL;


return temp;


}


双栈实现队列的入队函数:



LinkStack StackToQueuPush( LinkStack &S, int data)


{


node n;


LinkStack S1 = NULL;


CreateNULLStack( S1 ); //


创建空栈



while( NULL != S->next ) //S


出栈入


S1


{


n = Pop( S );


Push( S1, );


}


Push( S1, data ); //


新结点入栈



while( NULL != S1->next ) //S1


出栈入


S


{


n = Pop( S1 );


Push( S, );


}


return S;


}


说明:


用两个栈能够实现一个队列的功能,


那用两个队列能否实现一


个队列的功能呢?结果是否定



的,因为栈是 先进后出,将两个栈连在一起,就是先进先出。而队列


是现先进先出,无论多少个连在一



起都是先进先出,而无法实现先进后出。



面试题


23




计算一颗二叉树的深度



深度的计算函数:



int depth(BiTree T)


{


if(!T) return 0; //


判断当前结点是否为叶子结点



11


int d1= depth(T->lchild); //


求当前结点的左孩子树的深度



int d2= depth(T->rchild); //


求当前结点的右孩子树的深度



return (d1>d2?d1:d2)+1;


}


注意:根据二叉树的结构特点,很多算法都可以用递归算法来实现。



面试题


24




编码实现直接插入排序



直接插入排序编程实现如下:



#include


void main( void )


{


int ARRAY[10] = { 0, 6, 3, 2, 7, 5, 4, 9, 1, 8 };


int i,j;


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


{


cout<


}


cout<


for( i = 2; i <= 10; i++ ) //



ARRAY[2],

< p>


,ARRAY[n]


依次按序插入



{


if(ARRAY[i] < ARRAY[i-1]) //


如果


ARRAY[i]


大于一切有序的数值,



//ARRAY[i]


将保持原位不动



{


ARRAY[0] = ARRAY[i]; //



ARRAY[0]


看做是哨兵


,



ARRAY[i]


的副




j = i - 1;


do{ //


从右向左在有序区


ARRAY[1




i-1]




//


查找


ARRAY[i]


的插入位置



ARRAY[j+1] = ARRAY[j]; //


将数值大于


ARRAY[i]


记录后移



j-- ;


}while( ARRAY[0] < ARRAY[j] );


ARRAY[j+1]=ARRAY[0]; //ARRAY[i]


插入到正确的位置上



}


}


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


{


cout<


}


cout<


}


12


注意:



所有为简化边界条件而引入的附加结点(



元素)



均可称为

哨兵。引入哨兵后使得查找循环条



件的时间大约减少了一半,



对于记录 数较大的文件节约的时间就相


当可观。类似于排序这样使用频率非



常高的算法,


要尽可能地减少其运行时间。


所以不能把上述算法中的


哨兵视为雕虫小技。



面试题


25




编码实现冒泡排序



冒泡排序编程实现如下:



#include


#define LEN 10 //


数组长度



void main( void )


{


int ARRAY[10] = { 0, 6, 3, 2, 7, 5, 4, 9, 1, 8 }; //


待排序数




printf(


for( int a = 0; a < LEN; a++ ) //


打印数组内容



{


printf(


}


int i = 0;


int j = 0;


bool isChange; //


设定交换标志



for( i = 1; i < LEN; i++ )


{ //


最多做


LEN-1


趟排序



isChange = 0; //


本趟排序开始前


,


交换标志应为假



for( j = LEN-1; j >= i; j-- ) //


对当前无序区


ARRAY[i..LEN]


自下向上扫描



{


if( ARRAY[j+1] < ARRAY[j] )


{ //


交换记录



ARRAY[0] = ARRAY[j+1]; //ARRAY[0]


不是哨兵


,


仅做暂存单元



ARRAY[j+1] = ARRAY[j];


ARRAY[j] = ARRAY[0];


isChange = 1; //


发生了交换


,


故将交换标志置为真



}


}


printf(


for( a = 0; a < LEN; a++) //


打印本次排序后数组内容



{


printf(


}


if( !isChange ) //


本趟排序未发生交换


,


提前终止算法



{


break;


}


}


printf(


return;


}


13


面试题


26




编码实现直接选择排序



#include


#define LEN 9


void main( void )

战国七雄地图-


战国七雄地图-


战国七雄地图-


战国七雄地图-


战国七雄地图-


战国七雄地图-


战国七雄地图-


战国七雄地图-