C++

牛客网c/c++工程师测试题

2018-01-17  本文已影响178人  analanxingde

1.关于new:

  1. new单个对象
    new在自由空间分配内存,但其无法为其分配的对象命名,因次是无名 的,分配之后返回一个指向该对象的指针。
int *pi = new int; // pi指向一个动态分配的,未初始化的无名对象
  1. new多个对象
int *pia = new int[10];    // 10个未初始化int
int *pia2 = new int[10](); // 10个值初始化为0的int

对于一些结构体,我们可以看到()往往表示构造函数,int是基本类型算初始化吧

2.枚举类型

enum string{    
    x1,    
    x2,    
    x3=10,    
    x4,    
    x5,    
} x;

如果是函数外定义那么是0
如果是函数内定义,那么是随机值,因为没有初始化

3.指针

若有   unsigned  char *p1;
          unsigned  long *p2;
          p1 = (unsigned char *)0x1000;
          p2 = (unsigned long *)0x2000;
请问     p1+5=( 1005   )             p2+5=(  2014  ) 

p1指向字符型,一次移动一个字符型,1个字节;p1+5后移5个字节,16进制表示为5;
p2指向长整型,一次移动一个长整型,4个字节,p2+5后移20字节,16进制表示为14。
char每次移动1个字节;short移动2个字节;int ,long ,float移动4个字节;double移动8个字节

4.strlen与sizeof

strlen:实际元素个数
sizeof:数组代销,或者字符串加'\n'共同个数

char str[20]="0123456789";
int a=strlen(str); //a=10;
int b=sizeof(str); //而b=20;
char str[]="Hello";
     printf("%d\n",sizeof(str));//6
    printf("%d\n",strlen(str));//5
    char*p=str;
    printf("%d\n",sizeof(p));//4

5.虚函数

下面关于虚函数和函数重载的叙述不正确的是
虚函数不是类的成员函数(不正确)
虚函数实现了C++的多态性
函数重载允许非成员函数。
函数重载的调用根据参数的个数、序列来确定,而虚函数依据对象确定

6指针强制类型转换

char str[] = "glad to test something";
char *p = str;
p++;
int *p1 = reinterpret_cast<int *>(p);
p1++;
p = reinterpret_cast<char *>(p1); 
printf("result is %s\n", p);//result is to test something

该题的关键是要认清楚强制类型转换后指针的类型。
p的类型为char *,p++后p指向str数组的第2个元素即字母“l”的位置。
p1的类型为int *,p1++后p1指向的位置增加4个字节,指向str数组中的第6个元素即字母“t”的位置。
因此最后p的内容为“to test something”。

7.析构函数调用顺序

C c;
void main()
{
    A*pa=new A();
    B b;
    static D d;
    delete pa;
}
//

考察全局变量,静态局部变量,局部变量空间的堆分配和栈分配
其中全局变量和静态局部变量时从 静态存储区中划分的空间,
二者的区别在于作用域的不同,全局变量作用域大于静态局部变量(只用于声明它的函数中),
而之所以是先释放 D 在释放 C的原因是, 程序中首先调用的是 C的构造函数,然后调用的是 D 的构造函数,析构函数的调用与构造函数的调用顺序刚好相反。
局部变量A 是通过 new 从系统的堆空间中分配的,程序运行结束之后,系统是不会自动回收分配给它的空间的,需要程序员手动调用 delete 来释放。
局部变量 B 对象的空间来自于系统的栈空间,在该方法执行结束就会由系统自动通过调用析构方法将其空间释放。
之所以是 先 A 后 B 是因为,B 是在函数执行到 结尾 "}" 的时候才调用析构函数, 而语句 delete a ; 位于函数结尾 "}" 之前。

8.c++类的内存布局

若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)的值是多少?//12
(2)若按1字节对齐sizeof(CTest)的值是多少?//9
测试方法

解析:
1 虚函数表 占一个指针的空间 即 4 virtualvoidmem_fun(){}
2 charm_chData; 1 + 3 对齐 4
3 intm_nData; 4
4 static的成员变量属于类域,不算入对象中 0 staticchars_chData;

1 虚函数表 占一个指针的空间 即 4 virtual void mem_fun(){}
2 char m_chData; 1
3 int m_nData; 4

9.重载与重写

重载在一个类中,参数必须不一致,返回值可以不一致
重写子类方法覆盖父类的,继承过来的,所以返回值类型必须一致或相容(子类)

虚函数重写

class A
{
public:
 void FuncA()
 {
     printf( "FuncA called\n" );
 }
 virtual void FuncB()
 {
     printf( "FuncB called\n" );
 }
};
class B : public A
{
public:
 void FuncA()
 {
     A::FuncA();
     printf( "FuncAB called\n" );
 }
 virtual void FuncB()
 {
     printf( "FuncBB called\n" );
 }
};
void main( void )
{
 B  b;
 A  *pa;
 pa = &b;
 A *pa2 = new A;
 pa->FuncA(); ( 3) 
 pa->FuncB(); ( 4)
 pa2->FuncA(); ( 5)
 pa2->FuncB();
 delete pa2;
}
// 
pa->FuncA(); ( 3)//pa=&b动态绑定但是FuncA不是虚函数,所以FuncA called
 pa->FuncB(); ( 4)//FuncB是虚函数所以调用B中FuncB,FuncBB called  
 pa2->FuncA(); ( 5)//pa2是A类指针,不涉及虚函数,调用的都是A中函数,所以FuncA called FuncB called
 pa2->FuncB()

考察父类指针指向子类

10.指针

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 );
}  
答案:XYBCDCBA1BAA

解析:FindSubString() 函数就是要找到一个先递增再递减且递增和递减的数量相等的回文序列,例如: ABCDCBA ,先是 后一项 = 前一项 ASCII 码 +1 , 后是 后一项 = 前一项 ASCII 码 -1 ,才能返回回文子串的长度,否则返回 0 。
ModifyString() 函数不断寻找上述类型的子串,如果不满足条件,就
*p2++ = *p1++;
当遇到 ABABA 中前一个 ABA 的时候,满足回文子串要求,此时 p1 指向 A BABA , p2 指向 ABABA ; sprintf 重定向修改 ABABA , B 变为 1 ,且跟随一个 ‘\0’ (该函数自动产生的) , 此时,字符串变为 A1‘\0’BA 。
经过 while ( *p2 != '\0' ) 循环之后, p2 指向 A1‘\0’BA , p1 += count + count + 1 之后, p1 指向 A1‘\0’BA 。此时字符串已经被改动,之前的 ABABA 已经不存在,变为 A1‘\0’BA 。

再次进入 while ( *p1 != '\0' ) 循环之后,只能执行 else 部分的命令, p1 指向 p2 指向的元素的后一个,不断将 p1 指向的元素传给 p2 所指向的位置,将原数据覆盖。所以, A1‘\0’BA ,依次变为 A1BBA 、 A1BAA 。即最终结果为 XYBCDCBA1BAA 。

11.数组全排列

void perm(int list[], int k, int m)
{
    if (   )//k==m
    {
        copy(list,list+m,ostream_iterator<int>(cout," "));
        cout<<endl;
        return;
    }
    for (int i=k; i<=m; i++)
    {
        swap(&list[k],&list[i]);
        (    );//perm(list,k+1,m)
        swap(&list[k],&list[i]);
    }
}

12.数组作为函数会退化为指针

void example(char acWelcome[]) {
    printf("%d", sizeof(acWelcome));
    return;
}
int main() {
    char acWelcome[] = "Welcome to Huawei Test";
    example(acWelcome);//4(32位系统,64位系统则会输出8)
    printf("\n%d\n", sizeof(acWelcome));//23
    return 0;
}

数组作为函数的参数是会退化为函数指针的,想想看,数组作为函数参数的时候经常是需要传递数组大小的

13.深浅拷贝操作

#include<iostream>
using namespace std;
class MyClass
{
public:
    MyClass(int i = 0)
    {
        cout << i;
    }
    MyClass(const MyClass &x)
    {
        cout << 2;
    }
    MyClass &operator=(const MyClass &x)
    {
        cout << 3;
        return *this;
    }
    ~MyClass()
    {
        cout << 4;
    }
};
int main()
{
    MyClass obj1(1), obj2(2);
    MyClass obj3 = obj1;
    return 0;
}

解析:
关键是区分 浅/深拷贝操作 和 赋值操作:
没有重载=之前:
A a ;
A b;
a = b;
这里是赋值操作。
A a;
A b = a;
这里是浅拷贝操作。
重载 = 之后:
A a ;
A b;
a = b;
这里是深拷贝操作(当然这道题直接返回了,通常我们重载赋值运算符进行深拷贝操作)。
A a;
A b = a;
这里还是浅拷贝操作。
所以 MyClass obj3 = obj1; 调用的是拷贝构造函数。
如果写成 MyClass obj3; obj3 = obj1; 输出的结果就是 1203444

上一篇下一篇

猜你喜欢

热点阅读