C++面试笔试经典题集

来源:互联网 发布:计算一组数据的标准差 编辑:程序博客网 时间:2024/05/21 09:42

(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=CB: A<B<CC: A=C<BD: 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 关键字。此时,基类的函数被隐藏。


待续。。。


0 0
原创粉丝点击