【牛客网】C/C++牛客网专项刷题(00)

以下为牛客网C/C++专项刷题:


1、若要打开A盘上user子目录下名为abc.txt的文本文件进行读、写操作,符合此要求的函数调用是( )。

KEY:fopen("A:\\user\\abc.txt","r+")

解析:要注意文本文件的“\”要用转义


2、运行这段程序的运行结果:

#include <stdio.h>
main( )
{ 
    char c[2] [5]={ "6934", "8254"},* p[2];
    int i, j, s=0;
    for( i=0;i<2;i+ + ) 
        p[i] = c[i];
    for( i=0;i<2;i+ + )  
    for( j=0;p[i][j]>0 && p[i][j]<= '9';j+=2)
      s = 10 * s + p[i][j] - '0';
    printf("%d\n",s);
} 

KEY:6385

解析:这道题很简单。只需要注意以下:p此时是一个指针数组,不是数组指针!


3、请问p1+5= 什么?p2+5= 什么?

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

KEY:801005   810014

解析: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、运行这段程序的运行结果:

void main()
{
    int i = 11;
    int const *p = &i;
    p++;
    printf(“ % d”, *p);
}

KEY:Garbage value(垃圾值)

解析:不是Compile error。考的 int const *p , const int *p 以及 int * const p 的区别,前两个意思是一样的表示*p的内容不能修改,最后那个指的指针p是个常变量,不能修改它的值。也就是说,本题中p为一个指向const int的指针,p++后,p指向的地址内容不确定。

如果本题是(*p)++,那么就是Compile error。


5、#include命令的功能是()。

KEY:在命令处插入一个文本文件

解析:不是在命令处插入一个头文件。预处理器发现#include后,就会寻找指令后面<>中的文件名,并把这个文件的内容包含到当前的文件中,被包含的文件中的文本将替换源代码文件中的#include指令。


6、现在希望定义一个Head类,也想实现Look的功能,应该使用()方法,实现代码重用。

class Eye
{
    public:
    void Look(void);
};

KEY:组合

解析:组合是在新类中以原有类的对象作为数据成员,继承是在不改变现有的类的基础上,采用现有类的形式并在其中添加新代码,组合一般用于在新类中使用现有类的功能而不是他的接口的情况,就是新类用户看到的只是为新类所定义的接口。而继承则是用于在新类需要向基类转化的情况(多态),这也是组合和继承使用的最清晰的判断方法。

结合本题分析,我们只需让眼睛作为头的一个成员即可,而让头去继承眼睛的特性是没有必要的。


7、运行这段程序的运行结果:

#include <stdio.h>
main()
{
    FILE*fp;
    int i,a[6]={1,2,3,4,5,6};
    fp=fopen("d2.dat","w+");
    for(i=0;i<6;i++)fprintf(fp,"%d\n",a [i]);
    rewind(fp);
    for(i=0;i<6;i++)fscanf( fp,"%d",&a[5-i]);
    fclose(fp);
    for(i=0;i<6;i++)printf("%d,",a [i]);
}

KEY:6,5,4,3,2,1,

解析:主要是介绍一下fprintf函数和fscanf函数。


8、运行这段程序的运行结果:

int fun(int value)
{
	int cnt=0;
	while(value)
	{
		cnt++;
		value=value&(value-1);
	}
	return cnt;
}
void main(void)
{
	printf("%d",fun(65535));
}

KEY:16

解析:x&(x-1)的作用是对一个数中二进制1的个数进行统计;x|(x+1)的作用是对一个数中二进制0的个数进行统计。


9、final修饰符(关键字):如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承。因此一个类不能既被声明为 abstract的,又被声明为final的。将变量或方法声明为final,可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。

finally:异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常,那么相匹配的 catch 子句就会执行,然后控制就会进入 finally 块(如果有的话)。一般异常处理块需要。

finalize:Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。finalize() 方法是在垃圾收集器删除对象之前对这个对象调用的。 


10、没有重载下面哪个运算符:

std::vector::iterator

KEY:>>

解析:对于std::vector::iterator,

++、--用于双向迭代,迭代器最基本的功能;

*用于复引迭代器,用于引用迭代器对应的元素,也是基本操作;

==用于判断两个迭代器是否相等,迭代的时候需要判断迭代器是否到某个位置。


11、b的十六进制是什么,c==a为真么?

signed char a=0xe0;
unsigned int b=a;
unsigned char c=a;

KEY:0xffffffe0  假

解析:首先,先来看a是什么?a=0x1110_0000,有符号数最高位为1,所以a为负数;在计算机里用补码存储数值,负数原码转补码是符号位不变,其他各位取反,然后加1;无符号数的原,反,补码一样。这样一算,得到a化为int,其实是-32。

接下来,将signed char(负数)转化为unsigned int:a的原码为1010_0000(-32);将一个负数转化为unsigned int ,首先将符号位移动,最后使用补码即可。10000000_00000000_00000000_00100000。转化为补码ffffffe0。也就是说,这里涉及到一个整型提升。

最后,将signed char转化为unsigned char,直接将0xe0给c,c是一个比较大的正数。

总结起来:整型提升是C程序设计语言中的一项规定,在表达式计算时(包括比较运算、算术运算、赋值运算等),比int类型小的类型(char, signed char, unsigned char, short, unsigned short等)首先要提升为int类型,然后再执行表达式的运算。

至于提升的方法,是根据原始类型进行位扩展(如果原始类型为unsigned char,进行零扩展,如果原始类型为signed char,进行符号位扩展)到32位。也就是说:

  • 对于unsigned char,进行零扩展,即在左边的高位用 0 填充至32位;
  • 对于signed char,进行符号位扩展。如果其符号位为0,则左边的高位用 0 填充至32位;如果其符号位为1,则左边的高位用 1 填充至32位。


12、C ++处理异常的机制是由()三部分组成。

KEY:检查、抛出和捕获。


13、 具体来说,全局变量和局部变量的区别如下:

  • 作用域不同:全局变量的作用域为整个程序,而局部变量的作用域为当前函数或循环等;
  • 内存存储方式不同:全局变量存储在全局数据区中,局部变量存储在栈区;
  • 生命期不同:全局变量的生命期和主程序一样,随程序的销毁而销毁,局部变量在函数内部或循环内部,随函数的退出或循环退出就不存在了;
  • 使用方式不同:全局变量在声明后程序的各个部分都可以用到,但是局部变量只能在局部使用。函数内部会优先使用局部变量再使用全局变量。


14、判断:C 语言本身有输入输出语句。

KEY:错误。

解析:C语言本身并不提供输入输出语句,输入和输出操作都是有函数实现的。也就是说printf和scanf并不是C语言的关键字,而只是函数的名字,他们不是C语言文本中的组成成分。


15、请问sizeof(B)的值为:

    class A
{
public:
    int fun1();
    virtual void fun2();
private:
    int _a1;
    static int _a2;
};
class B: public A
{
public:
    virtual void fun2();
};

KEY:8

解析:静态变量在全局区存放,在类域中为所有对象共享,不属于任何对象。虚函数表指针占一个int大小空间,继承的int型变量,总共8字节。全面总结sizeof的用法(定义、语法、指针变量、数组、结构体、类、联合体、位域位段)


16、STL中的unordered_map和priority_queue使用的底层数据结构分别是什么?()

KEY:hashtable,heap


17、若要重载+、=、<<、==和[]运算符,则必须作为类成员重载的运算符是()

KEY:=和[]

解析:

只能使用成员函数重载的运算符有:=、()、[]、->、new、delete;(类型转换函数也是)

单目运算符最好重载为成员函数;

对于复合的赋值运算符如+=、-=、*=、/=、&=、!=、~=、%=、>>=、<<=建议重载为成员函数;

对于其它运算符,建议重载为友元函数。

运算符重载的方法是定义一个重载运算符的函数,在需要执行被重载的运算符时,系统就自动调用该函数,以实现相应的运算。也就是说,运算符重载是通过定义函数实现的。运算符重载实质上是函数的重载。重载运算符的函数一般格式如下:

          函数类型 operator 运算符名称 (形参表列)
          { 
          对运算符的重载处理
          }

重载为类成员函数时参数个数=原操作数个数-1(后置++、--除外);

重载为友元函数时 参数个数=原操作数个数,且至少应该有一个自定义类型的形参。


18、在16位IBM-PC上,若有下面的说明和定义,则sizeof(struct test)的值是()。

struct test 
{ 
    int m1;char m2;float m3; 
    union uu {char u1[5];int u2[2];} ua; 
} myaa; 

KEY:12

解析:整型变量在16位机中占2字节,字符型变量占1字节,浮点型变量占4字节


19、在 C 语言中,一维数组的定义方式为:元素类型 数组名[E];其中E 为()

KEY:整型常量表达式

在C语言中,引用数组元素时,其数组下标的数据类型允许是()。

KEY:整型常量或整型表达式


20、关于下列操作哪个复杂度为O(1)?

vector<>中插入元素(动态数组)

set中查找元素

hash_map中查找元素

deque尾部删除元素

KEY:CD

解析:vector基于数组,那么最糟糕要O(n),set查找,基于树,最糟糕要O(logN),hash_map用hash值映射,计算一次就ok,也就是O(1),deque队列,把尾部指针向next走一个就ok,O(1)。


21、以下代码中,A 的构造函数和析构函数分别执行了几次:  

A*pa=new A[10];
delete []pa;

KEY:10,10


22、判断是否正确:在C++中,inline函数表示编译器一定会将函数体直接插入到调用此函数的地方,这样可以加快程序的运行速度。

KEY:错误。

解析:内联函数与其他普通函数的区别是,内联函数在生成可执行文件时,其代码块是直接嵌入到调用处的,以此减少函数调用的开销提高程序性能,它与宏很类似。

但是,C++ primer 上明明白白的写过这么一句话:内联说明(inline specification)对于编译器来说只是一个建议,编译器可以选择忽略这个建议。也就是说,是否以嵌入式方式存在,是由编译器决定的,并不是一定。

内联展开的目的是为了节省函数调用时候的开销,一般来说内联机制用于优化规模较小、流程直接、频繁调用的函数。一般如果内联函数较长,编译器是会忽略的。


23、下列哪两个是等同的

int b;

A、const int *a = &b;

B、const * int a = &b;

C、const int* const a = &b;

D、int const* const a = &b;

KEY:C、D

解析:int const *a 和 const int *a 意义相同,作用等价,所以选择C、D。

const int *a、int const *a:含义是 a 是指针,指向 int,这个int 是静态的,const 修饰的是int;

int* const a:含义为 a 是静态的指针,指向int,也就是说,不能赋给a其他的地址值,但可以修改a指向的值;

const * int a:语法错误;

const int* const a、int const* const a :a 是静态的指针(第二个const 修饰),指向 int ,这个int是静态的(第一个const 修饰)。


24、以下代码编译有错误,哪个选项能解决编译错误?

class A {
    public:
        int GetValue() const {
            vv = 1;
            return vv;
         }
    private:
        int vv;
};

改变成员变量"vv"为"mutable int vv";

改变成员函数"GetValue"的声明,以使其不是const的。

KEY:A、B

解析:mutalbe的中文意思是“可变的,易变的”,跟constant(既 C ++中的const)是反义词。 

在C++中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中。 

我们知道,如果类的成员函数不会改变对象的状态,那么这个成员函数一般会声明成const的。但是,有些时候,我们需要在const的函数里面修改一些跟类状态无关的数据成员,那么这个数据成员就应该被mutalbe来修饰。


25、下面有关c++ traits,说法正确的有?

一个traits包括了enum、typedef、模板偏特化(template partial specialization);

typedef定义了各个类的各自不同的类型定义;

模板偏特化用于实现各个类的不同功能;

当函数,类或者一些封装的通用算法中的某些部分会因为数据类型不同而导致处理或逻辑不同,traits会是一种很好的解决方案。

KEY:A、B、C、D

解析:traits是C++的自动类型判断。

出发点:因为C++没有反射的机制。所以利用traits来完成。

大量使用场景:STL(STL中大量使用traits来区分类别。注释POD标量类型和类类型的构造函数等)。


26、要求打开文件D:\file.dat,并能够写入数据,正确的语句是( )。

ifstream infile("D:\\file.dat", ios::in );

ifstream infile("D:\\file.dat", ios::out );

ofstream outfile("D:\\file.dat", ios::in );

fstream infile("D:\\file.dat", ios::in|ios::out );

KEY:D


27、以下程序的输出结果为:

#include "stdio.h"
int func(int x, int y)
{
    return (x + y);
}
int main()
{
    int a = 1, b = 2, c = 3, d = 4, e = 5;
    printf(" %d\n", func((a + b, b + c, c + a), (d, e)));
     
    return 0;
}

KEY:9


28、以下关于纯虚函数的说法,正确的是()

声明纯虚函数的类不能实例化

声明纯虚函数的类成虚基类

子类必须实现基类的

纯虚函数必须是空函数

KEY:A


29、64位机上,一个结构体有三个成员,分别是char、int、short类型,三个成员位于结构体中不同位置时整个结构体的大小可能是哪些值?

KEY:8、12

解析:先看一个例子:

#include<iostream>
using namespace std;
struct A{
    char a;
    int b;
    short c;
};

struct B{
    short c;
    char a;
    int b;
};

int main(){
    cout<<sizeof(A)<<endl;
    cout<<sizeof(B)<<endl;
    return 0;
}

上面两个结构体A和B成员变量类型相同,但是占用的内存空间大小(单位:字节)却不一样

sizeof(A) = 12  sizeof(B) = 8

问题出来了,这两个一样的结构体,为什么sizeof的时候大小不一样呢?下面就是解释明白这一问题。

内存对齐,正是因为内存对齐的影响,导致结果不同。

对于大多数的程序员来说,内存对齐基本上是透明的,这是编译器该干的活,编译器为程序中的每个数据单元安排在合适的位置上,从而导致了相同的变量,不同声明顺序的结构体大小的不同。

那么编译器为什么要进行内存对齐呢?上面程序中的结构体按常理来理解结果都应该是7,因为4(int) + 2(short) + 1(char) = 7 。经过内存对齐后,结构体的空间反而增大了。

为了分析造成这种现象的原因,我们不得不提及内存对齐的3大规则:

  • 对于结构体的各个成员,第一个成员的偏移量是0,排列在后面的成员其当前偏移量必须是当前成员类型的整数倍;
  • 结构体内所有数据成员各自内存对齐后,结构体本身还要进行一次内存对齐,保证整个结构体占用内存大小是结构体内最大数据成员的最小整数倍;
  • 如程序中有#pragma pack(n)预编译指令,则所有成员对齐以n字节为准(即偏移量是n的整数倍),不再考虑当前类型以及最大结构体内类型。

以上面结构体A为例,第一个成员a是char类型,占用1个字节空间,偏移量为0;第二个成员b是int类型,占用4个字节空间,按照规则1,b的偏移量必须是int类型的整数倍,而b的偏移量是1(char占用的1个字节),所以编译器会在a变量后面插入3字节缓冲区,保证此时b的偏移量(4字节)是b类型的整数倍(当前恰好是1倍);第3个成员c为short类型,此时c的偏移量正好是4+4=8个字节,已经是short类型的整数倍,故b与c之间不用填充缓冲字节。

但这时,结构体A的大小为8+2=10个字节,按照规则2,结构体A大小必须是其最大成员类型int的整数倍,所以在10个字节的基础上再填充2个字节,保证最后结构体大小为12,以符合规则2。

数据成员-	-前面偏移量-	- 成员自身占用
(char) a	    0               1
缓冲补齐	            1	            3(规则1)
(int) b	            4	            4
(short) c	    8	            2
缓冲补齐	10	2(规则2)

类似的,结构体B成员的分析如下:

数据成员-	-前面偏移量-	-成员自身占用
short c	            0	            2
char a	            2	            1
缓冲补齐	            3	            1(规则1)
int b	            4	            4

如果还不太懂的,可以参考链接:内存对齐规则之我见或者内存对齐的规则以及作用


30、关于函数模板,描述错误的是? 

函数模板必须由程序员实例化为可执行的函数模板

函数模板的实例化由编译器实现

一个类定义中,只要有一个函数模板,则这个类是类模板

类模板的成员函数都是函数模板,类模板实例化后,成员函数也随之实例化

KEY:A、C、D

解析:就相当于泛型。


31、已有定义int x;float y;且执行scanf(“%3d%f”,&x,&y);语句时,假设输入数据为:

12345□678↙,则x、y的值分别为()。

12345  678.000000

123  678.000000

123  45.678000

123   45.000000

KEY:D

解释:因为%3d,所以输入中的前3位数123将赋给x,输入中后面的45将赋给y,因为45后键入了空格使得后面的输入无效,又因y是浮点数,所以y的值应为45.000000。


32、下列运算符中优先级最高的是()。

<

+

&&

!=

KEY:B

解释:算术运算符的优先级高于关系运算符,关系运算符的优先级高于逻辑运算符。


33、以下程序的输出结果是:

void  main()
{
    int x = 0, y = 5, z = 3;
    while(z-->0 && ++x<5)
    {
        y= y - 1;
    }
    printf("%d, %d, %d\n", x, y, z);
}

KEY:3,2,-1

解释:第一次:z=3>0,x=1<5 执行循环,x=1,y=4,z=2;

第二次:z=2>0,x=2<5执行循环,x=2,y=3,z=1;

第三次:z=1>0,x=3<5执行循环,x=3,y=2,z=0;

第四次:z=0不满足>0,判断后z--后z=-1,++x<5被短路,循环也不执行;

最终 x=3,y=2,z=-1 。


34、这段程序的运行结果为:

#include "stdio.h"
class Base
{
public:
    Base()
    {
        Init();
    }
    virtual void Init()
    {
        printf("Base Init\n");
    }
    void func()
    {
        printf("Base func\n");
    }
};
class Derived: public Base
{
public:
    virtual void Init()
    {
        printf("Derived Init\n");
    }
    void func()
    {
        printf("Derived func\n");
    }
};
 
int main()
{
    Derived d;
    ((Base *)&d)->func();
     
    return 0;
}

KEY:Base Init  Base func

解释:在构造函数不要调用虚函数。在基类构造的时候,虚函数是非虚,不会走到派生类中,既是采用的静态绑定。显然的是:当我们构造一个子类的对象时,先调用基类的构造函数,构造子类中基类部分,子类还没有构造,还没有初始化,如果在基类的构造中调用虚函数,如果可以的话就是调用一个还没有被初始化的对象,那是很危险的,所以C++中是不可以在构造父类对象部分的时候调用子类的虚函数实现。但是不是说你不可以那么写程序,你这么写,编译器也不会报错。只是你如果这么写的话编译器不会给你调用子类的实现,而是还是调用基类的实现。

在析构函数中也不要调用虚函数。在析构的时候会首先调用子类的析构函数,析构掉对象中的子类部分,然后在调用基类的析构函数析构基类部分,如果在基类的析构函数里面调用虚函数,会导致其调用已经析构了的子类对象里面的函数,这是非常危险的。

  • Derived d;首先创建对象d,找到基类的构造函数Base(){Init();},此时需要调用Init()函数,不要被virtual迷惑,这只是普通的调用函数,虚函数不起作用,所以调用的还是基类的Init(),输出Base Init;
  • ((Base *)&d)->func(),虽然是动态联编,但是func()不是虚函数,Base*指针指向派生类不起作用,执行的是基类的func(),输出Base func。


35、print()函数是一个类的常成员函数,它无返回值,下列表示中正确的是()

const void print();

void const print();

void print() const;

void print(const);

KEY:C

解释:const void print(const int num)const 第一个const修饰返回类型 第二个const修饰参数 第三个const修饰调用对象。

也就是说:const修饰类的成员函数,一般放在函数体后,如 void fun() const; 

常成员函数声明:形如 void funcName(参数表) const; 

对于常成员函数需要注意:

  • 其实现部分也要带const关键字;
  • 不能修改类的成员变量,不能调用类中没有被const修饰的成员函数(即只能调用常成员函数)。

猜你喜欢

转载自blog.csdn.net/qq_38410730/article/details/80445109