50个C语言C++常见面试题及答案资料下载.pdf
《50个C语言C++常见面试题及答案资料下载.pdf》由会员分享,可在线阅读,更多相关《50个C语言C++常见面试题及答案资料下载.pdf(26页珍藏版)》请在冰点文库上搜索。
(3)new可以调用对象的构造函数,对应的delete调用相应的析构函数。
(4)malloc仅仅分配内存,free仅仅回收内存,并不执行构造和析构函数(5)new、delete返回的是某种数据类型指针,malloc、free返回的是void指针。
malloc申请的内存空间要用free释放,而new申请的内存空间要用delete释放,不要混用。
因为两者实现的机理不同。
面试题6:
写一个“标准”宏MIN#definemin(a,b)(a)=(b)?
(a):
(b)注意:
在调用时一定要注意这个宏定义的副作用,如下调用:
(+*p)=(x)?
(+*p):
(x)。
p指针就自加了两次,违背了MIN的本意。
3面试题7:
一个指针可以是volatile吗可以,因为指针和普通变量一样,有时也有变化程序的不可控性。
常见例:
子中断服务子程序修改一个指向一个buffer的指针时,必须用volatile来修饰这个指针。
指针是一种普通的变量,从访问上没有什么不同于其他变量的特性。
其保存的数值是个整型数据,和整型变量不同的是,这个整型数据指向的是一段内存地址。
面试题8:
a和&
a有什么区别请写出以下代码的打印结果,主要目的是考察a和&
a的区别。
#includevoidmain(void)inta5=1,2,3,4,5;
int*ptr=(int*)(&
a+1);
printf(%d,%d,*(a+1),*(ptr-1);
return;
输出结果:
2,5。
数组名a可以作数组的首地址,而&
a是数组的指针。
思考,将原式的int*ptr=(int*)(&
改为int*ptr=(int*)(a+1);
时输出结果将是什么呢?
面试题9:
简述C、C+程序编译的内存分配情况C、C+中内存分配方式可以分为三种:
(1)从静态存储区域分配:
内存在程序编译时就已经分配好,这块内存在程序的整个运行期间都存在。
速度快、不容易出错,因为有系统会善后。
例如全局变量,static变量等。
(2)在栈上分配:
在执行函数时,函数内局部变量的存储单元都在栈上创建,函数执行结束时这些存储单元自动被释放。
栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
(3)从堆上分配:
即动态内存分配。
程序在运行的时候用malloc或new申请任意大小的内存,程序员自己负责在何时用free或delete释放内存。
动态内存的生存期由程序员决定,使用非常灵活。
如果在堆上分配了空间,就有责任回收它,否则运行的程序会出现内存泄漏,另外频繁地分配和释放不同大小的堆空间将会产生堆内碎块。
一个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的整型变量的值为0xaa66int*ptr;
ptr=(int*)0x67a9;
*ptr=0xaa66;
这道题就是强制类型转换的典型例子,无论在什么平台地址长度和整型数据的长度是一样的,即一个整型数据可以强制转换成地址指针类型,只要有意义即可。
面试题12:
面向对象的三大特征面向对象的三大特征是封装性、继承性和多态性:
封装性:
将客观事物抽象成类,每个类对自身的数据和方法实行protection(private,protected,public)。
继承性:
广义的继承有三种实现形式:
实现继承(使用基类的属性和方法而无需额外编码的能力)、可视继承(子窗体使用父窗体的外观和实现代码)、接口继承(仅使用属性和方法,实现滞后到子类实现)。
多态性:
是将父类对象设置成为和一个或更多它的子对象相等的技术。
用子类对象给父类对象赋值之后,父类对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。
面向对象的三个特征是实现面向对象技术的关键,每一个特征的相关技术都非常的复杂,程序员应该多看、多练。
面试题13:
C+的空类有哪些成员函数缺省构造函数。
缺省拷贝构造函数。
缺省析构函数。
缺省赋值运算符。
缺省取址运算符。
缺省取址运算符const。
有些书上只是简单的介绍了前四个函数。
没有提及后面这两个函数。
但后面这两个函数也是空类的默认函数。
另外需要注意的是,只有当实际使用这些函数的时候,编译器才会去定义它们。
5面试题14:
谈谈你对拷贝构造函数和赋值运算符的认识拷贝构造函数和赋值运算符重载有以下两个不同之处:
(1)拷贝构造函数生成新的类对象,而赋值运算符不能。
(2)由于拷贝构造函数是直接构造一个新的类对象,所以在初始化这个对象之前不用检验源对象是否和新建对象相同。
而赋值运算符则需要这个操作,另外赋值运算中如果原来的对象中有内存分配要先把内存释放掉注意:
当有类中有指针类型的成员变量时,一定要重写拷贝构造函数和赋值运算符,不要使用默认的。
面试题15:
用C+设计一个不能被继承的类templateclassAfriendT;
private:
A()A();
classB:
virtualpublicApublic:
B()B();
classC:
virtualpublicBpublic:
C()C();
voidmain(void)Bb;
/Cc;
构造函数是继承实现的关键,每次子类对象构造时,首先调用的是父类的构造函数,然后才是自己的。
面试题16:
访问基类的私有虚函数写出以下程序的输出结果:
#includeclassA6virtualvoidg()coutA:
gendl;
virtualvoidf()coutA:
fendl;
publicAvoidg()coutB:
virtualvoidh()coutB:
hendl;
typedefvoid(*Fun)(void);
voidmain()Bb;
FunpFun;
for(inti=0;
inext;
/记录上次翻转后的链表oldList-next=newHead;
/将当前结点插入到翻转后链表的开头newHead=oldList;
/递归处理剩余的链表return(next=NULL)?
newHead:
reverse(t,newHead);
循环算法就是图10.2图10.5的移动过程,比较好理解和想到。
递归算法的设计虽有一点难度,但是理解了循环算法,再设计递归算法就简单多了。
面试题21:
简述队列和栈的异同队列和栈都是线性存储结构,但是两者的插入和删除数据的操作不同,队列是“先进先出”,栈是“后进先出”。
区别栈区和堆区。
堆区的存取是“顺序随意”,而栈区是“后进先出”。
栈由编译器自动分配释放,存放函数的参数值,局部变量的值等。
其操作方式类似于数据结构中的栈。
堆一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。
分配方式类似于链表。
它与本题中的堆和栈是两回事。
堆栈只是一种数据结构,而堆区和栈区是程序的不同内存存储区域。
面试题22:
能否用两个栈实现一个队列的功能结点结构体:
typedefstructnodeintdata;
node*next;
node,*LinkStack;
创建空栈:
LinkStackCreateNULLStack(LinkStack&
S)S=(LinkStack)malloc(sizeof(node);
/申请新结点if(NULL=S)printf(Failtomallocanewnode.n);
9returnNULL;
S-data=0;
/初始化新结点S-next=NULL;
returnS;
栈的插入函数:
LinkStackPush(LinkStack&
S,intdata)if(NULL=S)/检验栈printf(Therenonodeinstack!
);
returnNULL;
LinkStackp=NULL;
p=(LinkStack)malloc(sizeof(node);
/申请新结点if(NULL=p)printf(Failtomallocanewnode.n);
if(NULL=S-next)p-next=NULL;
elsep-next=S-next;
p-data=data;
/初始化新结点S-next=p;
/插入新结点returnS;
出栈函数:
nodePop(LinkStack&
S)nodetemp;
temp.data=0;
temp.next=NULL;
if(NULL=S)/检验栈printf(Therenonodeinstack!
returntemp;
temp=*S;
10if(S-next=NULL)printf(ThestackisNULL,cantpop!
n);
LinkStackp=S-next;
/节点出栈S-next=S-next-next;
temp=*p;
free(p);
p=NULL;
双栈实现队列的入队函数:
LinkStackStackToQueuPush(LinkStack&
S,intdata)noden;
LinkStackS1=NULL;
CreateNULLStack(S1);
/创建空栈while(NULL!
=S-next)/S出栈入S1n=Pop(S);
Push(S1,n.data);
Push(S1,data);
/新结点入栈while(NULL!
=S1-next)/S1出栈入Sn=Pop(S1);
Push(S,n.data);
用两个栈能够实现一个队列的功能,那用两个队列能否实现一个队列的功能呢?
结果是否定的,因为栈是先进后出,将两个栈连在一起,就是先进先出。
而队列是现先进先出,无论多少个连在一起都是先进先出,而无法实现先进后出。
面试题23:
计算一颗二叉树的深度深度的计算函数:
intdepth(BiTreeT)if(!
T)return0;
/判断当前结点是否为叶子结点11intd1=depth(T-lchild);
/求当前结点的左孩子树的深度intd2=depth(T-rchild);
/求当前结点的右孩子树的深度return(d1d2?
d1:
d2)+1;
根据二叉树的结构特点,很多算法都可以用递归算法来实现。
面试题24:
编码实现直接插入排序直接插入排序编程实现如下:
#includevoidmain(void)intARRAY10=0,6,3,2,7,5,4,9,1,8;
inti,j;
for(i=0;
i10;
i+)coutARRAYi;
coutendl;
for(i=2;
i=10;
i+)/将ARRAY2,ARRAYn依次按序插入if(ARRAYiARRAYi-1)/如果ARRAYi大于一切有序的数值,/ARRAYi将保持原位不动ARRAY0=ARRAYi;
/将ARRAY0看做是哨兵,是ARRAYi的副本j=i-1;
do/从右向左在有序区ARRAY1i-1中/查找ARRAYi的插入位置ARRAYj+1=ARRAYj;
/将数值大于ARRAYi记录后移j-;
while(ARRAY0ARRAYj);
ARRAYj+1=ARRAY0;
/ARRAYi插入到正确的位置上for(i=0;
12注意:
所有为简化边界条件而引入的附加结点(元素)均可称为哨兵。
引入哨兵后使得查找循环条件的时间大约减少了一半,对于记录数较大的文件节约的时间就相当可观。
类似于排序这样使用频率非常高的算法,要尽可能地减少其运行时间。
所以不能把上述算法中的哨兵视为雕虫小技。
面试题25:
编码实现冒泡排序冒泡排序编程实现如下:
#include#defineLEN10/数组长度voidmain(void)intARRAY10=0,6,3,2,7,5,4,9,1,8;
/待排序数组printf(n);
for(inta=0;
aLEN;
a+)/打印数组内容printf(%d,ARRAYa);
inti=0;
intj=0;
boolisChange;
/设定交换标志for(i=1;
i=i;
j-)/对当前无序区ARRAYi.LEN自下向上扫描if(ARRAYj+1ARRAYj)/交换记录ARRAY0=ARRAYj+1;
/ARRAY0不是哨兵,仅做暂存单元ARRAYj+1=ARRAYj;
ARRAYj=ARRAY0;
isChange=1;
/发生了交换,故将交换标志置为真printf(n);
for(a=0;
a+)/打印本次排序后数组内容printf(%d,ARRAYa);
if(!
isChange)/本趟排序未发生交换,提前终止算法break;
printf(n);
13面试题26:
编码实现直接选择排序#includestdio.h#defineLEN9voidmain(void)intARRAYLEN=5,6,8,2,4,1,9,3,7;
/待序数组printf(Beforesorted:
for(intm=0;
mLEN;
m+)/打印排序前数组printf(%d,ARRAYm);
for(inti=1;
i=LEN-1;
i+)/选择排序intt=i-1;
inttemp=0;
for(intj=i;
jLEN;
j+)if(ARRAYjARRAYt)t=j;
if(t!
=(i-1)temp=ARRAYi-1;
ARRAYi-1=ARRAYt;
ARRAYt=temp;
printf(Aftersorted:
iLEN;
i+)/打印排序后数组printf(%d,ARRAYi);
在直接选择排序中,具有相同关键码的对象可能会颠倒次序,因而直接选择排序算法是一种不稳定的排序方法。
在本例中只是例举了简单的整形数组排序,肯定不会有什么问题。
但是在复杂的数据元素序列组合中,只是根据单一的某一个关键值排序,直接选择排序则不保证其稳定性,这是直接选择排序的一个弱点。
面试题27:
编程实现堆排序堆排序编程实现:
#include14voidcreateHeep(intARRAY,intsPoint,intLen)/生成大根堆while(2*sPoint+1)Len)intmPoint=2*sPoint+1;
if(2*sPoint+2)Len)if(ARRAY2*sPoint+1ARRAY2*sPoint+2)mPoint=2*sPoint+2;
if(ARRAYsPoint=0;
i-)/将Hr0,Lenght-1建成大根堆createHeep(ARRAY,i,Len);
for(i=Len-1;
i0;
i-)inttmpData=ARRAY0;
/与最后一个记录交换ARRAY0=ARRAYi;
ARRAYi=tmpData;
createHeep(ARRAY,0,i);
/将H.r0.i重新调整为大根堆return;
intmain(void)15intARRAY=5,4,7,3,9,1,6,8,2;
printf(Beforesorted:
/打印排序前数组内容for(inti=0;
i9;
i+)printf(%d,ARRAYi);
heepSort(ARRAY,9);
/堆排序printf(Aftersorted:
/打印排序后数组内容for(i=0;
return0;
堆排序,虽然实现复杂,但是非常的实用。
另外读者可是自己设计实现小堆排序的算法。
虽然和大堆排序的实现过程相似,但是却可以加深对堆排序的记忆和理解。
面试题28:
编程实现基数排序#include#include#defineLEN8typedefstructnode/队列结点intdata;
structnode*next;
node,*QueueNode;
typedefstructQueue/队列QueueNodefront;
QueueNoderear;
Queue,*QueueLink;
QueueLinkCreateNullQueue(QueueLink&
Q)/创建空队列Q=NULL;
Q=(QueueLink)malloc(sizeof(Queue);
if(NULL=Q)printf(Failtomallocnullqueue!
16Q-front=(QueueNode)malloc(sizeof(node);
Q-rear=(QueueNode)malloc(sizeof(node);
if(NULL=Q-front|NULL=Q-rear)printf(Failtomallocanewqueuesforntorrear!
Q-rear=NULL;
Q-front-next=Q-rear;
returnQ;
intlenData(nodedata,intlen)/计算队列中各结点的数据的最大位数intm=0;
intd;
i0)d/=10;
temp+;
if(tempm)m=temp;
temp=0;
returnm;
QueueLinkPush(QueueLink&
Q,nodenode)/将数据压入队列QueueNodep1,p;
p=(QueueNode)malloc(sizeof(node);
if(NULL=p)printf(Failtomallocanewnode!
p1=Q-front;
while(p1-next!
=NULL)p1=p1-next;
p-data=node.data;
p1-next=p;
p-next=Q-rear;
17returnNULL;
nodePop(QueueLink&
Q)/数据出队列nodetemp;
QueueNodep;
p=Q-front-next;
if(p!
=Q-rear)temp=*p;
Q-front-next=p-next;
intIsEmpty(QueueLinkQ)if(Q-front-next=Q-rear)return0;
return1;
intmain(void)inti=0;
intMax=0;
/记录结点中数据的最大位数intd=10;
intpower=1;
intk=0;
nodeArrayLEN=450,NULL,32,NULL,781,NULL,57,NULL,组145,NULL,613,NULL,401,NULL,594,NULL;
/队列结点数QueueLinkQueue10;
i+)CreateNullQueue(Queuei);
/初始化队列数组for(i=0;
i+)printf(%d,Arrayi.data);
Max=lenData(Array,LEN);
/计算数组中关键字的最大位数printf(%dn,Max);
18for(intj=0;
jMax;
j+)/按位排序if(j=0)power=1;
elsepower=power*d;
i+)k=Arrayi.data/power-(Arrayi.data/(power*d)*d;
Push(Queuek,Arrayi);
for(intl=0,k=0;
ld;
l+)/排序后出队列重入数组while(IsEmpty(Queuel)Arrayk+=Pop(Queuel);
for(intt=0;
tnext=NULL;
树结点入栈函数:
voidpush_path(pPathH,pBTreeT)pPathp=H-next;
pPathq=H;
while(NULL!
=p)20q=p;
p=p-next;
p=(pPath)malloc(sizeof(PATH);
/申请新结点p-next=NULL;
/初始化新结点p-tree=T;
q-next=p;
/