c++面试题2

1.    指出以下变量数据存储位置
 
全局变量int(*g_pFun)(int);g_pFun=myFunction;g_pFun存储的位置(A ) 为全局的函数指针
 
指向空间的位置( B) 所有函数代码位于TEXT段
 
函数内部变量 static int nCount;       ( A) 静态变量总是在DATA段或BSS段中
 
函数内部变量 char p[]=”AAA”;  p 指向空间的位置( C) 局域变量的静态数组,空间在Stack中
 
函数内部变量 char *p=”AAA”;  p 指向空间的位置( E) ,”AAA”为一字符常量空间,不同编译器有不同处理方法,大部分保存在TEXT(代码段中),也有编译的rodata段中
 
函数内部变量 char *p=new char; p的位置(C ) 指向空间的位置(D ) 所有malloc空间来自于heap(堆)
 
A.    数据段
 
B.    代码段
 
C.    堆栈
 
D.    堆
 
E.    不一定, 视情况而定
 
以上知识参见C语言变量的作用域相关课件
 
2.    以下程序的输出结果为 ( )
 
#include <iostream>
 
main( )
 
{
 
using namespace std;
 
int num[5]={1,2,3,4,5};
 
cout <<*((int *)(&num+1)-1) <<endl;
 
}
 
A. 1        B.2        C. 3        D. 4        E. 5       F. 0        G. 未初始化内存,无法确定
 
在C语言中,一维数组名表示数组的首地址,而且是一个指针.如上例num,
 
对&num,表示指针的指针.意味着这里强制转换为二维数组指针.
 
这样 &num+1 等同于 num[5][1],为代码空间. (&num+1)-1表示 num[4][0].即num[4].所以这里答案是E.
 
 
 
扩展题目:
 
*((int *)(num+1)-1)   的值是多少?
 
Num是首指针,num+1是第二个元素指针,-1后又变成首指针.所以这里是答案是num[0]即,A.1
 
3.    以下哪些是程序间可靠的通讯方式( C ),哪些可以用于跨主机通讯( C,D ,F).Windows命名管道跨机器也可跨机器.
 
A. 信号         B. 管道               C. TCP          D. UDP         E. PIPE         F,.串口I/O
 
4. class a
 
{
 
public:
 
virtual  void  funa( );
 
virtual  void  funb( );
 
void  fun( );
 
static  void  fund( );
 
static  int  si;
 
private:
 
int  i;
 
char  c;
 
};
 
问: 在32位编译器默认情况下,sizeof(a)等于( )字节?
 
A. 28             B. 25      C.24          D. 20           E. 16        F.12             G. 8
 
答案在VC++下是 12. 这里需要考虑三个问题,一是虚函数表vtable的入口表地址,二是字节对齐.三 ,静态成员是所有对象共享,不计入sizeof空间.
 
在大部分C++的实现中,带有虚函数的类的前4个BYTE是虚函数vtable表的这个类入口地址.所以sizeof必须要加入这个4个byte的长度,除此外,类的sizoef()为所有数据成员总的sizeof之和,这里是int i,和char c.其中char c被字节对齐为4.这样总长度为
 
 
 
Sizeof(a) = sizeof(vtable)+size(int)+sizeof(char + pad) = 12;
 
 
 
5. 32位Windows 系统或Linux系统下
 
struct
 
{
 
char  a;
 
char  b;
 
char  c;
 
}A;
 
struct
 
{
 
short  a;
 
short  b;
 
short  c;
 
}B;
 
struct
 
{
 
short  a;
 
long  b;
 
char  c;
 
}C;
 
printf(“%d,%d,%d\n”,sizeof(A),sizeof(B),sizeof(C)); 的执行结果为: ( )
 
A. 3,6,7         B. 3,6,8         C. 4,8,12              D. 3,6,12      E. 4,6,7         F. 4,8,9
 
 
 
C语法的字节对齐规则有两种情况要字节对齐, 在VC++,gcc测试都是如此
 
1)    对同一个数据类型(short,int,long)发生了跨段分布,(在32CPU里,即一个数据类型分布在两个段中)才会发生字节对齐.
 
2)    数据类型的首部和尾部必须有其一是与4对齐.而且违反上一规则.
 
l  Sizeof(A),sizeof(B)虽然总字节数不能被4整除.但刚好所有数据平均分布在以4为单位的各个段中.所以无需字节对齐,所以结果是 3和6
 
l  struct {char a;char b;char c;char d;char e;}F; 的sizoef(F)是等于5.
 
l  用以下实例更加清楚
 
struct {
 
char a[20];
 
short b;
 
}A;
 
struct {
 
char a[21];
 
short b;
 
}B;
 
Sizeof(A)=22,sizoef(B)=24.因为前者没有发生跨段分布.后者,如果不字节对齐.a[21]占用最后一个段的首地址,b无法作到与首部与尾部与4对齐,只能在a[21]与b之间加入一个byte,使用b的尾部与4对齐.
 
l  C就是比较好理解.要补多个成12
 
 
 
6.    依据程序,以下选择中那个是对的? (  )
 
class A
 
{
 
int  m_nA;
 
};
 
class B
 
{
 
int   m_nB;
 
};
 
class C:public A,public B
 
{
 
int  m_nC;
 
};
 
void f (void)
 
{
 
C* pC=new C;
 
B* pB=dynamic_cast<B*>(pC);
 
A* pA=dynamic_cast<A*>(pC);
 
}
 
A. pC= =pB,(int)pC= =(int)B                                  B. pC= =pB,(int)pC!=(int)pB
 
C. pC!=pB,(int)pC= =(int)pB                                  D. pC!=pB,(int)pC!=(int)pB
 
 
 
这里主要考多态..将程序变为如下比较易懂
 
 
 
#include <stdio.h>
 
 
 
class A
 
{
 
public:
 
int  m_nA;
 
};
 
class B
 
{
 
public:
 
int   m_nB;
 
};
 
class C:public A,public B
 
{
 
public:
 
int  m_nC;
 
};
 
 
 
void f (void)
 
{
 
C* pC=new C;
 
B* pB=dynamic_cast<B*>(pC);
 
A* pA=dynamic_cast<A*>(pC);
 
}
 
 
 
void f1 (void)
 
{
 
C* pC=new C;
 
pC->m_nA = 1;
 
pC->m_nB = 2;
 
pC->m_nC = 3;
 
B* pB=dynamic_cast<B*>(pC);
 
A* pA=dynamic_cast<A*>(pC);
 
 
 
printf(“A=%x,B=%x,C=%x,iA=%d,iB=%d,iC=%d\n”,pA,pB,pC,(int)pA,(int)pB,(int)pC);
 
}
 
 
 
 
 
void test1();
 
 
 
int main()
 
{
 
// test1();
 
f1();
 
getchar();
 
return 0;
 
}
 
以上程序输出:
 
A=4318d0,B=4318d4,C=4318d0,iA=4397264,iB=4397268,iC=4397264
 
即C从,A,B继承下来,由下图可以知道 pA=pC.而pB强制转换后,只能取到C中B的部分.所以pB在pC向后偏移4个BYTE,(即m_nA)的空间
 
 
 
7,请写出能匹配”[10]:dddddd ”和”[9]:abcdegf ”,不匹配”[a]:xfdf ”的正则表达式________,linux下支持正则的命令有:___find,grep_________
 
8.如下程序:
 
int i=1,k=0;
 
long *pl=NULL;
 
char *pc=NULL;
 
if(k++&&i++)
 
k++, pl++, pc++;
 
if(i++||k++)
 
i++, pl++, pc++;
 
printf(“i=%d,k=%d,pl=%ld,pc=%ld\n”,i,k,(long)pl,(long)pc);
 
 
 
打印结果为__i=3,k=1,pl=4,pc=1________
 
主要测试逻辑表达式的短路操作.
 
&&操作中,前一个表达式为0,后一表达式不执行
 
||操作中, 前一个表达式为1,后一表达式不执行
 
 
 
9. 以下程序的输出为______________
 
#include<iostream>
 
using std::cout;
 
class A
 
{
 
public:
 
void f(void){
 
cout<< ”A::f” <<’ ‘;
 
}
 
virtual void g(void)
 
{
 
cout <<”A::g” << ‘ ‘;
 
}
 
};
 
 
 
class B : public A
 
{
 
public:
 
void f(void)
 
{
 
cout << “B :: f “ << ‘ ‘;
 
}
 
void g(void)
 
{
 
cout << “B:: g “ << ‘ ‘;
 
}
 
 
 
};
 
 
 
int main()
 
{
 
A*  pA =new B;
 
pA->f();
 
pA->g();
 
B* pB = (B*)pA;
 
pB->f();
 
pB->g();
 
}
 
A::f B:: g  B :: f  B:: g  

多态中虚函数调用.
 
f()为非虚函数,这样强制转换后,执行本类的同名函数.
 
G()为虚函数,指针总是执行虚函数,这就是多态..
 
10.下列代码的作用是删除list lTest 中值为6的元素:
 
list<int> :: iterator Index = ITest .begin();
 
for( ;  Index != ITest .end();  ++ Index)
 
{
 
if((*Index) = = 6)
 
{
 
ITest .erase(Index);
 
}
 
}
 
请问有什么错误____ Index = ITest .erase(Index);____________________,
 
STL的游标处理,erase已经将Index破坏掉,需要用新的Index,否则下一循环的++Index被破坏掉
 
请写出正确的代码,或者在原代码上修正.
 
 
 
11.找错误_以下程序:
 
char* ptr = malloc(100);
 
if(!ptr)
 
{
 
…
 
}
 
…
 
//ptr 指向的空间不够需要重新分配
 
ptr = realloc(ptr,200);
 
if(!ptr)
 
{
 
…
 
}
 
…
 
请问有什么错误___if(ptr ==NULL)____________________,请写出正确的代码,或者在原代码上修正.
 
 
 
12.以下为window NT 下32 位C++程序,请填写如下值
 
class myclass
 
{
 
int a ;
 
int b;
 
};
 
char *p = “hello”;
 
char str[] = “world”;
 
myclass classes[2];
 
void *p2= malloc(100);
 
 
 
sizeof(p)=_4__
 
sizeof(str)=_6_
 
sizeof(classes)=_16__
 
sizeof(p2)=_4___
 
 
 
13.直接在以下程序中的错误的行数后的填空栏中打叉
 
程序1:
 
int main(void)
 
{
 
int i=10;_____
 
int *const j=&i;_______
 
(*j)++;____
 
j++;___*_____
 
}
 
程序2:
 
int main(void)
 
{
 
int i=20;_____
 
const int *j=&i;_________
 
*j++;______
 
(*j)++;____*____
 
}
 
 
 
主要考const 出现在*前后不同含意,const 在*后表示指针本身不能改,const 在*前面指针内容不能改,程序1中j不能修改指针,所以j++是错,程序2,j不能改改内容,所以
 
 
 
14.用C/C++代码实现以下要求:从1-100中挑选出10个不同的数字,请把可能的所有组合打印出来.
 
 
 
15.有一个非常大的全局数组int a[],长度n超过2的24次方,写一个针对该数组的查找算法unsigned search(int value)(返回值下标),插入算法insert(int value,unsigned index).再次注意该数组的长度很长.
 
题目不太清,可能可以把数值本身作下标.并且按顺序排序.
 
16.有两个单向链表,表头pHeader1,pHeader2,请写一个函数判断这两个链表是否有交叉.如果有交叉,给出交叉点.程序不能改变链表的内容,可以使用额外的空间,时间复杂度尽量小,最好给出两种解.(双重循环的解由于时间复杂度高,不算正解).
 
1.移动链表指针,如果最终
 
17.编写程序,将一棵树从根节点到叶子的所有最长路径都打印出来.比如一棵树从跟到最末端的叶子最远要经
 
过4个节点,那么就把到所有要经过4个节点才能到达的叶子的搜索路径(所有途径节点)都分别打印出来.
 
 
 
18.请分别对一个链表和一个数组进行排序,并指出你排序的算法名称以及为何选择该算法
 
数组可用交换法排序
 
 
 
 
 
19.有单向链表,其中节点结构为Node{int value;Node *pNext};只知道指向某个节点的指针pCurrent;并且知道该节点不是尾节点,有什么办法把他删除吗?要求不断链.
 
从链表头开始,找到pCurrent上一个结点pPrev,然后 pPrev->pNext = pCurrent->pNext;
 
 
 
20.问题A:用什么方法避免c/c++编程中的头文件重复包含?问题B:假设解决了重复包含问题,但是又需要在两个不同的头文件中引用各申明的类,应该如何处理?具体代码如下:
 
在头文件Man.h中
 
….
 
Class Cman
 
{
 
….
 
CFace m_face;
 
};
 
….
 
在头文件Face.h中
 
…
 
Class CFace
 
{
 
…
 
Cman *m_owner;
 
};
 
….
 
这样类CMan.CFace就相互引用了,该如何处理呢?
 
 
 
1.#ifndef ….
 
#define …..
 
2.类的前向声明
 
 
 
21.多线程和单线程各自分别在什么时候效率更高?
 
多线程在并发,并且各线程无需访问共享数据情况详细最高
 
如果多线程过于频繁切换,或共享数据很多情况下,使用单线程较好
 
 
 
22.在程序设计中,对公共资源(比如缓冲区等)的操作和访问经常需要使用锁来进行保护,但在大并发系统中过多的锁会导致效率很低,通常有那些方法可以尽量避免或减少锁的使用?
 
减少锁的粒度,每次尽可能减少锁范围
 
采用队列处理,这样无需使用锁.
 
23.请详细阐述如何在release版本(windows程序或linux程序都可以)中,查找段错误问题.
 
可以用编译器生成map文件来定位源码.通过地址反查源码
 
 
 
24.假设你编译链接release版本后得到一个可执行程序(由多个cpp文件和H文件编译),结果可执行程序文件非常大,你如何找到造成文件太大的可能原因,可能的原因是什么?
 
使用一个已经初始化的巨大的全局数组
 
25.在编写C++赋值运算符时有哪些要注意的地方?
 
返回值,参数最好用引用
 
减少友元函数使用,移植有问题.
 
26.假设你是参与设计嫦娥卫星的嵌入式单板软件工程师,其中有一个快速搜索可能要用到哈希变或者平衡二叉树,要求不管什么条件下,单板必须在指定的短时间内有输出,你会采取那种算法?为什么用这种算法,为什么不用另一种算法?
 
HASH.HASH访问速度较快.
 
27.strcpy()容易引起缓冲区溢出问题,请问有什么函数可以替代以减少风险,为什么?
 
strncpy
 
28.请指出spinlock,mutex,semaphore,critical section的作用与区别,都在哪些场合使用.
 
spin_lock Linux 内核自旋锁. Mutex Windows 互质量, semaphore  POSIX ,critical section Windows
 
29.在哪些方法使阻塞模式的recv函数在没有收到数据的情况下返回(不能将socket修改为非阻塞模式)请描述得详细点.
 
使用select
 
 
 
30.有3个红色球,2个白色球,1个绿色球.取出两个不同颜色的球就能变成两个第三种颜色的球(比如:取出1红球,1白球,就能变成2个绿球).问,最少几次变化能将所有球都变成同一颜色,说明步骤和原因?
 
 
 
31.单向链表的反转是一个经常被问到的一个面试题,也是一个非常基础的问题。比如一个链表是这样的: 1->2->3->4->5 通过反转后成为5->4->3->2->1。
 
最容易想到的方法遍历一遍链表,利用一个辅助指针,存储遍历过程中当前指针指向的下一个元素,然后将当前节点元素的指针反转后,利用已经存储的指针往后面继续遍历。源代码如下:
 1.struct linka {
 2.int data;
 3.linka* next;
 4.};
 5.void reverse(linka*& head) {
 6.if(head ==NULL)
 7.                  return;
 8.linka *pre, *cur, *ne;
 9.pre=head;
 10.cur=head->next;
 11.while(cur)
 12.{
 13.   ne = cur->next;
 14.   cur->next = pre;
 15.   pre = cur;
 16.   cur = ne;
 17.}
 18.head->next = NULL;
 19.head = pre;
 20.}
 
还有一种利用递归的方法。这种方法的基本思想是在反转当前节点之前先调用递归函数反转后续节点。源代码如下。不过这个方法有一个缺点,就是在反转后的最后一个结点会形成一个环,所以必须将函数的返回的节点的next域置为NULL。因为要改变head指针,所以我用了引用。算法的源代码如下:
 1.linka* reverse(linka* p,linka*& head)
 2.{
 3.if(p == NULL || p->next == NULL)
 4.{
 5.   head=p;
 6.   return p;
 7.}
 8.else
 9.{
 10.   linka* tmp = reverse(p->next,head);
 11.   tmp->next = p;
 12.   return p;
 13.}
 14.}
 
32.已知String类定义如下:
 
class String
 {
 public:
 String(const char *str = NULL); // 通用构造函数
 String(const String &another); // 拷贝构造函数
 ~ String(); // 析构函数
 String & operater =(const String &rhs); // 赋值函数
 private:
 char *m_data; // 用于保存字符串
 };
 
尝试写出类的成员函数实现。
 
答案:
 
String::String(const char *str)
 {
 if ( str == NULL ) //strlen在参数为NULL时会抛异常才会有这步判断
 {
 m_data = new char[1] ;
 m_data[0] = ‘\0′ ;
 }
 else
 {
 m_data = new char[strlen(str) + 1];
 strcpy(m_data,str);
 }
 
}
 
String::String(const String &another)
 {
 m_data = new char[strlen(another.m_data) + 1];
 strcpy(m_data,other.m_data);
 }
 
String& String::operator =(const String &rhs)
 {
 if ( this == &rhs)
 return *this ;
 delete []m_data; //删除原来的数据,新开一块内存
 m_data = new char[strlen(rhs.m_data) + 1];
 strcpy(m_data,rhs.m_data);
 return *this ;
 }
 
String::~String()
 {
 delete []m_data ;
 }
 


33.求下面函数的返回值(微软)
 
int func(x)
 {
 int countx = 0;
 while(x)
 {
 countx ++;
 x = x&(x-1);
 }
 return countx;
 }
 
假定x = 9999。 答案:8
 
思路:将x转化为2进制,看含有的1的个数。
 
34. 什么是“引用”?申明和使用“引用”要注意哪些问题?
 
答:引用就是某个目标变量的“别名”(alias),对应用的操作与对变量直接操作效果完全相同。申明一个引用的时候,切记要对其进行初始化。引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,不能再把该引用名作为其他变量名的别名。声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。不能建立数组的引用。
 
45. 将“引用”作为函数参数有哪些特点?
 
(1)传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。
 
(2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。
 
(3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用”*指针变量名”的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。
 
36. 在什么时候需要使用“常引用”?
 
如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。常引用声明方式:const 类型标识符 &引用名=目标变量名;
 
例1
 
int a ;
 const int &ra=a;
 ra=1; //错误
 a=1; //正确
 
例2
 
string foo( );
 void bar(string & s);
 
那么下面的表达式将是非法的:
 
bar(foo( ));
 bar(“hello world”);
 
原因在于foo( )和”hello world”串都会产生一个临时对象,而在C++中,这些临时对象都是const类型的。因此上面的表达式就是试图将一个const类型的对象转换为非const类型,这是非法的。
 
引用型参数应该在能被定义为const的情况下,尽量定义为const 。
 
37. 将“引用”作为函数返回值类型的格式、好处和需要遵守的规则?
 
格式:类型标识符 &函数名(形参列表及类型说明){ //函数体 }
 
好处:在内存中不产生被返回值的副本;(注意:正是因为这点原因,所以返回一个局部变量的引用是不可取的。因为随着该局部变量生存期的结束,相应的引用也会失效,产生runtime error!
 
注意事项:
 
(1)不能返回局部变量的引用。这条可以参照Effective C++[1]的Item 31。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了”无所指”的引用,程序会进入未知状态。
 
(2)不能返回函数内部new分配的内存的引用。这条可以参照Effective C++[1]的Item 31。虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其它尴尬局面。例如,被函数返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。
 
(3)可以返回类成员的引用,但最好是const。这条原则可以参照Effective C++[1]的Item 30。主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值