以下为牛客网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
解释:数组就是数组,不是指针。数组名代表被分配的内存的首地址,是一个地址常量,是右值;而指针作为变量,却是一个左值。数组名不是常量指针,因为他们的类型不一样。
也就是说,数组名为常量指针, 不能作为左值。