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

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


1、下面程序会输出什么:

static int a=1;
void fun1(void){    a=2;  }
void fun2(void){    int a=3;  }
void fun3(void){   static int a=4;  }
int main(int argc,char** args){ 
     printf(“%d”,a); 
     fun1( ); 
     printf(“%d”,a); 
     fun2( ); 
     printf(“%d”,a);
     fun3( );
      printf(“%d”,a); 
}

KEY:1 2 2 2

解析:静态局部变量只对定义它的函数体始终可见,函数体执行完过后虽然还存在,但是无法被其他的使用了。


2、下面程序会输出什么:

int main()
{
    bool first=true;
    int sum=0;
    int value;
    unsigned short i=0xFFFF;
    for (;i>=0;--i)
    {
        if (first)
        {
            value=65536;
            sum+=value%3;
            first=false;
        }
        else{
            sum+=--value%3;
            if (value<=0)
            {
                cout<<sum<<","<<i;
                break;
            }
        }
    }
    return 0;
}

KEY:65536,65535

解析:for(;i>=0;--i)由于是i是unsigned无符号,所以并不是i<0退出循环,是当i=0后下一个i的值从最初的0xffff(65535)从头再开始(无法表示-1),也就是说如果for循环里没有break即死循环。-1 = 1000 0000 0000 0001B = 补码:1111 1111 1111 1111 = 无符号则为0xffff,存储方式为补码形式。


3、下面有关C++的类和C里面的struct的描述,正确的有?

在C++中,来自class的继承默认按照private继承处理,来自struct的继承默认按照public继承处理

class的成员默认是private权限,struct默认是public权限

c里面的struct只是变量的聚合体,struct不能有函数

c++的struct可有构造和析构函数

KEY:A、B、C、D


4、以下正确的说法是(        ),在C语言中。

实参和与其对应的形参各占用独立的存储单元

实参和与其对应的形参共占用一个存储单元

只有当实参和与其对应的形象同名时才共占用存储单

形参是虚拟的,不占用存储单元

KEY:A

解释:形参在编译时是不会分配存储容间,在调用时才在栈里分配,在调用结束时,即刻释放所分配的内存单元。


5、程序运行后的输出结果是?

#include <stdio.h>
main(){
    int i=0;
    i=~i;
    printf("%d\n",i);
}

KEY:-1

解释:0 = 00000000000000000000000000000000 ,取反11111111111111111111111111111111,就是-1在计算机的存储形式。

计算机内, 负数以反码形式存储, 符号位不变, 源码求反加1, 就是反码。

11111111111111111111111111111111就是

10000000000000000000000000000001求反

11111111111111111111111111111110 加1 

就是 11111111111111111111111111111111。


6、程序运行后的输出结果是?

enum {
    a, b=5, c, d=4, e
} k;
k =c;

KEY:6

解释:enum中:首元素不赋值的话,默认为0;后一个元素不赋值的话比前一个元素大1。


7、关于“深拷贝”,下列说法正确的是:

会拷贝动态分配的成员对象

会拷贝成员数据的值

会拷贝静态分配的成员对象

KEY:A

解释:

  • 深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响;
  • 浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同)。对其中任何一个对象的改动都会影响另外一个对象。

B和C都算错在了一个地方,没有考虑静态成员。类的静态成员是所有类的实例共有的,存储在全局(静态)区,只此一份,不管继承、实例化还是拷贝都是一份。


8、有哪几种情况只能用intialization list 而不能用assignment?

当类中含有const成员变量

基类无默认构造函数时,有参的构造函数都需要初始化。

当类中含有reference成员变量

当类中含有static成员变量

KEY:A、B、C

解析:因为const对象以及引用只能初始化而不能赋值,所以只能使用成员初始化列表。

对于非内置类型,在进入函数体之前,如果没有提供显式初始化,会调用默认构造函数进行初始化。若没有默认构造函数,则编译器尝试调用默认构造函数将会失败,所以如果没有默认构造函数,则必须在初始化列表中显示的调用构造函数。

static 成员在执行构造函数前就已经构造好了,即使不存在类对象,也可以被使用,不需要初始化列表。

9、若有说明:int*p,m=5,n;则以下正确的程序段是()。

p=&n;scanf("%d",&p);

p=&;scanf("%d",*p)

scanf("%d”,&n); *p=n;

p=&n;*p=m;

KEY:D

解释:因p是指针变量,在scanf中不能再使用&p或*p来接收键盘的输入值,应直接用p即可。


10、在下列关于类型转换的描述中,错误的是( )。

任何形式的构造函数都可以实现数据类型转换。

带非默认参数的构造函数可以把基本类型数据转换成类类型对象。

类型转换函数可以把类类型对象转换为其他指定类型对象。

类型转换函数只能定义为一个类的成员函数,不能定义为类的友元函数。

KEY:A

解析:转换构造函数的作用是将一个其他类型的数据转换成一个类的对象。注意:转换构造函数只能有一个参数。如果有多个参数,就不是转换构造函数。原因是显然的,如果有多个参数的话,究竟是把哪个参数转换成类的对象呢?

类型转换函数是类中定义的一个成员函数。operator和“转换后的类型”一起构成转换函数名。该函数不能带有参数,也不能指定返回值类型。因为它的返回值类型就是“转换后的类型”。转换函数的作用就是将对象内的成员数据转换成某种特定的类型。类型转换函数只能定义为一个类的成员函数,不能定义为类的友元函数。


11、哪个选项可以将t初始化为当前程序的运行时间?

time_t t;

t = clock();

time( &t );

time( t );

t = localtime();

KEY:A

解释:clock()就是该程序从启动到函数调用占用CPU的时间;time( &t );为获取系统时间;localtime(&t);  将一个UTC时间转为本地时间。


12、从运行层面上来看,从四个选项选出不同的一个。

JAVA

Python

objectC

C#

KEY:B

解释:Java,C,C#是静态语言,需要编译;python时动态语言,不需要编译。


13、一个C语言程序是由()。

一个主程序和若干子程序组成

函数组成

若干过程组成

若干子程序组成

KEY:B

解释:C语言程序是由函数构成的,所谓函数是指功能相对独立的可以反复执行的。一段程序,在某些程序设计语言中也称为过程,但C语言中叫函数。


14、正确的输入语句是______。

int b;
char c[10];

scanf("%d%s",&b,&c);

scanf("%d%s",&b,c);

scanf("%d%s",b,c);

scanf("%d%s",b,&c);

KEY:A、B

解释: "c" 实际上有两种含义 : 一个指向十个char类型元素的数组、一个char* 类型的指针。

对于第一种情况:

scanf("%s", &c);         //这里c是一个指向十个char元素的数组的指针

对于第二种情况:

scanf("%s", c);         //这里c是一个char* 类型的指针,数组名就是一个地址


15、若给定条件表达式(M)?(a++):(a--),则其中表达式 M()。

和(M==0)等价

和(M==1)等价

和(M!=0)等价

和(M!=1)等价

KEY:C

解释:在C语言中非零数代表true,零代表false。而B选项,只是判断是否等于1,对于2、3等数就不视作真了。


16、假设有说明 int a=0; double x=5.16;,则在以下语句中,(   )属于编译错误。

x=a/x;

x=x/a;

a=a%x;

x=x*a;

KEY:C


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

#include <iostream>
 
using namespace std;
 
class A
{
public:
    virtual void print()
    {
        cout << "A::print()" << "\n";
    }
};
 
class B: public A
{
public: virtual void print()
    {
        cout << "B::print()" << "\n";
    }
};
 
class C: public A
{
public: virtual void print()
    {
        cout << "C::print()" << "\n";
    }
};
 
void print(A a)
{
    a.print();
}
 
int main()
{
    A a, *aa, *ab, *ac;
    B b;
    C c;
    aa = &a;
    ab = &b;
    ac = &c;
    a.print();
    b.print();
    c.print();
    aa->print();
    ab->print();
    ac->print();
    print(a);
    print(b);
    print(c);
}

KEY:A::print() B::print() C::print() A::print() B::print() C::print() A::print() A::print() A::print()

解释:虚函数会具有动态绑定功能,会按照实际类型调用相关的函数。而动态绑定(多态)只有在指针和引用时才有效,其他情况下无效!

  • a.print();   b.print();   c.print();  虽然是虚函数,但不是指针,无效!分别输出A::print() B::print() C::print();
  • aa->print();  ab->print();   ac->print(); 虚函数,指针,所以输出实际对象类型对应的print,因此输出A::print() B::print() C::print();
  • void print(A a){ a.print();}  函数声明的形参为A类型的,相当于强制类型转换,因此调用print(A a)函数的输出都是A::print()。


18、重写(override)与重载(overload)的区别:

  • 函数重写是子类和父类之间的继承关系,是垂直关系;方法的重载是同一个类中方法之间的关系,是水平关系;
  • 重写需要子类和父类中的两个函数的函数原型完全相同;重载要求两个函数参数列表不同;

在重写关系中,调用具体调用哪一个函数是根据(对象对应存储空间的实际类型)为准的,这涉及到动态绑定和静态绑定的问题,也就是虚函数的调用机制,而函数重载主要是靠形参列表的不同来区分具体调用哪个函数的。


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

int func()
{ 
   int i,j,k=0;
   for(i=0,j=-1;j=0;i++,j++)
   {
      k++;
    }
return k;
}
int main()
{
  cout<<(func());
}

KEY:0

解释:先看循环判定条件 ,true则执行,在执行循环体内之前进行的条件判断。而本题的判断条件时j=0。这是什么意思呢?

实际上可以将赋值语句去掉,比如说if语句:if(i=1)实际上就是 i=1;  if(1)。


20、下列说法错误的有( )

在类方法中可用this来调用本类的类方法

在类方法中调用本类的类方法时可直接调用

在类方法中只能调用本类中的类方法

在类方法中绝对不能调用实例方法

KEY:A、C、D

解释:首先明确一点,成员方法又称为实例方法,静态方法又称为类方法。

a,静态方法中没有this指针

c,可以通过类名作用域的方式调用Class::fun();

d,太绝对化了,在类中申请一个类对象或者参数传递一个对象或者指针都可以调用;


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

int func(int a)
{
    int b;
    switch (a)
    {
        case 1: b = 30;
        case 2: b = 20;
        case 3: b = 16;
        default: b = 0;
    }
    return b;
}

KEY:0

解析:func(1)=0,因为没有break语句,switch中会一直计算到b=0。这是提醒我们不要忘了break。


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

#include "iostream"  
#include "vector"  
using namespace std;  
   
int main(void)  
{  
    vector<int>array;  
    array.push_back(100);  
    array.push_back(300);  
    array.push_back(300);  
    array.push_back(500);  
    vector<int>::iterator itor;  
    for(itor=array.begin();itor!=array.end();itor++)  
    {  
        if(*itor==300)  
        {  
            itor = array.erase(itor);  
        }  
    }  
    for(itor=array.begin();itor!=array.end();itor++)  
    {  
        cout<<*itor<<" ";  
    }  
    return 0;  
} 

KEY:100    300     500

解析:vector erase以后,itor已经指向下一个元素了,不应该执行itor++,否则会跳过下一个元素,即连续两个300时跳过了第二个300. 


23、这段程序中,str1和str2的地址相同么?p1和p2指向的地址相同么?

const char str1[] = "abc";
const char str2[] = "abc";
const char *p1 = "abc";
const char *p2 = "abc";

KEY:str1和str2地址不同,P1和P2指向的地址相同

解释: str1和str2是栈空间中的两个字符数组,地址不同;p1和p2指向的位置在常量区,值都是“abc”所以是同一常量,地址相同。


24、在64位系统中,有如下类,那么sizeof(C)的数值是()

class C
{
public:
    char a;
    static char b;
    void *p;
    static int *c;
    virtual void func1();
    virtual void func2();
};

KEY:24

解释:sizeof(类)计算的是类中存在栈中的变量的大小,而类中的b和*c都是static静态变量,存在全局区中,因此不在计算范围之内。于是只剩下char a,void *p和两个virtual虚函数,a是char类型,占用一个字节,p是指针,在64位系统的指针占用8个字节,而两个虚函数只需要一个虚函数表指针,也是八个字节,加上类中的对齐方式(char a对齐时后面补上7个字节),故答案为24。

本题中的虚函数属于同一个类,故只需要一个指针指向虚函数表,所以在64位系统中占用8个字节。就算本题有100个虚函数,那么也只占用8个字节。类与结构一样,都有字节对齐的问题。类中普通的函数不占用类的大小。子类的大小等于子类新增的加上父类的大小。


25、C++语言中,有关类的初始化叙述正确的是()

静态函数中不能出现this指针

可以在类定义时,对对象直接初始化

一般数据类型可以在类的外部进行初始化

静态数据类型可以在类的外部进行初始化

KEY:AD

解释:静态成员变量必须在类外初始化,静态成员常量在类中初始化。


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

int main(int argc,char**argv){
    int a[4]={1,2,3,4};
    int*ptr=(int*)(&a+1);
    printf(“%d”,*(ptr-1));
}

KEY:4

解释:指针的实质为:地址+步长。指针的类型决定了步长。定义了数组a[4],其中a,&a,&a[0]都是数组的起始地址。但是步长有区别,也就是指向的类型不同。

a等同于a+0等同于&a[0],是指向数组第一个元素的指针,步长为指向的元素所占用的地址空间为sizeof(int) ;

&a也是指向数组第一个元素的指针,但其意义是指向整个数组的首地址,指向的类型为整个数组,所以其步长为4*sizeof(int)。

也就是说,&a+1移动了4个int的长度,指向4后面的那个数的地址;ptr-1,移动1个int的长度,指向4。


27、请问下面的程序一共输出多少个“-”?

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
 
int main(void) {
   int i;
   for (i=0; i<2; i++) {
      fork();
      printf("-\n");
   }
   return 0;
}

KEY:6

解释:fork()系统调用是Unix下以自身进程创建子进程的系统调用,一次调用,两次返回,如果返回是0,则是子进程,如果返回值>0,则是父进程(返回值是子进程的pid),这是众为周知的。

还有一个很重要的东西是,在fork()的调用处,整个父进程空间会原模原样地复制到子进程中,包括指令,变量值,程序调用栈,环境变量,缓冲区,等等。

i=0时,父进程A产生一个子进程A1,此时输出两行“-”;

i=1时,fork使父进程A产生子进程A2,A1产生子进程A3,此时A-A3共产生4行“-”(因为现在A,A1的输出行缓冲均为空);

总数为6:2(A)+2(A1)+1(A2)+1(A3)=6;

但如果题目改成:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
 
int main(void) {
   int i;
   for (i=0; i<2; i++) {
      fork();
      printf("-");
   }
   return 0;
}

结果就是8个了。这是因为printf(“-“);语句有buffer,所以,对于上述程序,printf(“-“);把“-”放到了缓存中,并没有真正的输出,在fork的时候,缓存被复制到了子进程空间,所以,就多了两个,就成了8个,而不是6个。

程序遇到“\n”,或是EOF,或是缓中区满,或是文件描述符关闭,或是主动flush,或是程序退出,就会把数据刷出缓冲区。需要注意的是,标准输出是行缓冲,所以遇到“\n”的时候会刷出缓冲区,但对于磁盘这个块设备来说,“\n”并不会引起缓冲区刷出的动作,那是全缓冲,你可以使用setvbuf来设置缓冲区大小,或是用fflush刷缓存。


28、在32位机器上,下列代码中,sizeof(a)的值是()。

class A 
 { 
      int i; 
      union U 
      { 
          char buff[13]; 
          int i; 
      }u; 
      void foo() {    } 
      typedef char* (*f)(void*); 
      enum{red, green, blue} color; 
 }a; 

KEY:24

解释:联合表示若干数据成员取其一,故以叠加方式分配内存,所占字节数为最大数据成员所占的字节数。 还要字节对齐:

空函数 不占字节。

int i占4个字节;

union U实例化为u占取16个字节(char数组占13个字节,但因为最大类型为int,所以占取只能为4字节的整数倍即最小16字节);

空函数不占取字节;

未实例化指针不占取字节;

枚举类型占取4个字节;

所以4+16+0+0+4=24。


29、下列关于bool,int,float,指针类型的变量a 与“零”的比较语句正确的有?

bool : if(!a)

int : if(a == 0)

float: if(a == 0.0)

指针: if(a == nullptr)

KEY:A、B、D

解释:由于计算机二进制表示浮点数有精度的问题,0.0(浮点double)实际上不是0,而是非常接近零的小数,所以C错!


30、程序运行后的输出结果是(  )。

#include <stdio.h>
main()
{int
a[]={1,2,3,4,5,6,7,8,9,10,11,12,},*p=a+5,*q=NULL;
  *q=*(p+5);
   
printf("%d %d\n",*p,*q);
}

KEY:运行后报错。

解释:*q=NULL,q是野指针,对q操作可能会引起程序崩溃,首先编译器就不允许他的存在。


31、选择填空:

#include
void test(void *data) {
    unsigned int value = (此处应填入)
    printf("%u", value);
}
using namespace std;
int main() {
    unsigned int value = 10;
    test(&value);
    return 0;
}

*data

(unsigned int)(*data)

(unsigned*)data

*((unsigned int *)data)

KEY:D

解释:实际上只要是*data,我们就知道了它是指针,如果是32位机器,该指针就指着内存中的某个地址,用32位表示,记住这个32位只是初始地址,任何指针都是的。而前面的void 或者int 类型是定义一次读几个字节,如果是int则读4个字节,也就是从*data存的地址开始从内存往后读4个字节就行,而void是空,没有指定要读多少个字节,所以要用指针类型(unsigned int *)强制转化为知道要读几个字节的int指针,然后再用*从开始地址,读取unsigned int个字节出来!

参数是 void*, 编译器不知道它的具体数值类型,不能直接取值,B错。


32、运行时的输出结果是()

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

KEY:122444

解释:注意区分实现拷贝功能的构造函数、赋值运算符的重载的区别,也就是:

A a ;
A b;
a = b;

这里是赋值操作。

A a;
A b = a; 

这里是拷贝的构造函数操作。

所以 MyClass obj3 = obj1; 调用的是拷贝构造函数。


33、关于抽象类和纯虚函数的描述中,错误的是:

纯虚函数的声明以“=0;”结束

有纯虚函数的类叫抽象类,它不能用来定义对象

抽象类的派生类如果不实现纯虚函数,它也是抽象类

纯虚函数不能有函数体

KEY:D

解释:纯虚函数可以有函数体!!!函数体必须定义在类的外部!!!(C++ Primer)。


34、设有以下说明语句,则下面的叙述不正确的是(). 

struct stu 
{ 
int a;
float b;
} stutype;

struct是结构体类型的关键字

struct stu是用户定义的结构体类型

stutype是用户定义的结构体类型名

a和b都是结构体成员名

KEY:C

解释:struct为结构体关键字,stu为结构体类型名,a、b为结构体成员名,stutype为结构体变量名


35、写出下面程序的输出结果:

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(); 
 pa->FuncB(); 
 pa2->FuncA(); 
 pa2->FuncB();
 delete pa2;
}
KEY:FuncA called
FuncBB called
FuncA called

FuncB called

解释:pa->FuncA();     //pa=&b动态绑定但是FuncA不是虚函数,所以FuncA called

pa->FuncB();     //FuncB是虚函数所以调用B中FuncB,FuncBB called  

pa2->FuncA();     //pa2是A类指针,不涉及虚函数,调用的都是A中函数,所以FuncA called FuncB called

pa2->FuncB()

也就是说,需要满足三点要求:动态绑定、指针或引用、虚函数。即必须使用基类类型的指针变量,使该指针指向不同派生类的对象,并通过调用指针所指向的虚函数才能实现动态的多态性。


36、若MyClass为一个类,执行语句时会自动调用该类构造函数的次数是:

MyClass a[4],*p[5];

KEY:4

解释:把MyClass a[4],*p[5];分开写;

MyClass a[4];
MyClass *p[5];

则a[4]是类数组,有4个对象,调用构造函数4次;

*p[5]是指针数组,也就是5个元素存放的是指向MyClass类型的对象的指针,没有初始化的指针为空,不指向任何对象,也不调用构造函数。


37、下面一段程序的输出结果是?

#define product(x) ((x)*(x))
int main()
{
    int i = 3, j, k;
    j = product(i++);
    k = product(++i);
    printf("%d %d", j, k);
    return 0;
}

KEY:12 42


38、若执行以下程序段,则z的二进制值是______。

int x=3,y=6,z; z=x^y<<2;

KEY:00011011

解析:需要注意的是:“^”为异或的意思,而不是指数运算的标志。其次“<<”的优先级较高,先计算。


39、当free释放内存之后,指针还指向原来的那块地址,需要我们设置 p = NULL;如果不手动设置 p = NULL,此时P就变成了野指针。也就是说:野指针是指向未分配或者已经释放的内存地址。


40、关于C语言中volatile关键字,下面的说法哪一个是错误的?

编译器会禁止对volatile修饰的变量进行读写优化

用volatile修饰的变量,读取速度会更快

每一次读取volatile修饰的变量都会从内存中读取

KEY:B

解释:volatile修饰的变量表示该变量为“易变的”。为保证正确性,禁止编译器进行读写优化,必须每次都从内存中读取。

使用volatile关键字声明的变量,系统总是重新从它所在的内存中读取数据,即使它前面的指令刚刚从该处读取过数据,而且读取的数据立刻被保存;相反,若没有使用volatile,编译器可能会做优化处理,可能暂时使用寄存器中的值,而如果该变量由别的程序更新了的话,将会出现不一致的现象!!

总结起来就是:

编译器会禁止对 volatile 修饰的变量做读写优化,A 正确;

每次使用该变量时,系统都会重新从它所在内存中读取数据,C 正确;

这相对于做了读取优化的变量来说,速度当然是慢了一些啦, B 错误。


41、下列代码试图打印数字1-9的全排列组合。其中run函数中缺失的部分应该依次为:

#include "stdio.h"
#define N 9
int x[N];
int count = 0;
 
void dump() {
  int i = 0;
  for (i = 0; i < N; i++) {
    printf("%d", x[i]);
  }
  printf("\n");
}
 
void swap(int a, int b) {
  int t = x[a];
  x[a] = x[b];
  x[b] = t;
}
 
void run(int n) {
  int i;
  if (N - 1 == n) {
    dump();
    count ++;
    return;
  }
  for (i = ___; i < N; i++) {
    swap(___, i);
    run(n + 1);
    swap(___, i);
  }
}
 
int main() {
  int i;
  for (i = 0; i < N; i++) {
    x[i] = i + 1;
  }
  run(0);
  printf("* Total: %d\n", count);
}

KEY:n, n, n


42、已知:int x,y;double z;以下语句中错误的函数调用是()。

scanf(“%d,%1x,%1e",&x,&y,&z);

scanf(“%2d*%d%1f”,&x,&y,&z);

scanf(“%x%*d%o”,&x,&y);

scanf(“%x%o%6.2f",&x,&y,&z);

KEY:D

解释:%m.nf只能用于输出时指定精度。输入时只能用%f,不能用%m.nf指定输入精度!

也就是说:scanf中的格式控制符不能指明浮点数的精度。


43、以下选项中非法的C语言字符常量是?

'\007'

'\b'

'a'

"\09"

KEY:转义字符分三种,一般转义字符,八进制转移字符和十六进制转移字符:

  • 一般转义字符,如‘\b’,由两个字符表示,其实代表一个字符,这个代表退格字符;
  • 八进制转义字符,如‘\007’,三位数字是八进制的,ASCII码为7的表示响铃,此处的0开头可以省略,写成'\7'也是正确的;
  • 十六进制,如'\x09',同样后面数字是所表示意思的Ascii码的十六进制表示,注意一定要有x,大小写都行。

并且,后面的取值范围必须在0-255之间。

D选项是双引号,所以错误。


44、若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’;

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

KEY:12、9

解释:在类中,如果什么都没有,则类占用1个字节,一旦类中有其他的占用空间成员,则这1个字节就不在计算之内,如:一个类只有一个int则占用4字节而不是5字节;

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

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


45、这段程序的输出结果为:

char ch=-1;
printf("%02x,%02x",ch,(unsigned char)ch);

KEY:ffffffff,ff

解释:%02x表示输出最少2位,不足补0。这是一道关于符号扩展的问题。

短数据类型扩展为长数据类型:

  • 要扩展的数据类型为有符号类型,用短数据的符号位填充长数据多出来的高字节 ,-1 (11111111)扩展为int(方便转换为十六进制)即(符号位是1)11111111 11111111 11111111 11111111(ffffffff);
  • 要扩展的数据类型为无符号的(unsigned char) ,用0来填充长数据类型的高字节,此时-1在内存的二进制存储(11111111 )扩展为int即00000000 00000000 00000000 11111111(ff)。


46、以下函数用法正确的个数是:

void test1()
{
    unsigned char array[MAX_CHAR+1],i;
    for(i=0;i<=MAX_CHAR;i++){
        array[i]=i;
    }
}
char*test2()
{
    char p[] = "hello world";
    return p;
}
char *p =test2();
void test3(){
    char str[10];
    str++;
    *str='0';
}

KEY:0

解释:第一个问题:

重点不在于CHAR_MAX的取值是多少,而是在于i的取值范围是多少。

一般char的取值范围是-128到127,而u char 则是0~255,所以i的取值范围是0~255.所以当CHAR_MAX常量大于255时,执行i++后,i不能表示256以上的数字,所以导致无限循环。

第二个问题:

重点在于函数中p的身份,它他是一个指针,还是数组名。如果是指针p,则p指向存放字符串常量的地址,返回p则是返回字符串常量地址值,调用函数结束字符串常量不会消失(是常量)。所以返回常量的地址不会出错;如果是数组p,则函数会将字符串常量的字符逐个复制到p数组里面,返回p则是返回数组p,但是调用函数结束后p被销毁,里面的元素不存在了。

例子中p是数组名,所以会出错,p所指的地址是随机值。若是把char p[]="hello";改成char *p="hello";就可以了。

第三个问题:

重点在于str++;这实际的语句就是str=str+1;而str是数组名,数组名是常量,所以不能给常量赋值(可以执行str+1,但是不能str=)。


47、执行这个程序的结果是()

#include<iostream>
using namespace std;
class TestClass{
    char x;
public:
    TestClass() { cout << 'A'; }
    TestClass(char c) { cout << c; }
    ~TestClass() { cout << 'B'; }
};
int main() {
    TestClass p1, *p2;
    p2 = new TestClass('X');
    delete p2;
    return 0;
}

KEY:AXBB

解释:类指针的声明,是不会调用构造函数的;但是指向一个类实例(new)就会调用构造函数。 但是类的声明,会调用默认构造函数。 

TestClass p1, *p2; //只为p1调用默认构造——A 

p2 = new TestClass('X'); //调用构造函数,由p2指向——X 

delete p2; //释放内存空间,p2所指实例调用析构函数——B 

return 0; //程序结束,p1调用析构——B


48、有如下模板定义,在下列对fun的调用中,错误的是()

template <class T>
T fun(T x,T y){
    return x*x+y*y;
}

fun(1, 2)

fun(1.0, 2)

fun(2.0, 1.0)

fun<float>(1, 2.0)

KEY:B

解释:模板定义就相当于泛型,类型必须要相同,且不会自动强制转换。用<float>进行声明,后面的实参会强制类型转换为float,所以也是类型一致的。


49、下列 C 代码中,不属于未定义行为的有:______。

int i=0;i=(i++);

char *p=”hello”;p[1]=’E’

char *p=”hello”;char ch=*p++

int i=0;printf(“%d%d\n”,i++,i--)

KEY:C

解释:未定义行为(Undefined Behavior)是指C语言标准未做规定的行为。同时,标准也从没要求编译器判断未定义行为,所以这些行为有编译器自行处理,在不同的编译器可能会产生不同的结果,又或者如果程序调用未定义的行为,可能会成功编译,甚至一开始运行时没有错误,只会在另一个系统上,甚至是在另一个日期运行失败。当一个未定义行为的实例发生时,正如语言标准所说,“什么事情都可能发生”,也许什么都没有发生。一句话,未定义行为就是运行结果不确定。

例如:变量即是左边结果,又是右边的操作数,如a+=a++,a %= b ^= a ^= b ^= a;使用越界数组也是C的一个“未定义行为”;允许一个随便指的指针的读写;使用未初始化的变量等等。

A选项,不知道编译器会怎么选择自增和赋值的顺序,所以这是由编译器决定的,属于未定义行为。

B选项,”hello“这个字符串属于一个字符串常量了,指针p指向了这个字符串常量,通过这个指针来直接修改常量第二个字符,这也属于未定义行为。

C选项,只是通过指针找到第二个字符并将它赋值给一个字符变量,并没有改变这个字符串常量,所以不属于未定义行为。

D选项,在printf语句中,i++和i–谁先执行由编译器决定,这是未定义行为。

还是不了解的可以参考链接:C语言未定义行为一览


50、下面选项中的程序段,没有编译错误的是()

char* sp, s[10]; sp = "Hello";

char* sp, s[10]; s = "Hello";

char str1[10] = "computer", str2[10]; str2 = str1;

char mark[]; mark = "PROGRAM";

KEY:A

解释:数组就是数组,不是指针。数组名代表被分配的内存的首地址,是一个地址常量,是右值;而指针作为变量,却是一个左值。数组名不是常量指针,因为他们的类型不一样。

也就是说,数组名为常量指针, 不能作为左值。

猜你喜欢

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