C++笔试题目练习

简介

这是我在笔试过程中通过牛客网进行模拟的笔试题目,这些题目很有意思也很锻炼人,写在博客中以方便后继的复习。
其中这些题目的题解全都来自于笔试下面评论区的题解,侵删。

题目

1

下面的程序可以从0…n-1中随机等概率的输出m个不重复的数。这里我们假设n远大于m

knuth(int n, int m)
{ 
    srand((unsigned int)time(0)); 
    for (int i = 0; i < n; i++) { 
        if ( ) { 
            cout << i << endl;
            ( );
        }
     }
}

A、rand()%(n-i)<=m   m--
B、rand()%(n-i)<m   m--
C、rand()%(n-i)>=m   m++
D、rand()%(n-i)>m   m++

解:为了方便解释假设n等于10,m等于5:
第一次rand()%(n-0)的余数范围是0~9,有可能小于m(=5),可以输出i=0;随后i++,m–
第二次rand()%(n-1)的余数范围是0~8,有可能小于m(=4),可以输出i=1;随后i++,m–

第五次rand()%(n-4)的余数范围是0~5,有可能小于m(=1),可以输出i=4;随后i++,m–得到i=5,m=0

第六次rand()%(n-5)的余数范围是0~4,不可能小于m(=0),算法结束。

倘若rand()%(n-i)<=m,则第六次还满足条件,意味着多输出的一次;

而rand()%(n-i)>=m,将会因为判断条件不满足而提早退出。因此选B

2

函数外部访问x等于什么?

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

A、5
B、12
C、0
D、随机值

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

3

unsigned char *p1;
unsigned long *p2;
p1=(unsigned char *)0x801000;
p2=(unsigned long *)0x810000;

A、801005   810005
B、801010   810014
C、801005   810014
D、801010  810015

请问p1+5= 什么?
p2+5= 什么?
解:
1代表的是一个单位量
p1+5=p1+51=p1+5sizeof(unsigned char)=p1+51=0x801000+ox5=0x801005
p2+5=p2+5
1=p2+5sizeof(unsigned long)=p1+54=0x810000+20=0x810000+0x14=0x810014
最后要转换成16进制

4

在32位机器中,如下代码:

void example(char acWelcome[]){
    printf("%d",sizeof(acWelcome));
    return;
}
void main(){
    char acWelcome[]="Welcome to Huawei Test";
    example(acWelcome);
    return;
}

A、0
B、4
C、23
D、24

的输出是?
解:这题的正确答案应该是32位系统是4 64位系统是8,数组作为函数的参数是会退化为函数指针的,想想看,数组作为函数参数的时候经常是需要传递数组大小的

5

下面程序运行后的结果为:

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

A、result is glad to test something
B、result is ad to test something
C、result is test something
D、result is to test something

解:
charstr[] = “glad to test something”; //定义字符串

char*p = str; //p指向字符串首地址,即字符’g’

p++; //p是char*类型,每次移动sizeof(char)字节,故此时p指向 'g’的下一个字符 ‘l’

intp1 = reinterpret_cast<int>§; //指针p被重新解释为整型指针并被赋值给p1

p1++; //p1是int*类型, 每次移动sizeof(int)字节,故此时p1 指向 'l’后的第四个字符 ‘t’

p = reinterpret_cast<char*>(p1); //指针p1被重新解释为字符型指针并被赋值给p

printf(“result is %s\n”, p); //从’t’开始输出字符串,即得到 “to test something”

6

设已经有A,B,C,D4个类的定义,程序中A,B,C,D析构函数调用顺序为?

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

A、A B C D
B、A B D C
C、A C D B
D、A C B D

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

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

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

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

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

7

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

A、16 4
B、16 10
C、12 9
D、10 10

问:
(1)若按4字节对齐sizeof(CTest)的值是多少?
(2)若按1字节对齐sizeof(CTest)的值是多少?
请选择正确的答案。
解:
若按4字节对齐sizeof(CTest)的值是12;
若按1字节对齐sizeof(CTest)的值是9
解释:
在类中,如果什么都没有,则类占用1个字节,一旦类中有其他的占用空间成员,则这1个字节就不在计算之内,如一个类只有一个int则占用4字节而不是5字节。

如果只有成员函数,则还是只占用1个字节,因为类函数不占用空间

虚函数因为存在一个虚函数表,需要4个字节,数据成员对象如果为指针则为4字节,注意有字节对齐,如果为13字节,则进位到16字节空间。

sizeof的本质是得到某个类型的大小,确切的来说就是当创建这个类型的一个对象(或变量)的时候,需要为它分配的空间的大小。而类也可以理解为类似于int、float这样的一种类型,当类中出现static成员变量的时候,static成员变量是存储在静态区当中的,它是一个共享的量,因此,在为这个类创建一个实例对象的时候,是无需再为static成员变量分配空间的,所以,这个类的实例对象所需要分配的空间是要排除static成员变量的,于是,当sizeof计算类的大小的时候会忽略static成员变量的大小

8

写出下面程序的输出结果

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

A、FuncA called
   FuncB called
   FuncA called
   FuncB called
B、FuncA called
   FuncBB called
   FuncA called
   FuncB called
C、FuncA called
   FuncBB called
   FuncAB called
   FuncBB called
D、FuncAB called
   FuncBB called
   FuncA called
   FuncB called

解:
选B
B b;
A *pa;
pa = &b;
A *pa2 = newA;
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()

9

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

A、XYBCDCBABABA
B、XYBCBCDA1BAA
C、XYBCDCBA1BAA
D、XYBCDDBA1BAB

解:
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 。

10

如下代码输出结果是什么?

#include<stdio.h>
char *myString()
{
    char buffer[6] = {0};
    char *s = "Hello World!";
    for (int i = 0; i < sizeof(buffer) - 1; i++)
    {
        buffer[i] = *(s + i);
    }
    return buffer;
}
int main(int argc, char **argv)
{
    printf("%s\n", myString());
    return 0;
}
A、Hello
B、Hello World!
C、Well
D、以上全部不正确


解:答案:D
函数char *myString()中没有使用new或者malloc分配内存,所有buffer数组的内存区域在栈区
随着char *myString()的结束,栈区内存释放,字符数组也就不存在了,所以会产生野指针,输出结果未知

11

#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、11214444
B、11314444
C、122444
D、123444

解:
拷贝构造函数发生在对象还没有创建,需要创建时,如obj3;赋值操作符重载仅发生在对象已经执行过构造函数,即已经创建的情况下

前两个对象构造时分别输出1,2
第三个对象是这样构造的MyClass obj3 = obj1,之前没有执行过构造函数创建对象,所以这里会调用拷贝构造函数,输出2
然后三个对象依次析构,输出444
所以最终输出122444

12

下面程序的功能是输出数组的全排列。请填空。

#include<iostream>
#include<iterator>
using namespace std;

//1、关键:先想函数作用:这个函数作用是输出从第k个位置到第m个数(第m-1个位置)的全排列 
void perm(int list[], int k, int m)
{
    if (k==m)
    {
        copy(list,list+m,ostream_iterator<int>(cout," ")); //拷贝迭代器,作用是将List中的元素逐个输出   
        cout<<endl;
        return;
    }
 
    for (int i=k; i<m; i++)  //初始状态 k=0,m=4; 
    {
        swap(list[k],list[i]); //i从第k个位置开始,一直到第m-1个位置,交换他们的值。假设函数已经执行完i=0了,此时k=0,i=1,交换后list成2134 
        perm(list,k+1,m); 
        //这时候执行函数(作用上面写了):对于数组2134,输出第k+1个位置到第m-1个位置的全排列,即操作(list[1]到list[3]的全排列),也就是此时的list[0] = 2不变,后面三个数134的全排列 
        //而输出134的全排列依然是用到perm函数,再按函数作用来理解,1不变的时候输出34全排列,3不变的时候...4不变的时候...etc 
        //如果递归到k==m,此时第k个位置到第m-1个位置范围不存在,这时候函数就把刚才经过一系列交换操作得到的数组输出
        swap(list[k],list[i]); //经过一系列操作之后得到了以list[1]=2开头的全排列,这个时候交换回来,list还得是原来的list1234,此时i++,再进行list[2]=3作为开头的一系列操作。 
    }
}

int main(){
    
    int list[4] = {1,2,3,4};
    perm(list,0,4);    
    
    cout<<"---------"<<endl;
    perm(list,2,4); //为了便于理解函数作用,加一个这个,则输出 1234与1243 
    
}

A、k!=m 和 perm(list,k+1,m)
B、k==m 和 perm(list,k+1,m)
C、k!=m 和 perm(list,k,m)
D、k==m 和 perm(list,k,m)

转载至:https://www.nowcoder.com/test/question/done?tid=29978838&qid=15225#summary

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值