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

C++面试笔试经典题集

2015-10-25 20:44 423 查看
(1)

char str[]="hello";
printf("%d\n",sizeof(str));


解析:答案是6,包含一个结束符 '\0'。

(2)下面哪种情况下B不能隐式转换为A?

A: class B:public A{}
B: class A:public B{}
C: class B{operator A();}
D: class A{A(const B&);}


解析:答案B。A中A表示基类,B是派生类,向上级类型转换是隐式的(也就是父类指针可以指向子类),因为部分元素丢弃可以自动完成,向下转型是显示的因为不知道应该增加的值是什么,所以B不能。

C中 operator除了表示函数重载操作符,还可以表示B类型可以转换为A类型。D拷贝函数,这个肯定是可以的。

//父类指针指向子类
#include <iostream>
using namespace std;
class A
{
public:
int x;
int y;
A(int x,int y)
{
this->x=x;
this->y=y;
}
};
class B:public A
{
public:
int m;
int n;
B(int x,int y,int m,int n):A(x,y)
{
A(x,y);
this->m=m;
this->n=n;
}
};
int main()
{
A *a;
B b(1,2,3,4);
a=&b;
printf("%d %d\n",a->x,a->y);

/*
B *pb;
A pa(5,6);
pb=&a;
*/
return 0;
}


(3)有三个类A B C定义如下, 请确定sizeof(A)
sizeof(B) sizeof(C)的大小顺序.
struct A{
A() {}
~A() {}
int m1;
int m2;
};
struct B:A{
B() {}
~B() {}
int m1;
char m2;
static char m3;
};
struct C{
C() {}
virtual~C() {}
int m1;
short m2;
};


A: A=B=C
B: A<B<C
C: A=C<B
D: A<C<B


解析:答案是D(32位机+32系统)。

首先结构体A的大小为8,很容易求出;

结构体B继承A,则B的起始存储大小从第8个字节开始,static对结构体本身大小没有影响,另外根据结构体的规则可知,其大小为最大对齐数(4)的倍数。(本来算得13)所以为16;

结构体C中有个虚函数,所以包含虚函数指针,占4个字节,(本来算得大小为10,其大小为最大对齐数(4)的倍数),所以其大小为12。

如果是64位机+64位系统答案则是 A<C=B,这种情况在C中的虚表指针是8个字节,这样C字节数是:8+4+2=14,最后在字节对齐就是16字节。

下面进行详细分析:

#include <stdio.h>
struct A
{
A(){m1=10,m2=10;};
~A(){};
int m1;
int m2;
};
struct B:A
{
B()
{
m1=10;
m2=10;

};
~B(){};
int m1;
char m2;
static char m3;
};
struct C
{
C(){m1=10,m2=10;};
virtual ~C(){};
int m1;
short m2;
};
char B::m3=10;

int main()
{
A a;
B b;
C c;
// printf("%d\n",b.A::m1);
printf("%d %d %d\n",sizeof(A),sizeof(B),sizeof(C));
}


在win32下,用vc6.0调试:



可以看到,从地址0X0018ff24开始,连续存放了b.A::m1,b.A::m2,b.m1,b.m2,内存区域中的十六进制CC,是vc6.0默认的初始化,为了对齐添加了3个字节的默认数据0xCC,这个我们可以从反汇代码中看出来:



那么问题来了,m3去哪里了呢,这个我们后面再看。



这是a地址信息,可以看到b结束后紧挨着就是a的数据。

下面看看c的内存结构:



在我们自定义数据的开始前,我们看到了传说中的虚表指针占4个字节。

最后我们再来看看B中的m3,在程序中我们定义了一个全局变量m,我们先来看看m的存储区域:



m的地址在全局区域,很明显和我们的a,b,c地址不一样。
我们再看看B中的m3的信息:



哎呀,m3和m挨着的呀,思考一下我们得出了一个结论:静态数据放在全局变量区的。

在Mac(64位)看虚表指针大小:



刚好8字节。

(4)以下代码的输出结果是?

#define a 10

void foo();
main(){

printf("%d..",a);
foo();
printf("%d",a);
}
void foo(){
#undef a
#define a 50
}


解析:选A,define在预处理阶段就把main中的a全部替换为10了. 另外,不管是在某个函数内,还是在函数外,define都是从定义开始知道文件结尾,所以如果把foo函数放到main上面的话,则结果会是50 ,50

(5)以下说法正确的是?

在多线程中不加限制的随意访问非static局部变量可能会导致运算结果出错

在多线程中不加限制的随意访问非static全局变量可能会导致运算结果出错

在多线程中不加限制的随意访问static局部变量可能会导致运算结果出错

在多线程中不加限制的随意访问static全局变量可能会导致运算结果出错

解析:选BD
无论是static还是非static的全局变量,如果不加限制随意访问的话易出现同步问题。

非static的局部变量,每个线程都是私有的,其他线程不会对其进行干扰.

对于静态局部变量,我字节测试了一下,发觉其它线程会对其进行干扰。所以觉得C也是对的。

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
struct A
{
A(){m1=10,m2=10;};
~A(){};
int m1;
int m2;
};
struct B:A
{
B()
{
m1=10;
m2=10;

};
~B(){};
int m1;
char m2;
static char m3;
};
struct C
{
C(){m1=10,m2=10;};
virtual ~C(){};
int m1;
short m2;
};
char B::m3=10;

void *fun(void *arg)
{
static int cnt=0;
cnt++;
printf("cnt 地址:%d\n",&cnt);
printf("cnt:%d\n",cnt);

return NULL;
}
int m;
int main()
{
printf("全局变量m地址:%d\n",&m);
pthread_t pid1,pid2;
pthread_create(&pid1, NULL, fun, NULL);
pthread_create(&pid2, NULL, fun, NULL);
sleep(10);
pthread_join(pid1, NULL);
pthread_join(pid2, NULL);
}


结果:



(6)函数作用:将整型数组p中n个数据增大
void increment_ints (int p [ ], int n)
{
assert(p != NULL);  /* 确保p不为空指针 */
assert(n >= 0);  /* 确保n不为负数 */
while (n)  /* 循环n次. */
{
*p++;          /* 增大p*/
p++, n--;      /* p指向下一位,n减1 */
}
}


A:*p++使得p在解引用之前增大,因为*和++两个运算符有相同的优先级并按自右向左的方式结合。

B: 数组的值是一个不能改变的值,所以p不能直接被修改。应该使用一个和p相关联的指针来完成这个操作。

C: *p++使得p在解引用之前增大,因为自增运算符的优先级比取址运算符优先级高。

D: while循环的条件必须是一个布尔类型的表达式,表达式应该为n!=0.

E: An array cannot be initialized to a variable size. The subscript n should be removed from the definition of the parameter p.

解析:选择C.

(7)下列代码的输出为:

class CParent
{
public: virtual void Intro()
{
printf( "I'm a Parent, " ); Hobby();
}
virtual void Hobby()
{
printf( "I like football!" );
}
};
class CChild : public CParent {
public: virtual void Intro()
{
printf( "I'm a Child, " ); Hobby();
}
virtual void Hobby()
{
printf( "I like basketball!\n" );
}
};
int main( void )
{
CChild *pChild = new CChild();
CParent *pParent = (CParent *) pChild;
pParent->Intro();
return(0);
}


A:I'm a Parent, I like football!

B: I'm a Parent, I like basketball!

C: I'm a Child, I like basketball!

D: I'm a Child, I like football!

解析:选择C,程序中是涉及到虚函数的调用,在基类pParent中加了Virtual关键字的函数就是虚拟函数,于是在pParent的派生类CChild中就可以通过重写虚拟函数来实现对基类虚拟函数的覆盖。当基类pParent的指针pParent指向派生类CChild的对象pChild时,对pChild的Intro()函数的调用实际上是调用了CChild的Intro()函数而不是pParent的Intro()函数。这是面向对象中的多态性的体现

(8)以下程序的输出是
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
class Base {
public:
Base(int j): i(j)  {}
virtual~Base() {}
void func1() {
i *= 10;
func2();
}
int getValue() {
return  i;
}
protected:
virtual void func2() {
i++;
}
protected:
int i;
};
class Child: public Base {
public:
Child(int j): Base(j) {}
void func1() {
i *= 100;
func2();
}
protected:
void func2() {
i += 2;
}
};
int main()
{
Base * pb = new Child(1);
pb->func1();
cout << pb->getValue() << endl; delete pb;
}


A: 11

B: 101

C: 12

D: 102

解析:选择C,用基类的指针指向不同的派生类的对象时, 基类指针调用其虚成员函数,则会调用其真正指向对象的成员函数, 而不是基类中定义的成员函数(只要派生类改写了该成员函数)。 若不是虚函数,则不管基类指针指向的哪个派生类对象,调用时都 会调用基类中定义的那个函数。

(9)阅读下面代码,程序会打印出来的值是?

#include <stdio.h>
void f(char**p)
{
*p +=2;
}
int main()
{
char *a[] = {"123","abc","456"},**p;
p = a;
f(p);
printf("%s\r\n",*p);
}


解析:程序会输出3,数组a是一个指针数组,指针p是二级指针,指向指针的一个指针,p=a,让指针p指向了数组a,即指针p里面存的是数组a地址,也即是a[0](指针)的地址,进入f函数后,构建了一个指针副本和指针p完全一致,*p为a数组中的第一个成员的值(也就是指针a[0]的值,指针a[0]的值是字符串“123”的地址),原本指针a[0]保存的是字符串"123"的地址,*p+=2过后,让a[0]指向了"3",*p指向的是a[0],所以最后*p指向的是字符串“3”.

下面是我改过后的代码可以参考一下:
#include <stdio.h>
void f(char**p)
{
*p +=2;
printf("f函数内:%s\n",*p);
}

void f2(char *p)
{
p+=2;
printf("f2函数内:%s\n",p);
}
int main()
{
char *a[] = {"123","abc","456"},**p;
p = a;
char *pp=*p;
printf("%s\n",pp);
f2(*p);
printf("f2函数外:%s\n",a[0]);
f(p);
printf("%s\r\n",*p);
printf("f函数外:%s\n",a[0]);
}


结果:



如果不清楚函数参数副本的可以看一下下面的博客:

C语言指针初探 一 指针与函数

(10)下述程序的输出是______。

#include<stdio.h>

int main()
{
static char *s[] = {"black", "white", "pink", "violet"};
char **ptr[] = {s+3, s+2, s+1, s}, ***p;
p = ptr;
++p;
printf("%s", **p+1);
return 0;
}


解析:输出结果是ink,分析方式和上一题一样的,ptr是一个二级指针数组,分别指向s[3],s[2],s[1],s[0],指针p是一个指向二级指针的三级指针,p=ptr.p存的是ptr的地址,,++p过后指向了下一个指针(也就是ptr[1]),此时p的地址是ptr[1]的地址,*p为ptr[1]的值,也就是s[2]的地址,**p指向了字符串s[2].

修改代码如下:

#include<stdio.h>

int main()
{
static char *s[] = {"black", "white", "pink", "violet"};
char **ptr[] = {s+3, s+2, s+1, s}, ***p;
p = ptr;
printf("%0x %0x\n",&ptr[0],p);
printf("%0x %0x\n",ptr[0],&s[3]);
++p;
printf("%0x %0x\n",&ptr[1],p);
printf("%s", **p+1);
return 0;
}


结果:



仔细分析一下很容易就明白的。

(11)当一个类A 中没有声明任何成员变量与成员函数,这时sizeof(A)的值是多少?

解析:空类的长度为1字节,如果对象完全不占用内存空间,那么空类就无法取得实例对象的地址,this指针失效,因此不能被实例化,而类的定义是由成员数据和成员函数组成,在没有成员数据的情况下,还可以有成员函数,因此任然需要实例化,分配了1字节的空间用于类的实例化,这1字节的数据并没有被使用。

(12) 在64位系统下,分别定义如下两个变量:char *p[10]; char(*p1)[10];请问,sizeof(p)和sizeof (p1)分别值为____。
解析:分别为 80,8 ,p是指针数组,含有10个指针元素,p1只是一个指向数组的指针而已。

(13)设已经有A,B,C,D4个类的定义,程序中A,B,C,D析构函数调用顺序为?
C c;
void main()
{
A*pa=new A();
B b;
static D d;
delete pa;
}


解析:A B D C

这道题主要考察的知识点是 :全局变量,静态局部变量,局部变量空间的堆分配和栈分配。

其中全局变量和静态局部变量是从 静态存储区中划分的空间,二者的区别在于作用域的不同,而之所以是先释放 D 在释放 C的原因是, 程序中首先调用的是 C的构造函数,然后调用的是 D 的构造函数,析构函数的调用与构造函数的调用顺序刚好相反。

局部变量A 是通过 new 从系统的堆空间中分配的,程序运行结束之后,系统是不会自动回收分配给它的空间的,需要程序员手动调用 delete 来释放。

局部变量 B 对象的空间来自于系统的栈空间,在该方法执行结束就会由系统自动通过调用析构方法将其空间释放。

之所以是 先 A 后 B 是因为,B 是在函数执行到 结尾 "}" 的时候才调用析构函数, 而语句 delete a ; 位于函数结尾 "}" 之前。

(14)若char是一字节,int是4字节,指针类型是4字节,代码如下:

class CTest
{
public:
CTest():m_chData(‘\0’),m_nData(0)
{
}
virtual void mem_fun(){}
private:
char m_chData;
int m_nData;
static char s_chData;
};
char CTest::s_chData=’\0’;

问:

(1)若按4字节对齐sizeof(CTest)的值是多少?

(2)若按1字节对齐sizeof(CTest)的值是多少?

4字节对齐情况下:
4字节虚表指针(存放位置0-3),char型数据一字节(存放在位置4),int型数据因为要数据对齐,所以从位置8开始放(8%4=0),位置5,6,7空着,静态数据存放在全局区。
所以最后结果为:4+1+3+4=12.
1字节对齐情况下:就是4+1+4=9.

解析:12,9

注意:

(1) 先找有没有virtual 有的话就要建立虚函数表,需要虚表指针(32位系统 4字节)

(2) static的成员变量属于类域,不算入对象中

(3) 空类或者只有成员函数 占1字节.
(4)字节对齐

(15)类成员函数的重载、覆盖和隐藏区别描述正确的有?

A:
覆盖是指在同一个类中名字相同,参数不同

B: 重载是指派生类函数覆盖基类函数,函数相同,参数相同,基类函数必须有virtual关键字

C: 派生类函数与基类函数相同,但是参数不同,会"隐藏"父类函数

D: 函数名字相同,参数相同,基类无virtual关键字的派生类的函数会"隐藏"父类函数

解析:C D

一、成员函数被重载的特征:

(1)相同的范围(在同一个类中);

(2)函数名字相同;

(3)参数不同;

(4)virtual 关键字可有可无。

二、覆盖是指派生类函数覆盖基类函数,特征是:

(1)不同的范围(分别位于派生类与基类);

(2)函数名字相同;

(3)参数相同;

(4)基类函数必须有virtual 关键字。

三、“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:

(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏。

(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏。

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