【牛客网】C/C++牛客网专项刷题(03)
以下为牛客网C/C++专项刷题:
1、阅读以下程序,当输入数据的形式为12a345b789↙,正确的输出结果为()。
void main()
{char c1,c2;int a1,a2;c1=getchar();scanf("%2d",&a1);c2=getchar();scanf("%3d",&a2);printf ("%d,%d,%c,%c\n",a1,a2,c1,c2);
}
KEY:2,345,1,a
解答:这里的"%2d"表示向缓冲区中放入2个输入。如果放入的输入都符合输入的条件,那么都一起接受了;如果放入的输入不符合输入的条件,那么也不丢弃掉,看接下来的输入条件。
2、Math.round(11.5) 等于()。
KEY:12
解答:取近似值,如果有两个数距离它的距离相等,那么就取较大的那个值。
3、如下程序:
#include <iostream>
using namespace std;class A
{
public:A(){printf("A");}
};int main()
{A *p1 = new A;A *p2 = (A *)malloc(sizeof(A));return 0;
}
该程序运行结果为()。
KEY:A
解答:这题主要是考的new和malloc的区别,new会分配内存,并调用类的构造函数创建对象,而malloc只是分配内存,不调用类的构造函数创建对象。同时这个程序应该用delete p1;free(p2);。
4、以下程序用来统计文件中字符的个数(函数feof 用以检查文件是否结束,结束是返回非零):
#include<stdio.h>main()
{ FILE *fp; long num=0;fp=fopen("fname.dat", "r" );while (________) { fgetc( fp );num++ ;}printf( " num= % d\n",num);fclose( fp );
}
下面选项中,填入横线处不能得到正确结果的是?
feof( fp )= =NULL
!feof( fp )
feof(fp)
feof( fp ) == 0
KEY:C
解答:feof()函数,如果遇到文件结束,函数值为非零值,否则函数值为0。文件结束符EOF,Windows下为组合键Ctrl+Z,Unix/Linux下为组合键Ctrl+D。
5、若有以下类W说明,则函数fConst的正确定义是( )。
class W
{int a;public:void fConst(int&) const;
};
void W::fConst( int &k )const { k = a; }
void W::fConst( int &k )const { k = a++; }
void W::fConst( int &k )const { cin >> a; }
void W::fConst( int &k )const { a = k; }
KEY:A
解答:这道题目考的是const关键字,类的方法后面加了const后,该方法的实现中不能修改类的成员。即不能修改类成员a选项中,只有选项A,没有修改类成员a的值。
注意:const如此用法,可以在该成员函数中引用成员数据的值,但不能修改对象的数据成员,在函数体内只能调用const成员函数,不能调用其他成员函数。
6、C++内存分配中说法错误的是:______。
对于栈来讲,生长方向是向上的,也就是向着内存地址增加的方向
对于堆,大量的 new/delete 操作会造成内存空间的不连续
堆容易产生 memory leak(内存泄漏)
堆的效率比栈要低得多
栈变量引用容易逃逸
栈区一般由编译器自动分配释放,堆区一般由程序员分配释放
KEY:A
解答:栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将 提示overflow。因此,能从栈获得的空间较小;
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
7、函数func的定义如下:
void func(const int& v1, const int& v2)
{std::cout << v1 << ' ';std::cout << v2 << ' ';
}
以下代码在vs中输出结果为____。
int main (int argc, char* argv[])
{int i=0;func(++i,i++);return 0;
}
0 1
1 2
2 1
2 0
KEY:D
解答:在C语言中,函数的参数是从右到左的顺序压入到栈的,函数执行时再从栈弹出弹出来执行。
参数i先入栈0,然后执行i++此时i为1,接着参数i先执行++i,i此时为2,后入栈进行输出2 0。
8、下面有关回调函数的说法,错误的是?
回调函数就是一个通过函数指针调用的函数
回调函数可能被系统API调用一次,也可能被循环调用多次
回调函数本身可以是全局函数 ,静态函数和某个特定的类的成员函数
回调函数可用于通知机制
KEY:C
解答:所谓的回调函数,就是预先在系统的对函数进行注册,让系统知道这个函数的存在,以后,当某个事件发生时,再调用这个函数对事件进行响应。 定义一个类的成员函数时在该函数前加CALLBACK即将其定义为回调函数,函数的实现和普通成员函数没有区别。
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
类的成员函数需要隐含的this指针,而回调函数没有办法提供。
9、下列情况中,不会调用拷贝构造函数的是()
用一个对象去初始化同一个类的另一个新对象时
将类的一个对象赋值给该类的另一个对象时
函数的形参对象,调用函数进行形参和实参结合时
函数的返回值是类的对象,函数执行返回调用时
KEY:B
解答:关于拷贝构造函数的意义:
class Base{public:Base(){}; //构造函数Base(int i)(); //构造函数Base(Base &b){}; //拷贝构造函数
};Base w1; //构造函数
Base w2(w1); //拷贝构造函数
Base w3 = w1; //拷贝构造函数 ,与上式等价
w1 = w2; //赋值运算符Base w4(100); //构造函数
Base w5 = 100; //构造函数,与上式等价
w5 = 200; //构造函数,实现了类型转换功能
可知:将类的一个对象赋值给该类的另一个对象时,是赋值运算,不是构造函数。
10、函数外部访问x等于什么?
enum string{ x1, x2, x3=10, x4, x5,
} x;
5
12
0
随机值
KEY:C
解答:在函数外部定义,为全局变量,默认为0;若在函数内部定义,则在编译时会显示变量未初始化,是一个随机值。
11、若变量已正确的定义为float类型,要通过输入函数scanf(“%f%f%f”,&a,&b,&c)给a赋值10,b赋值22,c赋值33,以下不正确的输入形式是()。
10 22 33
10.0,22.0,33.0
KEY:B
解答:空格、换行和制表符都可以用来分割,但是逗号不行。
12、若 a 是 float 型变量,b 是 unsigned 型变量,以下输入语句中合法的是()。
scanf("%6.2f%d",&a,&b);
scanf("%f%n",&a,&b);
scanf("%f%3o",&a,&b);
scanf("%f%f",&a,&b);
KEY:BC
解答:%n用于接受一个uint,代表到%n为止所输入的字符数,其本身不消耗字符。也就是说,此时的b不是命令行输入进去的,而是自动生成的!命令行输入一个float数,然后将这个数赋值给a,同时将这个数的字符数赋值给b。
而u、o、x分别代表10、8、16进制的无符号数输入。
13、两个线程并发执行以下代码,假设a是全局变量,那么以下输出___哪个是可能的?
int a=1;
void foo(){++a;printf("%d",a);
}
3 2
2 3
3 3
2 2
KEY:ABCD
解答:选项D是,假设线程A先执行++a操作但没有写回到内存,这时线程B执行++a操作写回内存并printf,输出2,线程A继续执行,++a操作写回内存,a的值保持2,再printf。
这里关键有两点:
- 两个线程可随时被抢占 ;
- ++a和printf不是原子指令,可随时被打断;特别注意函数printf,a作为参数压栈后,a再变化则不会影响输出(printf实际打印的是压栈的参数,是值拷贝的栈变量)。
14、vector和list的区别:
- vector数据结构:vector和数组类似,拥有一段连续的内存空间,并且起始地址不变。因此能高效的进行随机存取,时间复杂度为o(1)。但因为内存空间是连续的,所以在进行插入和删除操作时,会造成内存块的拷贝,时间复杂度为o(n)。另外,当数组中内存空间不够时,会重新申请一块内存空间并进行内存拷贝;
- list数据结构:list是由双向链表实现的,因此内存空间是不连续的。只能通过指针访问数据,所以list的随机存取非常没有效率,时间复杂度为o(n)。但由于链表的特点,能高效地进行插入和删除。
在对它们的迭代器iterator进行讨论:
- vector拥有一段连续的内存空间,能很好的支持随机存取,因此vector<int>::iterator支持“+”,“+=”,“<”,“[]”等操作符。
- list的内存空间可以是不连续,它不支持随机访问,因此list<int>::iterator则不支持“+”、“+=”、“<”,“[]”等
- vector<int>::iterator和list<int>::iterator都重载了“++”运算符。
总之,如果需要高效的随机存取,而不在乎插入和删除的效率,使用vector;如果需要大量的插入和删除,而不关心随机存取,则应使用list。
15、代码生成阶段的主要任务是:
把高级语言翻译成机器语言
把高级语言翻译成汇编语言
把中间代码变换成依赖具体机器的目标代码
把汇编语言翻译成机器语言
KEY:C
解答:编译的过程为:扫描(词法分析)、语法分析、语义分析、源代码优化、代码生成和目标代码优化。
源码 ->(扫描)-> 标记 ->(语法分析)-> 语法树 ->(语义分析)-> 标识语义后的语法树 ->(源码优化)-> 中间代码 ->(代码生成)-> 目标机器代码 ->(目标代码优化)-> 最终目标代码。
参考文章:谈谈c语言程序如何变成可执行文件。
16、以下程序的打印结果是()。
#include<iostream>
using namespace std;void swap_int(int a , int b)
{int temp = a;a = b;b = temp;
}void swap_str(char* a , char* b)
{char* temp = a;a = b;b = temp;
}int main(void)
{int a = 10;int b = 5;char* str_a = "hello world";char* str_b = "world hello";swap_int(a , b);.swap_str(str_a , str_b);printf("%d %d %s %s\n",a,b,str_a,str_b);return 0;
}
KEY:10 5 hello world world hello
解答:要交换字符串,需要将两个char*的地址发送过去,而不是发送一个char的地址过去!其实两个交换函数都犯得相同的一个错误,在函数中形成了局部变量。正确的字符串交换的方法为:
void swap_str(char **a , char **b)
{char *temp = *a;*a = *b;*b = temp;
}
或者,还是使用引用:
void swap_str(char* &a , char* &b)
{char *temp = a;a = b;b = temp;
}
17、如下程序用于输出“Welcome to Huawei Test”,请指出其中的两处错误。
char * GetWelcome(void){char * pcWelcome;char * pcNewWelcome;pcWelcome="Welcome to Huawei Test";pcNewWelcome=(char *)malloc(strlen(pcWelcome)); //1if(NULL==pcNewWelcome){return NULL; //2}strcpy(pcNewWelcome, pcWelcome); //3return pcNewWelcome; //4
}
KEY:1 3
解答:1处,正确形式是:pcNewWelcome=(char*)malloc(strlen(pcWelcome)*sizeof(char)) ,当然char的大小为1,形式上可以算是对了。但是逻辑上呢?strlen()统计字符个数,不含结尾符'\0',所以这样子分配会少一个字节。3处,由1处,既然新分配的空间少了一个字节,你用原来的来复制到新的里?能装下吗?不能。
同样看一下strlen和sizeof的区别:
char *a = "hello";
char b[] = "hello";
cout << sizeof(a) << ' ' << strlen(a) << endl;
cout << sizeof(b) << ' ' << strlen(b) << endl;
这个的答案是4 5 6 5。需要注意的是sizeof(a)判断的是一个指针的大小,不是字符串的大小!需要使用数组才行。
18、编译运行如下程序会出现什么结果?
#include <iostream>
using namespace std;class A
{A(){printf("A()");}
};void main()
{A a;
}
A()
编译错误
链接错误
以上都不对
KEY:B
解答:在类中,其成员的缺省的存取权限是私有的;而在结构体类型中,其成员的缺省的存取权限是公有的。也就是说,使用一个私有的构造函数进行初始化。
19、下面代码的输出是什么?
class A
{
public: A() { } ~A() { cout<<"~A"<<endl; }
}; class B:public A
{ public: B(A &a):_a(a) { } ~B() { cout<<"~B"<<endl; } private: A _a; }; int main(void) { A a;B b(a);
}
KEY:~B ~A ~A ~A
解答:对象成员的构造函数、基类构造函数、派生类构造函数的调用顺序:先调用基类的构造函数,再调用对象成员的构造函数,最后执行派生类的构造函数。
- 多个基类的条件下:取决于在类继承中说明的顺序,与它们在构造函数的初始化成员列表的先后顺序无关;
- 多个对象成员的条件下:取决于它们在派生类中说明的顺序。
本题:A a;,调用一次构造函数。B b(a);,先基类的构造函数,再对象成员的构造函数,再派生类的构造函数。析构的时候,顺序正好相反。
20、关于CSingleLock,下面说法错误的是?
主要是同步多个线程对一个数据类的同时访问。
CSingleLock有RAII的好处。
CSingleLock对象需要有一个从CSyncObject派生的对象存在。
CSingleLock必须要全部显示的进行unLock操作
KEY:D
解答:RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。 RAII的一般做法是这样的:在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处: 不需要显式地释放资源。 采用这种方式,对象所需的资源在其生命期内始终保持有效。
CSingleLock主要是同步多个线程对一个数据类的同时访问。在创建CSingleLock对象时类对象会自动根据参数赋值,而且会lock,不用显式lock,即,只需创建对象就可lock。CSinglelock退出时自动调用析构函数,析构时自动unlock。
21、不考虑任何编译器优化(如:NRVO),下述代码的第10行会发生:
#include <stdio.h> //1class B //2
{ //3
}; //4B func(const B& rhs){ //5return rhs; //6
} //7int main(int argc, char **argv){ //8B b1,b2; //9b2=func(b1); //10
} //11
一次默认构造函数,一次拷贝构造函数,一次析构函数,一次(拷贝赋值运算符)operator=
二次拷贝构造函数,一次析构函数
一次(拷贝赋值运算符)operator=,一次析构函数
一次拷贝构造函数,一次析构函数,一次(拷贝赋值运算符)operator=
KEY:D
解答:答案是D。但是次序并不是,正确的顺序是一次拷贝构造函数,一次(拷贝赋值运算符)operator= ,一次析构函数。
一次拷贝构造函数发生在func函数调用完成,返回B类型的对象时,因为返回的不是引用类型,所以会生成一个对象,不妨称为TEMP,将返回的对象通过拷贝构造函数复制给TEMP,同时由于拷贝构造函数的参数是const B&,rhs并不会在函数结束时候被析构,这时并不调用析构函数(也就是return这一句,由b&的引用类型,来生成TEMP);
赋值运算符在func函数执行完成后,将上面提到的TEMP,通过赋值运算符赋值给b2;
表达式的最后将临时对象TEMP进行析构。
22、下面程序的输出结果是:
char *p1= “123”, *p2 = “ABC”, str[50]= "xyz";
strcpy(str+2,strcat(p1,p2));
cout << str;
xyz123ABC
z123ABC
xy123ABC
出错
KEY:D
解答:首先,c++的内存分配地址有小到大分别是:动态内存区,包括栈和堆;代码内存区;静态内存区,包括全局变量,静态变量,只读变量(就是常量),按照定义的先后顺序分配地址;
本题中,p1,p2都是指针,是局部变量,在分配内存时会将其分配到栈区,且只会分配p本身所需要的内存空间,即p所指向的内容的地址大小的空间,并没有为字符串分配内存,所以*p1="123"和*p2="ABC"中 123和ABC都被分配在了文字常量区,既然是常量,大小就是不可变的,而strcat的第一个变量必须是可变的,所以程序会出错。
也就是说:
char* strcat(char *,const char*); //第一个参数所指向的内容必须可以修改,可以赋值为在栈上分配的数组
strcat(p1,p2); //试图修改p1的内容,p1指向文字常量区,其指向的内容无法修改
23、如果x=2014,下面函数的返回值是()。
int fun(unsigned int x)
{int n=0;while((x+1)){n++;x=x|(x+1);}return n;
}
KEY:C
解答:x&(x-1)统计1的个数,x|(x+1)统计0的个数。
2014对应的二进制为:0000 0000 000 0000 0000 0111 1101 1110
- x|(x+1)的结束终止条件:直到变成全1的时候x+1就溢出为全0,循环结束;
- x&(x-1)的结束终止条件:直到变成全0的时候,循环结束。
24、若有说明:int *p,m=5,n;以下正确的程序段是()。
p=&n;scanf("%d",&p);
p=&n;scanf("%d",*p)
scanf("%d",&n);*p=n;
p=&n;*p=m;
KEY:D
解答:scanf的一般格式下,放入的应该为地址,也就是说,正确的写法应该是:
int i;
scanf("%d", &i);int* p=&i;
scanf("%d", p);
25、下列表达式正确的是:
9++
(x+y)++
c+++c+++c++
++(a-b--)
KEY:C
解答:a++表达式的本质是编译器翻译成a=a+1。
那么:A、9=9+1?;B、x+y=x+y+1? C++左值表达式不能有运算符;D、选项与B类似。也就是主要考的就是,C++的等号左边不能有运算符!
26、下面程序运行结果为()。
void main()
{char c=’a’;if ('a'<c<='z') printf ("Low”);else printf("UP");
}
LOW
UP
LOWUP
程序语法错误
KEY:A
解答:尽管我们都知道表示一个数是否处于一个范围之间时,这样使用时达不到效果的。但是,这句话本身是没有语法错误的。不要突然就脑子转不过来弯了。
27、下面代码会输出什么?()
class A{public:int m;void print() { cout << "A\n"; }
};A *p = 0;
p->print();
KEY:A
解答:初始化为NULL(或者0)的类指针可以安全的调用不涉及类成员变量的类成员函数而不出错,但是如果类成员函数中调用了类成员变量则会出错。
原因:调用成员函数的时候,函数地址是编译期间确定的,成员函数不通过对象指针去调用,对象指针仅仅作为参数传入函数然后去调用成员变量。而如果是虚函数,需要通过this去计算vptr,然后找到vtable,而this为NULL,那么就会报错。
28、C语言和C++的默认缺省大比较:
返回值方面
- C中:如果函数未指定返回值类型,则默认为int;
- c++中:如果一个函数没有返回值,返回值类型必须指定为void。
参数列表方面
- C中:如果函数没有指定参数列表,则默认可以接受任意多个参数;
- C++中:有严格的类型检测,没有参数列表的函数默认为void,不接受任意参数。
29、看以下代码:
A *pa = new A[10];
delete pa;
则类A的构造函数和析构函数分别执行了几次()。
KEY:10 1
解答:应该改成 delete[] pa;才对,这样会调用10次A的构造函数和10次A的析构函数。
但是这还没有完。如果A是简单类型,比如 int ,char ,double 或者结构体,那么动态创建简单类型的数组,是可以调用delete pa;这样来析构的,效果是和 delete[] pa的效果一样,不会报错。
但是如果,A是自己定义的一个类,那么动态创建了对象数组,用delete pa;就会使程序崩溃。因为动态创建一个对象的内存实际上比A要大,有一些附加的内存需要存放附加的信息。
30、若 ch 为 char 型变量,k 为 int 型变量(已知字符 a 的 ASCII 十进制代码为97),则以下程序段的执行结果是()。
char ch='a';
int k=12;
printf("%x,%o,", ch, ch, k);
printf("k=%%d\n", k);
因变量类型与格式描述符的类型不匹配,输出无定值
输出项与格式描述符个数不符,输出为零值或不定值
61,141,k=%d
61,141,k=%12
KEY:C
解答:若输出项多于格式描述符的个数,输出输出项中对应格式描述符的内容,剩余输出项则丢弃;若输出项少于格式描述符的个数,输出输出项中对应格式描述符的内容,缺少的输出项则输出不定值。
printf("%x,%o,", ch, ch, k); //将ch以16进制输出为61,八进制为141,k参数被忽略。
printf("k=%%d\n", k); // %是控制符,用 %% 表示输出一个百分号
31、switch后面的“表达式”,可以是int、char和枚举型中的一种,不能是float型变量;同时,case后面必须是“整型常量表达式”,表达式中不能包含变量。
注意:字符型常量也是属于整型常量表达式!
32、下列关于数组与指针的区别描述正确的是?
数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。
用运算符sizeof 可以计算出数组的容量(字节数)
指针可以随时指向任意类型的内存块。
用运算符sizeof 可以计算出指针所指向内容的容量(字节数)
KEY:B
解答:A.堆上创建动态数组;B.sizeof(数组名)就是数组的容量;C.const指针不可以;D. char* str = "hello"; sizeof(str)不能计算出内容的容量,只是指针的容量。
33、下面哪种情况下,B不能隐式转换为A?
class B:public A{}
class A:public B{}
class B { operator A(); }
class A { A(const B&); }
KEY:B
解答:答案A,表示A是基类,B是派生类,向上级类型转换是隐式的,因为部分元素丢弃可以自动完成,向下转型是显式的因为不知道应该增加的值是什么。所以B不能。
答案C,转换函数(又称为类型转换函数)。
答案D,拷贝构造函数, B b = A 肯定是可以的。
转换函数(又称为类型转换函数)是类中定义的一个成员函数,其一般格式为:
类名::operator 转换后的类型 (){ //将“类名”类型转换为“转换后的类型”...
}
其中:operator和“转换后的类型”一起构成转换函数名。该函数不能带有参数,也不能指定返回值类型。因为它的返回值类型就是“转换后的类型”。转换函数的作用就是将对象内的成员数据转换成某种特定的类型。
34、C++语言本身没有输入输出语句。说法是否正确?
正确
错误
KEY:A
解答:输入和输出并不是C++语言中的正式组成成分。C和C++本身都没有为输入和输出提供专门的语句结构。输入输出不是由C++本身定义的,而是在编译系统提供的I/O库中定义的。 C++的输出和输入是用“流”(stream)的方式实现的。
35、下面有关C++中为什么用模板类的原因,描述错误的是?
可用来创建动态增长和减小的数据结构
它是类型无关的,因此具有很高的可复用性
它运行时检查数据类型,保证了类型安全
它是平台无关的,可移植性
KEY:C
解答:(1)可用来创建动态增长和减小的数据结构 (2)它是类型无关的,因此具有很高的可复用性。 (3)它在编译时而不是运行时检查数据类型,保证了类型安全 (4)它是平台无关的,可移植性 (5)可用于基本数据类型。
36、以下哪些做法是不正确或者应该极力避免的:【多选】( )
构造函数声明为虚函数
派生关系中的基类析构函数声明为虚函数
构造函数中调用虚函数
析构函数中调用虚函数
KEY:ACD
解答:构造函数和析构函数都不能调用虚函数:
先析构子类再析构父类,如果父类析构函数有虚函数,会导致调用子类的已经析构的内容。先构造父亲类再构造子类,如果父类构造函数有虚函数,会导致调用子类还没构造的内容。
构造函数不能为虚函数:
虚函数对应一个vtable,可是这个vtable其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过 vtable来调用,可是对象还没有实例化,也就是内存空间还没有,无法找到vtable,所以构造函数不能是虚函数。
基类的析构函数要定义为虚函数:
我们往往通过基类的指针来销毁对象。这时候如果析构函数不是虚函数,就不能正确识别对象类型从而不能正确调用析构函数。也就是说,基类的虚构函数如果不声明成为虚函数,那么销毁派生类时有可能造成资源泄漏,出现指向基类的派生类指针在销毁时,只能销毁基类对象而无法销毁派生类对象的现象。
参考文章:析构函数可以为virtual,构造函数则不能。原因?、为什么构造函数不能为虚函数,而析构函数可以为虚函数。
37、关于以下代码,哪个说法是正确的?
myClass::foo(){delete this;
}void func(){myClass *a = new myClass();a->foo();
}
它会引起栈溢出
都不正确
它不能编译
它会引起段错误
KEY:B
解答:在类的成员函数中能不能调用delete this?答案是肯定的,能调用。那么这个对象在调用release方法后,还能进行其他操作,如调用该对象的其他方法么?答案仍然是肯定 的,调用release之后还能调用其他的方法,但是有个前提:被调用的方法不涉及这个对象的数据成员和虚函数。
根本原因在于delete操作符的功能和类对象的内存模型。当一个类对象声明时,系统会为其分配内存空间。在类对象的内存空间中,只有数据成员和虚函数表指针,并不包含代码内容,类的成员函数单独放在代码段中。在调用成员函数时,隐含传递一个this指针,让成员函数知道当前是哪个对象在调用它。当调用delete this时,类对象的内存空间被释放。在delete this之后进行的其他任何函数调用,只要不涉及到this指针的内容,都能够正常运行。一旦涉及到this指针,如操作数据成员,调用虚函数等,就会出现不可预期的问题。
大致明白在成员函数中调用delete this会发生什么之后,再来看看另一个问题,如果在类的析构函数中调用delete this,会发生什么?实验告诉我们,会导致堆栈溢出。原因很简单,delete的本质是“为将被释放的内存调用一个或多个析构函数,然后,释放内存” (来自effective c++)。显然,delete this会去调用本对象的析构函数,而析构函数中又调用delete this,形成无限递归,造成堆栈溢出,系统崩溃。
38、下列格式控制符,既可以用于输入,又可以用于输出的是( )。
setbase
setfill
setprecision
setw
KEY:A
解答:C++提供了许多的预定义格式控制函数,可直接用于控制输入/输出数据的格式,如下表:
格式控制函数名 | 功能 | 适用于输入/输出流 |
dec | 设置为十进制 | IO |
hex | 设置为十六进制 | IO |
oct | 设置为八进制 | IO |
ws | 提取空白字符 | I |
endl | 插入一个换行符 | O |
flush | 刷新流 | O |
resetioflags(long) | 取消指定的标志 | IO |
setioflags(long) | 设置指定的标志 | IO |
setbase(int) | 将数字转换为 n 进制 | IO |
setfill(int) | 设置填充字符 | O |
setprecision(int) | 设置实数的精度 | O |
setw(int) | 设置宽度 | O |
ends | 插入一个表示字符串结束的字符 |
这些格式控制函数的标头都是:<iomanip>,std命令空间。
39、请问运行Test 函数会有什么样的结果?
char* getmemory(void)
{char p[]="hello world";return p;
}void test(void)
{char *str=NULL;str=getmemory();printf(str);
}
出错
输出"hello world"
输出空""
输出乱码
KEY:D
解答:getmemory,返回的指针,是内部变量(动态数据区),调用之后会被回收。 所以输出是不确定的。
假如修改为:
char* getmemory(void)
{char *p = "hello world";return p;
}void test(void)
{char *str = NULL;str = getmemory();printf(str);
}
就可以输出"hello world"啦!
返回“字符串常量的指针”和“返回数组名”的区别在于,一个返回常量区的地址,一个返回栈内存(动态数据区)的地址。动态数据区的内容出了getmemory()函数范围就被释放了,但常量区则不会被释放。
40、注意区分一下指针函数和函数指针:
- char *p (): 是指针函数,函数;
- char (*p) ():是函数指针,指针。
41、若调用fputc函数输出字符成功,则其返回值是()。
EOF
1
0
输出的字符
KEY:D
解答:fputc函数有一个返回值,如写入成功则返回写入的字符, 否则返回一个EOF。可用此来判断写入是否成功。
42、以下程序的输出结果是()。
#include <string.h>
#include <stdio.h>void main()
{char a[80] = "AB", b[80] = "LMNP", i=0;strcat(a, b);while (a[i] != '\0'){i++;b[i] = a[i];}puts(b);
}
KEY:LBLMNP
解答:主要是介绍一下C++string类的几个函数:
string &append(const char *s); //把c类型字符串s连接到当前字符串结尾
string &append(const char *s,int n); //把c类型字符串s的前n个字符连接到当前字符串结尾int compare(const string &s) const; //比较当前字符串和s的大小
int compare(int pos, int n,const string &s)const; //比较当前字符串从pos开始的n个字符组成的字符串与s的大小string substr(int pos = 0,int n = npos) const; //返回pos开始的n个字符组成的字符串void swap(string &s2); //交换当前字符串与s2的值int find(char c, int pos = 0) const; //从pos开始查找字符c在当前字符串的位置
int find(const char *s, int pos = 0) const; //从pos开始查找字符串s在当前串中的位置
int find(const char *s, int pos, int n) const; //从pos开始查找字符串s中前n个字符在当前串中的位置int rfind(char c, int pos = npos) const; //从pos开始从后向前查找字符c在当前串中的位置
int rfind(const char *s, int pos = npos) const;
int rfind(const char *s, int pos, int n = npos) const;string &replace(int p0, int n0,const char *s); //删除从p0开始的n0个字符,然后在p0处插入串s
string &replace(int p0, int n0,const char *s, int n); //删除p0开始的n0个字符,然后在p0处插入字符串s的前n个字符string &insert(int p0, const char *s); //在p0位置插入字符串s
string &insert(int p0, const char *s, int n); //在p0位置插入字符串s中pos开始的前n个字符string &erase(int pos = 0, int n = npos); //删除pos开始的n个字符,返回修改后的字符串
再介绍一下C语言标准库<string.h>的几个函数:
void *memchr(const void *str, int c, size_t n); //在参数 str 所指向的字符串的前 n 个字节中搜索第一次出现字符 c(一个无符号字符)的位置。int memcmp(const void *str1, const void *str2, size_t n); //把 str1 和 str2 的前 n 个字节进行比较。void *memcpy(void *dest, const void *src, size_t n); //从 src 复制 n 个字符到 dest。void *memmove(void *dest, const void *src, size_t n); //另一个用于从 src 复制 n 个字符到 dest 的函数。void *memset(void *str, int c, size_t n); //复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。char *strchr(const char *str, int c); //在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置。
char *strrchr(const char *str, int c); //在参数 str 所指向的字符串中搜索最后一次出现字符 c(一个无符号字符)的位置。char *strcat(char *dest, const char *src); //把 src 所指向的字符串追加到 dest 所指向的字符串的结尾。
char *strncat(char *dest, const char *src, size_t n); //把 src 所指向的字符串追加到 dest 所指向的字符串的结尾,直到 n 字符长度为止。int strcmp(const char *str1, const char *str2); //把 str1 所指向的字符串和 str2 所指向的字符串进行比较。
int strncmp(const char *str1, const char *str2, size_t n); //把 str1 和 str2 进行比较,最多比较前 n 个字节。char *strcpy(char *dest, const char *src); //把 src 所指向的字符串复制到 dest。
char *strncpy(char *dest, const char *src, size_t n); //把 src 所指向的字符串复制到 dest,最多复制 n 个字符。size_t strlen(const char *str); //计算字符串 str 的长度,直到空结束字符,但不包括空结束字符
43、在16位IBM-PC上使用C语言,若有如下定义:
struct data
{ int i; char ch;double f;
} b;
则结构变量b占用内存的字节数是()。
1
2
8
11
KEY:D
解答:16位下,int 2字节,char 1字节,double 8字节。这个版本的编译器太旧了,没有内存对齐。
44、以下描述错误的是:
函数的形参在函数未调用时不分配存贮空间
若函数的定义出现在主函数之前且仅被主函数使用,则可以不必再说明
若一个函数(非主函数)没有return语句,返回类型是void
一般来说,函数的形参和实参的类型应该一致
KEY:C
解答:构造函数和析构函数都没有返回类型,也没有return语句。
45、当一个类A 中没有声明任何成员变量与成员函数,这时sizeof(A)的值是多少?
1
0
4
运行时错误
KEY:A
解答:深度探索c++对象模型中是这样说的:那是被编译器插进去的一个char ,使得这个class的不同实体(object)在内存中配置独一无二的地址。也就是说这个char是用来标识类的不同对象的。
再深究一下:
class A {}; //sizeof(A)=1class A {void fun() {};
}; //sizeof(A)=1class A {virtual void fun() {};
}; //sizeof(A)=4
46、以下哪项不属于STL container?( )
stack
queue
multimap
string
KEY:D
解答:STL container分为三大类:
- 序列容器:动态数组vector,双端队列deque(本质是动态数组加索引),链表list。
- 关联容器:set,map,multiset,multimap,bitset(叫bit_array更合适)。
- 容器适配器:stack,queue,priority_queue。
47、执行"int x=1;int y=~x;"语句后,y的值为?
1
0
-1
-2
KEY:D
解答:x的补码为00000000 00000000 00000000 00000001,则y为11111111 11111111 11111111 11111110。化为源码就是:10000000 00000000 00000000 00000010,也就是-2。
48、设有如下说明:
typedef struct ST
{long a;int b;char c[2];
} NEW;
则下面叙述中正确的是( ) 。
以上的说明形式非法
ST是一个结构体类型
NEW是一个结构体类型
NEW是一个结构体变量
KEY:C
解答:经过typedef定义之后,struct ST和NEW等价。
49、this作用域是在类内部,当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。静态成员函数不拥有this指针。
可以这么理解:对于一个类myclass,this指针的类型应该是myclass *,而对其的解引用*this就应该是一个myclass类型的变量。也就是说,this指针指向该类的实例对象,而不是该类本身。
50、以下 C 语言指令:
int a[5] = { 1,3,5,7,9 };
int *p = (int *)(&a + 1);
printf("%d, %d", *(a + 1) , *(p - 1));
运行结果是什么?
2,1
3,1
3,9
运行时崩溃
KEY:C
解答:区分一下&a、a、&a[0]:
- &a和a做右值时的区别:&a是整个数组的首地址,而a是数组首元素的首地址。这两个在数字上是相等的,但是意义不相同。意义不相同会导致他们在参与运算的时候有不同的表现;
- a和&a[0]做右值时意义和数值完全相同,完全可以互相替代。
&a表示一个指向大小为5数组的指针,那么(&a+1)就是表示一个指向大小为5的下一个数组的指针,也就是数组a最后一个元素的下一个位置,-1则指向a中最后一个元素;a是保存数组的首地址,*(a+1)保存的是数组第二个地址的值,故为3.p是保存数组a最后一个位置的下一个位置的地址,*p是指向数组a最后一个位置的下一个位置,值为-1,*(p-1)是指向数组a最后一个位置,值为9。
【牛客网】C/C++牛客网专项刷题(03)相关推荐
- 【牛客网】C/C++牛客网专项刷题(02)
以下为牛客网C/C++专项刷题: 1.虚函数不可以内联,因为虚函数是在运行期的时候确定具体调用的函数,内联是在编译期的时候进行代码展开,两者冲突,所以没有一起使用的做法. 2.C++中构造函数和析构函 ...
- 【牛客网】C/C++牛客网专项刷题(00)
以下为牛客网C/C++专项刷题: 1.若要打开A盘上user子目录下名为abc.txt的文本文件进行读.写操作,符合此要求的函数调用是( ). KEY:fopen("A:\\user\\ab ...
- 【牛客网】C/C++牛客网专项刷题(01)
以下为牛客网C/C++专项刷题: 1.下面程序会输出什么: static int a=1; void fun1(void){ a=2; } void fun2(void){ int a=3; } vo ...
- 牛客网《剑指offer》专栏刷题练习之双指针算法的使用
✅作者简介:C/C++领域新星创作者,为C++和java奋斗中 ✨个人社区:微凉秋意社区
- 牛客网《剑指offer》专栏刷题练习之数组专精
✅作者简介:C/C++领域新星创作者,CSDN内容合伙人 ✨个人社区:微凉秋意社区
- 手把手带你刷好题(牛客刷题⑤)
作者:月亮嚼成星~ 博客主页:月亮嚼成星~的博客主页 专栏:手把手带你刷牛客 工欲善其事必先利其器,给大家介绍一款超牛的斩获大厂offer利器--牛客网 点击免费注册和我一起刷题吧 1.类中的数据域使 ...
- sql里查询一个组和组的用户数怎么查?_【牛客网SQL刷题】留存率怎么算?
抽空刷了牛客网SQL实战72题,最后几道以牛客网为例的题目还挺有挑战性,在此记录 统计时间段新用户次日留存率 每日的次日留存率 每日的新用户数 每日新用户的次日留存 求新登录用户次日留存 表login ...
- 牛客网Java刷题知识点之关键字static、static成员变量、static成员方法、static代码块和static内部类...
不多说,直接上干货! 牛客网Java刷题知识点之关键字static static代表着什么 在Java中并不存在全局变量的概念,但是我们可以通过static来实现一个"伪全局"的概 ...
- 洛谷、牛客网、AcWing 刷题(python版)
牛客网python专项练习整理(一) https://blog.csdn.net/weixin_41913008/article/details/87203468 牛客网剑指offer--python ...
最新文章
- java webdriver page object_Selenium+PageObject+Java实现测试用例
- C#连接sqlserver windows 和 sqlserver 身份验证的两种连接字符串
- Java高并发编程:多个线程之间共享数据的方式探讨
- Neko and Aki's Prank
- jquery使用技巧总结
- java webservice用户验证_使用java webservice的.net4.0 web app需要Usernametoken身份验证
- 【记录一个问题】linux+opencv+cuvid解码1080P视频,当使用CUDA核函数的时候,必然崩溃...
- linux物理网卡地址没有,Linux更改网卡物理地址
- ZMQ == 服务端创建,接受请求的过程
- C# 格式化json移除空值,参数按照第一个字符的键值 ASCII 码递增排序(SM2签名)
- python去重列表,并获取重复值索引
- 盛大剥离新业务:陈大年控股
- Ubuntu下显示Git仓库分支信息
- 上班族做什么副业赚钱?全面解析副业赚钱模式!
- 华为鸿蒙六月更新机型,华为鸿蒙OS带来好消息,这8款机型6月2日升级,有你的手机吗?...
- 又一经典音乐,不说了,你懂得
- DTOJ#5238. 庆生会
- 最近写了一个预测体彩3D的小软件
- liunx服务器 telnet 带用户名 端口登陆方法
- 每天汇总----10.18