【技术试题】C++开发工程师面试题库 200~250道

199 MFC中SendMessage和PostMessage的区别?

答:PostMessage 和SendMessage的区别主要在于是否等待应用程序做出消息处理。PostMessage只是把消息放入队列,然后继续执行;而SendMessage必须等待应用程序处理消息后才返回继续执行。这两个函数的返回值也不同,PostMessage的返回值表示PostMessage函数执行是否正确,而SendMessage的返回值表示其他程序处理消息后的返回值。

202.改错
#include
#include
class CBuffer
{
char * m_pBuffer;
int m_size;
public:
CBuffer()
{
m_pBuffer=NULL;
}
~CBuffer()
{
Free();
}
void Allocte(int size) (3) {
m_size=size;
m_pBuffer= new char[size];
}
private:
void Free()
{
if(m_pBuffer!=NULL)
{
delete m_pBuffer;
m_pBuffer=NULL;
}
}
public:
void SaveString(const char* pText) const
{
strcpy(m_pBuffer, pText);
}
char* GetBuffer() const
{
return m_pBuffer;
}
};

void main (int argc, char* argv[])
{
CBuffer buffer1;
buffer1.SaveString(“Microsoft”);
printf(buffer1.GetBuffer());
}

答:改正后
主要改正SaveString函数

void SaveString(const char* pText) const
{
strcpy(m_pBuffer, pText);
}
改为
void SaveString(const char* pText) (1)
{
Allocte(strlen(pText)+1); (2)
strcpy(m_pBuffer, pText);
}
原因:
(1) const成员函数表示不会修改数据成员,而SaveString做不到,去掉const声明
(2) m_pBuffer指向NULL,必须用Allocte分配空间才能赋值。
(3) 另外需要将Allocte成员函数声明为私有成员函数更符合实际

203.下来程序想打印“Welcome MSR Asia”,改正错误
char * GetName (void)
{
//To return “MSR Asia” String
char name[]=”MSR Asia”;
return name;
}
void main(int argc, char* argv[])
{
char name[32];
//Fill in zeros into name
for(int i=0;i {
name[i]=’\0′;
}
//copy “Welcome” to name
name=”Welcome”;
//Append a blank char
name[8]=” “;
//Append string to name
strcat(name,GetName());
//print out
printf(name);
}

答:改正后为
char * GetName (void)
{
//To return “MSR Asia” String
//char name[]=”MSR Asia”; (1)
char *name=(char *)malloc(strlen(“MSR Asia”)+1);
strcpy(name,”MSR Asia”);
return name;
}
void main(int argc, char* argv[])
{
char name[32];
//Fill in zeros into name
for(int i=0;iFuncA(); (3)
pa->FuncB(); (4)
pa2->FuncA(); (5)
pa2->FuncB();
delete pa2;
}

206.In the main() function, after ModifyString(text) is called, what’s the value of ‘text’?

int FindSubString(char* pch)
{
int count=0;
char* p1=pch;
while(*p1!=’\0′)
{
if(*p1==p1[1]-1)
{
p1++;
count++;
}
else
{
break;
}
}
int count2=count;
while(*p1!=’\0′)
{
if(*p1==p1[1]+1)
{
p1++;
count2–;
}
else
{
break;
}
}
if(count2==0)
return count;
return 0;
}

void ModifyString(char* pText)
{
char* p1=pText;
char* p2=p1;
while(*p1!=’\0′)
{
int count=FindSubString(p1);
if(count>0)
{
*p2++=*p1;
sprintf(p2, “%I”, count);
while(*p2!= ‘\0′)
{
p2++;
}
p1+=count+count+1;
}
else
{
*p2++=*p1++;
}
}
}
void main(void)
{
char text[32]=”XYBCDCBABABA”;
ModifyString(text);
printf(text);
}

答:我不知道这个结构混乱的程序到底想考察什么,只能将最后运行结果写出来是XYBCDCBAIBAAP

207. Programming (Mandatory)
Linked list
a. Implement a linked list for integers,which supports the insertafter (insert a node after a specified node) and removeafter (remove the node after a specified node) methods;
b. Implement a method to sort the linked list to descending order.
答:题目的意思是实现一个整型链表,支持插入,删除操作(有特殊要求,都是在指定节点后进行操作),并写一个对链表数据进行降序排序的方法。
那我们不妨以一个线性链表进行编程。
// 单链表结构体为
typedef struct LNode
{
int data;
struct LNode *next;
}LNode, *pLinkList;

// 单链表类
class LinkList
{
private:
pLinkList m_pList;
int m_listLength;
public:
LinkList();
~LinkList();
bool InsertAfter(int afternode, int data);//插入
bool RemoveAfter(int removenode);//删除
void sort();//排序
};

实现方法
//insert a node after a specified node
bool LinkList::InsertAfter(int afternode, int data)
{
LNode *pTemp = m_pList;
int curPos = -1;
if (afternode > m_listLength ) // 插入点超过总长度
{
return false;
}
while (pTemp != NULL) // 找到指定的节点
{
curPos++;
if (curPos == afternode)
break;
pTemp = pTemp->next;
}
if (curPos != afternode) // 节点未寻到,错误退出
{
return false;
}
LNode *newNode = new LNode; // 将新节点插入指定节点后
newNode->data = data;
newNode->next = pTemp->next;
pTemp->next = newNode;
m_listLength++;
return true;
}

//remove the node after a specified node
bool LinkList::RemoveAfter(int removenode)
{
LNode *pTemp = m_pList;
int curPos=-1;
if (removenode > m_listLength) // 删除点超过总长度
{
return false;
}

// 找到指定的节点后一个节点,因为删除的是后一个节点
while (pTemp != NULL)
{
curPos++;
if (curPos == removenode+1)
break;
pTemp = pTemp->next;
}
if (curPos != removenode) // 节点未寻到,错误退出
{
return false;
}
LNode *pDel = NULL; // 删除节点
pDel = pTemp->next;
pTemp->next = pDel->next;
delete pDel;
m_listLength–;
return true;
}

//sort the linked list to descending order.
void LinkList::sort()
{
if (m_listLength {
return;
}
LNode *pTemp = m_pList;
int temp;
// 选择法排序
for(int i=0;i for(int j=i+1;j if (pTemp[i].data {
temp=pTemp[i].data;
pTemp[i].data=pTemp[j].data;
pTemp[j].data=temp;
}
}
前两个函数实现了要求a,后一个函数sort()实现了要求b

208. Debugging (Mandatory)
a. For each of the following recursive methods, enter Y in the answer box if the method terminaters (assume i=5), Otherwise enter N.
(题目意思:判断下面的递归函数是否可以结束)
static int f(int i){
return f(i-1)*f(i-1);
}

Ansewr: N,明显没有返回条件语句,无限递归了

static int f(int i){
if(i==0){return 1;}
else {return f(i-1)*f(i-1);}
}

Ansewr:Y,当i=0时可结束递归

static int f(int i){
if(i==0){return 1;}
else {return f(i-1)*f(i-2);}
}

Ansewr:N,因为i=1时,f(i-2)=f(-1),进入一个无限递归中

209.编程
将整数转换成字符串:void itoa(int,char);
例如itoa(-123,s[])则s=“-123”;

答:
char* itoa(int value, char* string)
{
char tmp[33];
char* tp = tmp;
int i;
unsigned v;
char* sp;
// 将值转为正值
if (value < 0)
v = -value;
else
v = (unsigned)value;
// 将数转换为字符放在数组tmp中
while (v)
{
i = v % 10;
v = v / 10;
*tp++ = i+’0′;
}
// 将tmp里的字符填入string指针里,并加上负号(如果有)
sp = string;
if (value < 0) *sp++ = ‘-’; while (tp > tmp)
*sp++ = *–tp;
*sp = 0;
return string;
}

210.完成下列程序
*
*.*.
*..*..*..
*…*…*…*…
*….*….*….*….*….
*…..*…..*…..*…..*…..*…..
*……*……*……*……*……*……*……
*…….*…….*…….*…….*…….*…….*…….*…….
#include
#define N 8
int main()
{
int i;
int j;
int k;
———————————————————
│ │
│ │
│ │
———————————————————
return 0;
}

答:#define N 8
int main()
{
int i;
int j;
int k;

for(i=0;i{
for(j=0;j {
printf(“*”);
for(k=0;kvalue=N;
NewNode->left=NULL; //新增
NewNode->right=NULL; //新增
if(root==NULL)
{
root=NewNode;
return;
}
else
{
TNode* temp;
temp=root;
while((N>=temp->value && temp->left!=NULL)||(Nvalue && temp-

>right!=NULL))
{
while(N>=temp->value && temp->left!=NULL)
temp=temp->left;
while(Nvalue && temp->right!=NULL)
temp=temp->right;
}
if(N>=temp->value)
temp->left=NewNode;
else
temp->right=NewNode;
return;
}
}

212.打印如下图案,共19行,只能有一个for循环(题目已经提供)
* 
*** 
***** 
******* 
********* 
***********
************* 
*************** 
 ***************** 
*******************
 ***************** 
*************** 
************* 
*********** 
********* 
******* 
***** 
*** 
* 
for(i=0;i {

}

答:
#include “stdio.h”
void main()
{
for(int i=0;i {
int j=0;
while (j {
if (i {
if (j=9)
printf(“*”);
else
printf(” “);
}
else
if (j-i printf(“*”);
else
printf(” “);
}
else
{
if (j {
if (i-j printf(“*”);
else
printf(” “);
}
else
if (j+i printf(“*”);
else
printf(” “);
}
j++;
}
printf(“\n”);
}
}

213.stack data (栈)存在于
A.rom, B .flash C .eeprom D.ram E .none of the above

答:D.ram。这题稍微涉及到一点硬件知识,ROM的全称是Read Only Memory,即只读存储器,flash ,eeprom都是ROM家族的一员,RAM是Random Access Memory的简称,意为随机存取存储器,也就是内存了。不管是堆还是栈都是放在内存里的。

214.
int i;
int x=0×12345678;
unsigned char *p=(unsigned char *)&x;
for(i=0;i printf(“%2x”,*(p+i));
在80x86pc机器上运行结果?

答:x在PC机上的内存存放顺序为78 56 34 12,高字节在前,低字节在后,因此输出78563412
Sun Sparc Unix上运行结果?

215.
char a[2][2][3]={{{1,6,3},{5,4,15}},{{3,5,33},{23,12,7}} };
for(int i=0;inext;
while (p->next!=link) // 链表结尾
{
if (p->key==key) // 查找到key值相同,删除该节点,并返回
{
p->prev->next=p->next;
p->next->prev=p->prev;
free(p);
return link;
}
else
p=p->next; // 否则查找下一节点
}
if (p->next == link) return NULL; //没找到,返回NULL

}

217、请用标准C语言实现下列标准库函数,设计中不得使用其他库函数。
char *strstr(char *str1,char *str2);
在字符串str1中,寻找字串str2,若找到返回找到的位置,否则返回NULL。

答:
函数为
char * strstr ( const char * str1, const char * str2 )
{
char *cp = (char *) str1;
char *s1, *s2;
if ( !*str2 )
return((char *)str1);
while (*cp)
{
s1 = cp;
s2 = (char *) str2;
while ( *s1 && *s2 && !(*s1-*s2) )
s1++, s2++;
if (!*s2)
return(cp);
cp++;
}
return(NULL);
}

218.实现双向链表删除一个节点P,在节点P后插入一个节点,写出这两个函数;

答:
假设线性表的双向链表存储结构
typedef struct DulNode{
struct DulNode *prior; //前驱指针
ElemType data; //数据
struct DulNode *next; //后继指针
}DulNode,*DuLinkList;
删除操作
Status ListDelete_DuL(DuLinkList &L,int i,ElemType &e)
{
if(!(p=GetElemP_DuL(L,i)))
return ERROR;
e=p->data;
p->prior->next=p->next;
p->next->prior=p->pror;
free(p);
return OK;
}
插入操作
Status ListInsert_DuL(DuLinkList &L,int i,ElemType &e)
{
if(!(p=GetElemP_DuL(L,i)))
return ERROR;
if(!(s=(DuLinkList)malloc(sizeof(DuLNode))))
return ERROR;

s->data=e;
s->prior=p->prior;
p->prior->next=s;
s->next=p;
p->prior=s;
return OK;
}

219.写一个函数,将其中的\t都转换成4个空格。

答:
该函数命名为convert,参数的意义为:
*strDest目的字符串,*strSrc源字符串,length源字符串的长度
函数实现为:
char* convert(char *strDest, const char *strSrc,int length)
{
char * cp = strDest;
int i=0;
while(*strSrc && i
{
if (*strSrc==’\t’) //将\t转换成4个空格
{
for(int j=0;jhigh) return -1;
mid=(low+high)/2;
if(x==a[mid]) return mid;
if(x else return(BSearch(a,x,mid+1,high));
}

2、非递归方法实现:
int BSearch(elemtype a[],keytype key,int n)
{
int low,high,mid;
low=0;high=n-1;
while(low {
mid=(low+high)/2;
if(a[mid].key==key) return mid;
else if(a[mid].key else high=mid-1;
}
return -1;
}

242,写出下面代码段的输出结果,并说明理由:

char str1[] = “abc”;
char str2[] = “abc”;

const char str3[] = “abc”;
const char str4[] = “abc”;

const char *str5 = “abc”;
const char *str6 = “abc”;

char *str7 = “abc”;
char *str8 = “abc”;

cout << ( str1 == str2 ) << endl;
cout << ( str3 == str4 ) << endl;
cout << ( str5 == str6 ) << endl;
cout << ( str7 == str8 ) << endl;

1, str1,str2,str3,str4是数组变量,它们有各自的内存空间;
而str5,str6,str7,str8是指针,它们指向相同的常量区域。

243. 以下代码中的两个sizeof用法有问题吗?
void UpperCase( char str[] )

{
for( size_t i=0; i if( ‘a’ str[i] -= (‘a’-‘A’ );
}
char str[] = “aBcDe”;
cout << “str字符长度为: ” << sizeof(str)/sizeof(str[0]) << endl;
UpperCase( str );
cout << str << endl;

函数内的sizeof有问题。根据语法,sizeof如用于数组,只能测出静态数组的大小,无法检测动态分配的或外部数组大小。函数外的str是一个静态定义的数组,因此其大小为6,函数内的str实际只是一个指向字符串的指针,没有任何额外的与数组相关的信息,因此sizeof作用于上只将其当指针看,

244,下面程序输出结果是多少:

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
原因如下:
&a是数组指针,其类型为 int (*)[5];
而指针加1要根据指针类型加上一定的值,
不同类型的指针+1之后增加的大小不同
a是长度为5的int数组指针,所以要加 5*sizeof(int)
所以ptr实际是a[5]
但是prt与(&a+1)类型是不一样的(这点很重要)
所以prt-1只会减去sizeof(int*)
a,&a的地址是一样的,但意思不一样,a是数组首地址,也就是a[0]的地址,&a是对象(数组)首地址,a+1是数组下一元素的地址,即a[1],&a+1是下一个对象的地址,即a[5].

245,请问运行Test函数会有什么样的结果?
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, “hello world”);
printf(str);
}

请问运行Test函数会有什么样的结果?
char *GetMemory(void)
{
char p[] = “hello world”;
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}

请问运行Test函数会有什么样的结果?
Void GetMemory2(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, “hello”);
printf(str);
}

请问运行Test函数会有什么样的结果?
void Test(void)
{
char *str = (char *) malloc(100);
strcpy(str, “hello”);
free(str);
if(str != NULL)
{
strcpy(str, “world”);
printf(str);
}
}

【技术试题】C++开发工程师面试题库 150~200道

151.简述需求分析的过程和意义

 

152.网状、层次数据模型与关系数据模型的最大的区别是什末

 

153.软件质量保证体系是什末 国家标准中与质量保证管理相关的几个标准是什末 编号和全称是什末号和全称是什末

 

153文件格式系统有哪几种类型?分别说说win95、win98、winMe、w2k、winNT、winXP分别支持那些文件系统

 

154.我现在有个程序,发现在WIN98上运行得很慢,怎么判别是程序存在问题还是软硬件系统存在问题?

 

155.有关P2P点对点文件传输的原理

 

156.一台计算机的IP是192.168.10.71子网掩码255.255.255.64与192.168.10.201是同一局域网吗?

 

157.internet中e-mail协仪,IE的协仪,NAT是什么,有什么好处,能带来什么问题?DNS是什么,它是如何工作的?

 

158.PROXY是如何工作的?

 

169.win2k系统内AT命令完成什么功能,Messenger服务是做什么,怎么使用?

 

170进程,线程的定义及区别

 

171,32位操作系统内,1进程地址空间多大,进程空间与物理内存有什么关系?

 

172.网络攻击常用的手段,防火墙如何保证安全.

 

173.如何配静态IP,如何测网络内2台计算机通不通,PING一次返几个数据包?

 

174.WIN9X与WINNT以上操作系统有”服务”吗,服务是什么,如何停止服务?

 

175.AD在WIN2KSERVER上建需什么文件格式,AD是什么?XP多用户下”注销”与”切换”的区别.

 

176.UDP可以跨网段发送吗?

 

177.最简单的确认远程计算机(win2K以上)某个监听端口是正常建立的?

 

178. 找错

void test1()

{

char string[10];

char* str1=”0123456789″;

strcpy(string, str1);

}

答:表面上并且编译都不会错误。但如果string数组原意表示的是字符串的话,那这个赋值就没有达到意图。最好定义为char string[11],这样最后一个元素可以存储字符串结尾符’\0′;

void test2()

{

char string[10], str1[10];

for(int I=0; I<10;I++)

{

str1[I] =’a’;

}

strcpy(string, str1);

}

答:strcpy使用错误,strcpy只有遇到字符串末尾的’\0′才会结束,而str1并没有结尾标志,导致strcpy函数越界访问,不妨让str1[9]=’\0′,这样就正常了。

void test3(char* str1)

{

char string[10];

if(strlen(str1)<=10)

{

strcpy(string, str1);

}

}

答:这又会出现第一道改错题的错误了。strlen(str1)算出来的值是不包含结尾符’\0′的,如果str1刚好为10个字符+1结尾符,string就得不到结尾符了。可将strlen(str1)<=10改为strlen(str1)<10。

 

179. 找错

#define MAX_SRM 256

 

DSN get_SRM_no()

{

static int SRM_no;

int I;

for(I=0;I<MAX_SRM;I++,SRM_no++)

{

SRM_no %= MAX_SRM;

if(MY_SRM.state==IDLE)

{

break;

}

}

if(I>=MAX_SRM)

return (NULL_SRM);

else

return SRM_no;

}

答:我不知道这段代码的具体功能,但明显有两个错误

1,SRM_no没有赋初值

2,由于static的声明,使该函数成为不可重入(即不可预测结果)函数,因为SRM_no变量放在程序的全局存储区中,每次调用的时候还可以保持原来的赋值。这里应该去掉static声明。

180. 写出程序运行结果

int sum(int a)

{

auto int c=0;

static int b=3;

c+=1;

b+=2;

return(a+b+c);

}

void main()

{

int I;

int a=2;

for(I=0;I<5;I++)

{

printf(“%d,”, sum(a));

}

}

答:8,10,12,14,16

该题比较简单。只要注意b声明为static静态全局变量,其值在下次调用时是可以保持住原来的赋值的就可以。

181.

int func(int a)

{

int b;

switch(a)

{

case 1: b=30;

case 2: b=20;

case 3: b=16;

default: b=0;

}

return b;

}

则func(1)=?

答:func(1)=0,因为没有break语句,switch中会一直计算到b=0。这是提醒我们不要忘了break。呵呵。

 

182:

int a[3];

a[0]=0; a[1]=1; a[2]=2;

int *p, *q;

p=a;

q=&a[2];

则a[q-p]=?

答:a[q-p]=a[2]=2;这题是要告诉我们指针的运算特点

183.定义 int **a[3][4], 则变量占有的内存空间为:_____

答:此处定义的是指向指针的指针数组,对于32位系统,指针占内存空间4字节,因此总空间为3×4×4=48。

184.编写一个函数,要求输入年月日时分秒,输出该年月日时分秒的下一秒。如输入2004年12月31日23时59分59秒,则输出2005年1月1日0时0分0秒。

答:

/*输入年月日时分秒,输出年月日时分秒的下一秒,输出仍然在原内存空间*/

bool NextMinute(int *nYear,int *nMonth,int *nDate,int *nHour,int *nMinute,int *nSecond)

{

if(*nYear<0 || *nMonth>12 || *nMonth<0 || *nHour>23 || *nHour<0 || *nMinute<0 || *nMinute>59 || *nSecond<0 || *nSecond>59) return false;

int nDays;

switch(*nMonth)

{

case 1:

case 3:

case 5:

case 7:

case 8:

case 10:

case 12:

nDays=31;

break;

case 2:// 判断闰年

if(*nYear%400==0||*nYear%100!=0&&*nYear%4==0)

{

nDays=29;

}

else

{

nDays=28;

}

break;

default:

nDays=30;

break;

}

if(*nDate<0 || *nDate>nDays) return false;

 

(*nSecond)++;  // 秒加1

if(*nSecond>=60)  // 秒满60,做出特殊处理,下面时,日,月等类同

{

*nSecond=0;

(*nMinute)++;

if(*nMinute>=60)

{

*nMinute=0;

(*nHour)++;

if(*nHour>=24)

{

*nHour=0;

(*nDate)++;

if(*nDate>nDays)

{

*nDate=1;

(*nMonth)++;

if(*nMonth>12)

{

*nMonth=1;

(*nYear)++;

}

}

}

}

}

return true;

}

/*示例可运行代码*/

void main()

{

int nYear=2004,nMonth=12,nDate=31,nHour=23,nMinute=59,nSecond=59;

bool res = NextMinute(&nYear,&nMonth,&nDate,&nHour,&nMinute,&nSecond);

if(res)

printf(“The result:%d-%d-%d %d:%d:%d”,nYear,nMonth,nDate,nHour,nMinute,nSecond);

else

printf(“Input error!\n”);

}

185. 写一个函数,判定运算环境(16位以上字长)是little-endian还是big-endian

 

186. 操作系统的主要组成部分?

 

187.操作系统中进程调度策略有哪几种?

 

188.进程间主要的通讯方式?

 

189.写出进程的主要状态?

 

190.以太网物理地址和IP地址转换采用什么协议?

 

191.IP地址的编码分为哪两部分?

 

192.写出以太网数据帧格式

 

193.8031和8051的主要区别?

 

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

分析以下程序的执行结果

#include<iostream.h>

class base

{

public:

base(){cout<< “constructing base class”<<endl;}

~base(){cout<<”destructing base class”<<endl;}

};

class subs:public base

{

public:

subs(){cout<<”constructing sub class”<<endl;}

~subs(){cout<<”destructing sub class”<<endl;}

};

void main()

{

subs s;

}

 

195.指出下面程序的错误

#define SIZE 5

struct String

{

char *pData;

};

void  main()

{

char *pData;

};

void  main()

{

char acValue1[SIZE]={‘H’,’E’,’L’,’L’,’O’};

char acValue2[SIZE]={‘W’,’O’,’L’,’D’};

struct String a,b,c;

a.pData=malloc(SIZE*sizeof(char));

memcpy(a.pData,acValuel,SIZE);

b.pData=malloc(SIZE*sizeof(char));

mempcpy(b.pData,acValue2,SIZE);

b=a;

free(a.pData);

c=b;

}

 

196.指出下面两段程序的区别

1

main()

{

int loop=1;

int arr[10];

int i=0;

while(loop<5)

{

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

{

arr[i]=1;

}

loop++;

}

}

2

main()

{

int arr[10];

int loop=1;

int i=0;

while(loop<5)

{

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

{

arr[i]=1;

}

loop++;

}

}

 

197.指出下面程序的错误(函数GetValue返回 unsigned char类型的值)

#define  MAXNUM  400;

unsigned char uclndex,uclnputVar,aucArray[MAXNUM];

for(ucIndx =0;ucIndex<=MAXNUM;ucIndex++)

{

aucArray[ucIndex]=aucArray[ucIndex]+1;

}

ucInputVar=GetValue();

for(ucIndex=0;ucIndex>(ucInputVar-1);ucIndex++)

{

aucArray[ucIndex]=aucArray[ucIndex]*2+1;

}

 

198.什么是com和ActiveX,简述DCOM。

答:COM(Component Object Mode)即组件对象模型,是组件之间相互接口的规范。其作用是使各种软件构件和应用软件能够用一种统一的标准方式进行交互。COM不是一种面向对象的语言,而是一种与源代码无关的二进制标准。
ActiveX是Microsoft提出的一套基于COM的构件技术标准,实际上是对象嵌入与炼接(OLE)的新版本。基于分布式环境下的COM被称作DCOM(Distribute COM,分布式组件对象模型),它实现了COM对象与远程计算机上的另一个对象之间直接进行交互。DCOM规范定义了分散对象创建和对象间通信的机制,DCOM是ActiveX的基础,因为ActiveX主要是针对Internet应用开发(相比OLE)的技术,当然也可以用于普通的桌面应用程序。

199.列出3个常用网络协议使用的端口。

答:HTTP协议用80端口,FTP协议用21端口,POP3协议用110端口

 

199  什么是ODBC?

答:ODBC(Open Database Connectivity,开放数据库互连)是微软公司开放服务结构(WOSA,Windows Open Services Architecture)中有关数据库的一个组成部分,它建立了一组规范,并提供了一组对数据库访问的标准API(应用程序编程接口)。ODBC的最大优点是能以统一的方式(用它提供的API访问数据库)处理所有的数据库。

200  结构化编程和goto语句的区别和关系?

答:结构化编程设计思想采用了模块分解与功能抽象和自顶向下、分而治之的方法,从而有效地将一个较复杂的程序系统设计任务分解成许多易于控制和处理的子程序,便于开发和维护。goto语句可以实现无条件跳转,改变程序流向,破坏结构化编程设计风格。但goto语句在结构化编程中并非不可使用,只是要受到限制的使用。

【技术试题】C++开发工程师面试题库 50~100道

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

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

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

 

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

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

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

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

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

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

1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。
2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。
3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多。

56.struct 和 class 的区别

 

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

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

57.当一个类A 中没有生命任何成员变量与成员函数,这时sizeof(A)的值是多少,如果不是零,请解释一下编译器为什么没有让它为零。(Autodesk)

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

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

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

59. 比较C++中的4种类型转换方式?

 

请参考:http://blog.csdn.net/wfwd/archive/2006/05/30/763785.aspx,重点是static_cast, dynamic_cast和reinterpret_cast的区别和应用。

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

61.请说出const与#define 相比,有何优点?

答案:1) const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
2) 有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。

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

数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。
(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 字节
}

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

答案:
a.成员函数被重载的特征:
(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual 关键字可有可无。
b.覆盖是指派生类函数覆盖基类函数,特征是:
(1)不同的范围(分别位于派生类与基类);
(2)函数名字相同;
(3)参数相同;
(4)基类函数必须有virtual 关键字。
c.“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)

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

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

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

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

答案:
#ifdef __cplusplus
cout<<”c++”;
#else
cout<<”c”;
#endif

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

答案:

#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(); //关闭输出文件流
}

68. 链表题:一个链表的结点结构

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 ;
}

69. 分析一下这段程序的输出 (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

70. 写一个函数找出一个整数数组中,第二大的数 (microsoft)

答案:
const int MINNUMBER = -32767 ;
int find_sec_max( int data[] , int count)
{
int maxnumber = data[0] ;
int sec_max = MINNUMBER ;
for ( int i = 1 ; i < count ; i++)
{
if ( data[i] > maxnumber )
{
sec_max = maxnumber ;
maxnumber = data[i] ;
}
else
{
if ( data[i] > sec_max )
sec_max = data[i] ;
}
}
return sec_max ;
}

71. 写一个在一个字符串(n)中寻找一个子串(m)第一个位置的函数。

KMP算法效率最好,时间复杂度是O(n+m)。

72. 多重继承的内存分配问题:

比如有class A : public class B, public class C {}
那么A的内存结构大致是怎么样的?

这个是compiler-dependent的, 不同的实现其细节可能不同。
如果不考虑有虚函数、虚继承的话就相当简单;否则的话,相当复杂。
可以参考《深入探索C++对象模型》,或者:

http://blog.csdn.net/wfwd/archive/2006/05/30/763797.aspx

73. 如何判断一个单链表是有环的?(注意不能用标志位,最多只能用两个额外指针)

struct node { char val; node* next;}

bool check(const node* head) {} //return false : 无环;true: 有环

一种O(n)的办法就是(搞两个指针,一个每次递增一步,一个每次递增两步,如果有环的话两者必然重合,反之亦然):
bool check(const node* head)
{
if(head==NULL) return false;
node *low=head, *fast=head->next;
while(fast!=NULL && fast->next!=NULL)
{
low=low->next;
fast=fast->next->next;
if(low==fast) return true;
}
return false;
}

74.不使用库函数,编写函数int strcmp(char  *source, char *dest)相等返回0,不等返回-1

int StrCmp(char  *source, char *dest)

{

assert(source !=NULL) ;

assert(dest!=NULL) ;

while(*source= =*dest&&*source&&*dest)

{

source++;

dest++;

}

return (*source!=*dest)?-1:0;

}

75.写一个函数,实现将一个字符串中的’\t’替换成四个’*’, ’\t’个数不定。如char *p=”ht\thdsf\t\ttt\tfds dfsw\t ew\t”,替换后p=”ht****hdsf********tt****fds dfsw**** ew****”。

char *Replace(char *Sorce)

{

char *pTemp=Sorce;

int iCount=0;

int iSizeOfSorce=0;

 

while(*pTemp!=’\0′)

{

if(‘\t’==*pTemp)

iCount++;

pTemp++;

}

iSizeOfSorce=pTemp-Sorce;

 

char *pNewStr=new char[iSizeOfSorce+3*iCount*sizeof(char)+1];

char *pTempNewStr=pNewStr;

 

pTemp=Sorce;

while(*pTemp!=’\0′)

{

if(‘\t’==*pTemp)

{

for(int iLoop=0; iLoop<4; iLoop++)

{

*pTempNewStr=’*’;

pTempNewStr++;

}

pTemp++;

}

else

{

*pTempNewStr=*pTemp;

pTemp++;

pTempNewStr++;

}

}

*pTempNewStr=’\0′;

 

return pNewStr;

}

76.写一函数实现将一个字符串中的数字字符全部去掉。

void RemoveNum(char strSrc[])

{

char *p=strSrc;

char *q;

while(*p!=’\0′)

{

if(*p>=’0′&&*p<=’9′)

{

q=p;

while(*q!=’\0′)

{

*q=*(q+1);

q++;

}

}

else

{

p++;

}

}

}

77、链表节点结构如下:

struct STUDENT

{

long num;

float score;

STUDENT *pNext;

};

编写实现将两棵有序(按学号从小到大)的链表合并的函数,要求合并后的链表有序(按学号从小到大)

STUDENT *EmergeList(STUDENT *pHead1,STUDENT *pHead2)

{

//取小者为表头

STUDENT * pHead;

STUDENT *p1;

STUDENT *p2;

STUDENT *pCur;

STUDENT *pTemp;

 

if (pHead1->num<= pHead2->num)

{

pHead = pHead1;

p2 = pHead2;

p1=pHead1->pNext;

}

else

{

pHead = pHead2;

p1 = pHead1;

p2=pHead2->pNext;

}

pCur=pHead;

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

{

if(p2->num<p1->num)

{

pCur->pNext=p2;

p2=p2->pNext;

pCur=pCur->pNext;

}

else if(p2->num>p1->num)

{

pCur->pNext=p1;

p1=p1->pNext;

pCur=pCur->pNext;

}

else if(p2->num==p1->num)

{

pCur->pNext=p1;

p1=p1->pNext;

pCur=pCur->pNext;

pTemp= p2;

p2=p2->pNext;

delete pTemp;

pTemp = NULL;

}

}

if(NULL==p1)

{

pCur->pNext=p2;

}

else if(NULL==p2)

{

pCur->pNext=p1;

}

return pHead;

}

78、封装CMyString类,要求声明和实现分开,声明见MyString.h,该类的声明可以添加内容,完成STLTest文件夹下main文件的要求。

 参见STLTest Answer文件夹

79.请你分别画出OSI的七层网络结构图和TCP/IP的五层结构图。

 

80.请你详细地解释一下IP协议的定义,在哪个层上面?主要有什么作用?TCP与UDP呢?

 

81.请问交换机和路由器各自的实现原理是什么?分别在哪个层次上面实现的?

 

82.请问C++的类和C里面的struct有什么区别?

 

83.全局变量和局部变量有什么区别?是怎么实现的?操作系统和编译器是怎么知道的?

 

84. 非C++内建型别 A 和 B,在哪几种情况下B能隐式转化为A?[C++中等

a. class B : public A { ……} // B公有继承自A,可以是间接继承的

b. class B { operator A( ); } // B实现了隐式转化为A的转化

c. class A { A( const B& ); } // A实现了non-explicit的参数为B(可以有其他带默认值的参数)构造函数

d. A& operator= ( const A& ); // 赋值操作,虽不是正宗的隐式类型转换,但也可以勉强算一个

85. 以下代码中的两个sizeof用法有问题吗?[C易]

void UpperCase( char str[] ) // 将 str 中的小写字母转换成大写字母

{

for( size_t i=0; i<sizeof(str)/sizeof(str[0]); ++i )

if( ‘a’<=str[i] && str[i]<=’z’ )

str[i] -= (‘a’-‘A’ );

}

char str[] = “aBcDe”;

cout << “str字符长度为: ” << sizeof(str)/sizeof(str[0]) << endl;

UpperCase( str );

cout << str << endl;

86. 以下代码有什么问题?[C难]

void char2Hex( char c ) // 将字符以16进制表示

{

char ch = c/0×10 + ’0′; if( ch > ’9′ ) ch += (‘A’-’9′-1);

char cl = c%0×10 + ’0′; if( cl > ’9′ ) cl += (‘A’-’9′-1);

cout << ch << cl << ‘ ‘;

}

char str[] = “I love 中国”;

for( size_t i=0; i<strlen(str); ++i )

char2Hex( str[i] );

cout << endl;

87. 以下代码有什么问题?[C++易]

struct Test

{

Test( int ) {}

Test() {}

void fun() {}

};

void main( void )

{

Test a(1);

a.fun();

Test b();

b.fun();

}

88. 以下代码有什么问题?[C++易]

cout << (true?1:”1″) << endl;

 

89. 以下代码能够编译通过吗,为什么?[C++易]

unsigned int const size1 = 2;

char str1[ size1 ];

unsigned int temp = 0;

cin >> temp;

unsigned int const size2 = temp;

char str2[ size2 ];

90. 以下代码中的输出语句输出0吗,为什么?[C++易]

struct CLS

{

int m_i;

CLS( int i ) : m_i(i) {}

CLS()

{

CLS(0);

}

};

CLS obj;

cout << obj.m_i << endl;

91. C++中的空类,默认产生哪些类成员函数?[C++易]

 答:

class Empty

{

public:

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

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

~Empty();                            // 析构函数

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

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

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

};

92. 以下两条输出语句分别输出什么?[C++难]

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; // 输出什么?

93. 以下反向遍历array数组的方法有什么错误?[STL易]

vector array;

array.push_back( 1 );

array.push_back( 2 );

array.push_back( 3 );

for( vector::size_type i=array.size()-1; i>=0; –i ) // 反向遍历array数组

{

cout << array[i] << endl;

}

94. 以下代码有什么问题?[STL易]

typedef vector IntArray;

IntArray array;

array.push_back( 1 );

array.push_back( 2 );

array.push_back( 2 );

array.push_back( 3 );

// 删除array数组中所有的2

for( IntArray::iterator itor=array.begin(); itor!=array.end(); ++itor )

{

if( 2 == *itor ) array.erase( itor );

}

95. 写一个函数,完成内存之间的拷贝。[考虑问题是否全面]

答:

void* mymemcpy( void *dest, const void *src, size_t count )

{

char* pdest = static_cast<char*>( dest );

const char* psrc = static_cast<const char*>( src );

if( pdest>psrc && pdest<psrc+cout ) 能考虑到这种情况就行了

{

for( size_t i=count-1; i!=-1; –i )

pdest[i] = psrc[i];

}

else

{

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

pdest[i] = psrc[i];

}

return dest;

}

 

int main( void )

{

char str[] = “0123456789″;

mymemcpy( str+1, str+0, 9 );

cout << str << endl;

system( “Pause” );

return 0;

}

 

华为C/C++笔试题(附答案)2008年02月15日星期五 18:001.写出判断ABCD四个表达式的是否正确, 若正确, 写出经过表达式中 a的值(3分)

96.int a = 4;

(A)a += (a++); (B) a += (++a) ;(C) (a++) += a;(D) (++a) += (a++);

a = ?

答:C错误,左侧不是一个有效变量,不能赋值,可改为(++a) += a;

改后答案依次为9,10,10,11

97.某32位系统下, C++程序,请计算sizeof 的值(5分).

char str[] = “http://www.ibegroup.com/”

char *p = str ;

int n = 10;

请计算

sizeof (str ) = ?(1)

sizeof ( p ) = ?(2)

sizeof ( n ) = ?(3)

void Foo ( char str[100]){

请计算

sizeof( str ) = ?(4)

}

void *p = malloc( 100 );

请计算

sizeof ( p ) = ?(5)

答:(1)17 (2)4 (3) 4 (4)4 (5)4

98. 回答下面的问题. (4分)

(1).头文件中的 ifndef/define/endif 干什么用?预处理

答:防止头文件被重复引用

(2). #i nclude 和 #i nclude “filename.h” 有什么区别?

答:前者用来包含开发环境提供的库头文件,后者用来包含自己编写的头文件。

(3).在C++ 程序中调用被 C 编译器编译后的函数,为什么要加 extern “C”声明?

答:函数和变量被C++编译后在符号库中的名字与C语言的不同,被extern “C”修饰的变

量和函数是按照C语言方式编译和连接的。由于编译后的名字不同,C++程序不能直接调

用C 函数。C++提供了一个C 连接交换指定符号extern“C”来解决这个问题。

(4). switch()中不允许的数据类型是?

答:实型

99. 回答下面的问题(6分)

(1).Void GetMemory(char **p, int num){

*p = (char *)malloc(num);

}

void Test(void){

char *str = NULL;

GetMemory(&str, 100);

strcpy(str, “hello”);

printf(str);

}

请问运行Test 函数会有什么样的结果?

答:输出“hello”

(2). void Test(void){

char *str = (char *) malloc(100);

strcpy(str, “hello”);

free(str);

if(str != NULL){

strcpy(str, “world”);

printf(str);

}

}

请问运行Test 函数会有什么样的结果?

答:输出“world”

100. char *GetMemory(void){

char p[] = “hello world”;

return p;

}

void Test(void){

char *str = NULL;

str = GetMemory();

printf(str);

}

请问运行Test 函数会有什么样的结果?

答:无效的指针,输出不确定

【技术试题】C++开发工程师面试题库 1~50道

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)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。

(4)流操作符重载返回值申明为“引用”的作用:

流操作符<<和>>,这两个操作符常常希望被连续使用,例如:cout << “hello” << endl; 因此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。可选的其它方案包括:返回一个流对象和返回一个流对象指针。但是对于返回一个流对象,程序必须重新(拷贝)构造一个新的流对象,也就是说,连续的两个<<操作符实际上是针对不同对象的!这无法让人接受。对于返回一个流指针则不能连续使用<<操作符。因此,返回一个流对象引用是惟一选择。这个唯一选择很关键,它说明了引用的重要性以及无可替代性,也许这就是C++语言中引入引用这个概念的原因吧。赋值操作符=。这个操作符象流操作符一样,是可以连续使用的,例如:x = j = 10;或者(x=10)=100;赋值操作符的返回值必须是一个左值,以便可以被继续赋值。因此引用成了这个操作符的惟一返回值选择。

例3

#i nclude <iostream.h>
int &put(int n);
int vals[10];
int error=-1;
void main()
{
put(0)=10; //以put(0)函数值作为左值,等价于vals[0]=10;
put(9)=20; //以put(9)函数值作为左值,等价于vals[9]=20;
cout<<vals[0];
cout<<vals[9];
}
int &put(int n)
{
if (n>=0 && n<=9 ) return vals[n];
else { cout<<”subscript error”; return error; }
}

(5)在另外的一些操作符中,却千万不能返回引用:+-*/ 四则运算符。它们不能返回引用,Effective C++[1]的Item23详细的讨论了这个问题。主要原因是这四个操作符没有side effect,因此,它们必须构造一个对象作为返回值,可选的方案包括:返回一个对象、返回一个局部变量的引用,返回一个new分配的对象的引用、返回一个静态对象引用。根据前面提到的引用作为返回值的三个规则,第2、3两个方案都被否决了。静态对象的引用又因为((a+b) == (c+d))会永远为true而导致错误。所以可选的只剩下返回一个对象了。

38. “引用”与多态的关系?

引用是除指针外另一个可以产生多态效果的手段。这意味着,一个基类的引用可以指向它的派生类实例。

例4

Class A; Class B : Class A{…}; B b; A& ref = b;

39. “引用”与指针的区别是什么?

指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。此外,就是上面提到的对函数传ref和pointer的区别。

40. 什么时候需要“引用”?

流操作符<<和>>、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数、其它情况都推荐使用引用。

以上 2-8 参考:http://blog.csdn.net/wfwd/archive/2006/05/30/763551.aspx

41. 结构与联合有和区别?

1. 结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)。
2. 对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的。

42. 下面关于“联合”的题目的输出?

a)

#i nclude <stdio.h>
union
{
int i;
char x[2];
}a;

void main()
{
a.x[0] = 10;
a.x[1] = 1;
printf(“%d”,a.i);
}
答案:266 (低位低地址,高位高地址,内存占用情况是Ox010A)

b)

main()
{
union{ /*定义一个联合*/
int i;
struct{ /*在联合中定义一个结构*/
char first;
char second;
}half;
}number;
number.i=0×4241; /*联合成员赋值*/
printf(“%c%c\n”, number.half.first, mumber.half.second);
number.half.first=’a’; /*联合中结构成员赋值*/
number.half.second=’b’;
printf(“%x\n”, number.i);
getch();
}
答案: AB (0×41对应’A’,是低位;Ox42对应’B’,是高位)

6261 (number.i和number.half共用一块地址空间)

 

43. 已知strcpy的函数原型:char *strcpy(char *strDest, const char *strSrc)其中strDest 是目的字符串,strSrc 是源字符串。不调用C++/C 的字符串库函数,请编写函数 strcpy。

答案:
char *strcpy(char *strDest, const char *strSrc)
{
if ( strDest == NULL || strSrc == NULL)
return NULL ;
if ( strDest == strSrc)
return strDest ;
char *tempptr = strDest ;
while( (*strDest++ = *strSrc++) != ‘\0’)
return tempptr ;
}

 

44. .h头文件中的ifndef/define/endif 的作用?

答:防止该头文件被重复引用。

 

45. #i nclude<file.h> 与 #i nclude “file.h”的区别?

答:前者是从Standard Library的路径寻找和引用file.h,而后者是从当前工作路径搜寻并引用file.h。

 

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

首先,作为extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。

通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明。例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可。这样,模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但是并不会报错;它会在连接阶段中从模块A编译生成的目标代码中找到此函数

extern “C”是连接申明(linkage declaration),被extern “C”修饰的变量和函数是按照C语言方式编译和连接的,来看看C++中对类似C的函数是怎样编译的:

作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在符号库中的名字与C语言的不同。例如,假设某个函数的原型为:

void foo( int x, int y );

该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同,但是都采用了相同的机制,生成的新名字称为“mangled name”)。

_foo_int_int 这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。例如,在C++中,函数void foo( int x, int y )与void foo( int x, float y )编译生成的符号是不相同的,后者为_foo_int_float。

同样地,C++中的变量除支持局部变量外,还支持类成员变量和全局变量。用户所编写程序的类成员变量可能与全局变量同名,我们以”.”来区分。而本质上,编译器在进行编译时,与函数的处理相似,也为类中的变量取了一个独一无二的名字,这个名字与用户程序中同名的全局变量名字不同。

未加extern “C”声明时的连接方式

假设在C++中,模块A的头文件如下:

// 模块A头文件 moduleA.h
#ifndef MODULE_A_H
#define MODULE_A_H
int foo( int x, int y );
#endif

在模块B中引用该函数:

// 模块B实现文件 moduleB.cpp
#i nclude “moduleA.h”
foo(2,3);

实际上,在连接阶段,连接器会从模块A生成的目标文件moduleA.obj中寻找_foo_int_int这样的符号!

加extern “C”声明后的编译和连接方式

加extern “C”声明后,模块A的头文件变为:

// 模块A头文件 moduleA.h
#ifndef MODULE_A_H
#define MODULE_A_H
extern “C” int foo( int x, int y );
#endif

在模块B的实现文件中仍然调用foo( 2,3 ),其结果是:
(1)模块A编译生成foo的目标代码时,没有对其名字进行特殊处理,采用了C语言的方式;

(2)连接器在为模块B的目标代码寻找foo(2,3)调用时,寻找的是未经修改的符号名_foo。

如果在模块A中函数声明了foo为extern “C”类型,而模块B中包含的是extern int foo( int x, int y ) ,则模块B找不到模块A中的函数;反之亦然。

所以,可以用一句话概括extern “C”这个声明的真实目的(任何语言中的任何语法特性的诞生都不是随意而为的,来源于真实世界的需求驱动。我们在思考问题时,不能只停留在这个语言是怎么做的,还要问一问它为什么要这么做,动机是什么,这样我们可以更深入地理解许多问题):实现C++与C及其它语言的混合编程。

明白了C++中extern “C”的设立动机,我们下面来具体分析extern “C”通常的使用技巧:

extern “C”的惯用法

(1)在C++中引用C语言中的函数和变量,在包含C语言头文件(假设为cExample.h)时,需进行下列处理:

extern “C”
{
#i nclude “cExample.h”
}

而在C语言的头文件中,对其外部函数只能指定为extern类型,C语言中不支持extern “C”声明,在.c文件中包含了extern “C”时会出现编译语法错误。

C++引用C函数例子工程中包含的三个文件的源代码如下:

/* c语言头文件:cExample.h */
#ifndef C_EXAMPLE_H
#define C_EXAMPLE_H
extern int add(int x,int y);
#endif

/* c语言实现文件:cExample.c */
#i nclude “cExample.h”
int add( int x, int y )
{
return x + y;
}

// c++实现文件,调用add:cppFile.cpp
extern “C”
{
#i nclude “cExample.h”
}
int main(int argc, char* argv[])
{
add(2,3);
return 0;
}

如果C++调用一个C语言编写的.DLL时,当包括.DLL的头文件或声明接口函数时,应加extern “C” { }。

(2)在C中引用C++语言中的函数和变量时,C++的头文件需添加extern “C”,但是在C语言中不能直接引用声明了extern “C”的该头文件,应该仅将C文件中将C++中定义的extern “C”函数声明为extern类型。

C引用C++函数例子工程中包含的三个文件的源代码如下:

//C++头文件 cppExample.h
#ifndef CPP_EXAMPLE_H
#define CPP_EXAMPLE_H
extern “C” int add( int x, int y );
#endif

//C++实现文件 cppExample.cpp
#i nclude “cppExample.h”
int add( int x, int y )
{
return x + y;
}

/* C实现文件 cFile.c
/* 这样会编译出错:#i nclude “cExample.h” */
extern int add( int x, int y );
int main( int argc, char* argv[] )
{
add( 2, 3 );
return 0;
}

15题目的解答请参考《C++中extern “C”含义深层探索》注解:

 

47. 关联、聚合(Aggregation)以及组合(Composition)的区别?

涉及到UML中的一些概念:关联是表示两个类的一般性联系,比如“学生”和“老师”就是一种关联关系;聚合表示has-a的关系,是一种相对松散的关系,聚合类不需要对被聚合类负责,如下图所示,用空的菱形表示聚合关系:

从实现的角度讲,聚合可以表示为:

class A {…} class B { A* a; …..}

而组合表示contains-a的关系,关联性强于聚合:组合类与被组合类有相同的生命周期,组合类要对被组合类负责,采用实心的菱形表示组合关系:

实现的形式是:

class A{…} class B{ A a; …}

参考文章:http://blog.csdn.net/wfwd/archive/2006/05/30/763753.aspx

http://blog.csdn.net/wfwd/archive/2006/05/30/763760.aspx

 

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

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

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

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

 

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

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

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

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

从实现原理上来说:

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

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

 

50. 多态的作用?



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