找工作笔试面试那些事儿(8)---常问的CC++基础题
这一部分是C/C++程序员在面试的时候会被问到的一些题目的汇总。来源于基本笔试面试书籍,可能有一部分题比较老,但是这也算是基础中的基础,就归纳归纳放上来了。大牛们看到一笑而过就好,普通人看看要是能补上一两个模糊的知识点,也算有点进步吧。
1.简述变量声明和定义的区别。
为变量分配地址和存储空间的称为定义,不分配地址的称为声明。一个变量可以在多个地方声明,但是只在一个地方定义。加入extern修饰的是变量的声明,说明此变量将在文件以外或在文件后面部分定义。
2.简述sizeof和strlen的区别
最常考察的题目之一。主要区别如下:
1)sizeof是一个操作符,strlen是库函数。
2)sizeof的参数可以是数据的类型,也可以是变量,而strlen只能以结尾为‘\0‘的字符串作参数。
3)编译器在编译时就计算出了sizeof的结果。而strlen 函数必须在运行时才能计算出来。并且sizeof计算的是数据类型占内存的大小,而strlen计算的是字符串实际的长度。
4)数组做sizeof的参数不退化,传递给strlen就退化为指针了。
3.说说C和C++中的static有什么作用
这个真的在面试的时候被问过。
在C中static用来修饰局部静态变量和外部静态变量、函数。而C++中除了上述功能外,还用来定义类的成员变量和函数。即静态成员和静态成员函数。编程时最常用的是static的记忆性,和全局性的特点可以让在不同时期调用的函数进行通信,传递信息,而C++的静态成员则可以在多个对象实例间进行通信,传递信息。
4.C和C++中动态内存分配有什么方法,有何区别
C里面一般用malloc/free,C++可用malloc/free和new/delete。区别见找工作笔试面试那些事儿(3)---内存管理那些事中的内容。
5.简述C、C++程序编译的内存分配情况
之前提过,主要有静态存储区分配,在堆上和栈上分配三种,具体场景见找工作笔试面试那些事儿(3)---内存管理那些事中的内容。
6.说说strcpy、sprintf与memcpy三个函数
三个函数的功能分别为:
strcpy:实现字符串变量间的拷贝
sprintf:主要实现其他数据类型格式到字符串的转化
Memcpy:主要是内存块间的拷贝
它们的区别有:
(1)操作对象不同,strcpy的两个操作对象均为字符串,sprintf的操作源对象可以是多种数据类型,目的操作对象是字符串,memcpy 的两个对象就是两个任意可操作的内存地址,并不限于何种数据类型。
(2)执行效率不同,memcpy最高,strcpy次之,sprintf的效率最低。
7.说说拷贝构造函数和赋值运算符
拷贝构造函数和赋值运算符有以下两个不同之处:
(1)拷贝构造函数生成新的类对象,而赋值运算符不能。
(2)由于拷贝构造函数是直接构造一个新的类对象,所以在初始化这个对象之前不用检验源对象是否和新建对象相同。而赋值运算符则需要这个操作,另外赋值运算中如果原来的对象中有内存分配要先把内存释放掉(这一点在之前找工作笔试面试那些事儿(5)---构造函数、析构函数和赋值函数中提到了)。
8.简述类成员函数的重写、重载和隐藏的区别
见找工作笔试面试那些事儿(4)---C++函数高级特征中所述。
9.用递归和非递归两种方法翻转一个链表
先定义一下链表:
int func(x) { int countx = 0; while(x) { countx ++; x = x&(x-1); } return countx; }17.将“引用”作为函数返回值类型的格式、好处和需要遵守的规则?
格式:类型标识符&函数名(形参列表及类型说明){ //函数体}
好处:在内存中不产生被返回值的副本;(注意:正是因为这点原因,所以返回一个局部变量的引用是不可取的。因为随着该局部变量生存期的结束,相应的引用也会失效,产生
runtime error!) 注意事项:
(1)不能返回局部变量的引用。
主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态。
(2)不能返回函数内部new分配的内存的引用。
虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其它尴尬局面。例如,被函数返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成内存泄露。
(3)可以返回类成员的引用,但最好是const。
(4)流操作符重载返回值申明为“引用”的作用:
流操作符<<和>>,这两个操作符常常希望被连续使用,例如:cout << "hello" << endl;
因此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。
(5)在另外的一些操作符中,却千万不能返回引用:+-*/ 四则运算符。它们不能返回引用。 主要原因是这四个操作符没有side effect,因此,它们必须构造一个对象作为返回值,可选的方案包括:返回一个对象、返回一个局部变量的引用,返回一个new分配的对象的引用、返回一个静态对象引用。
18.谈谈对于关联、聚合(Aggregation)以及组合(Composition)的认识
涉及到UML中的一些概念:
关联是表示两个类的一般性联系,比如“学生”和“老师”就是一种关联关系;
聚合表示has-a的关系,是一种相对松散的关系,聚合类不需要对被聚合类负责,用空的菱形表示聚合关系:从实现的角度讲,聚合可以表示为:
class A {...} class B { A* a; .....}
组合表示contains-a的关系,关联性强于聚合:组合类与被组合类有相同的生命周期,组合类要对被组合类负责,采用实心的菱形表示组合关系:实现的形式是:
class A{...} class B{ A a; ...}
19.当一个类C 中没有任何成员变量与成员函数,这时sizeof(C)的值是多少。如果不是零,请解释一下编译器为什么没有让它为零。
一个空类对象的大小是1byte。这是被编译器安插进去的一个字节,这样就使得这个空类的两个实例得以在内存中配置独一无二的地址。
20.用变量a给出下面的定义
a) 一个整型数(An integer)
b) 一个指向整型数的指针(A pointer to an integer)
c) 一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a pointer to an
integer)
d) 一个有10个整型数的数组(An array of 10 integers)
e) 一个有10个指针的数组,该指针是指向一个整型数的(An array of 10 pointers to integers)
f) 一个指向有10个整型数数组的指针(A pointer to an array of 10 integers)
g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function
that takes an integer as an argument and returns an integer)
h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型
数( An array of ten pointers to functions that take an integer argument and return an integer )
非常非常经典的一道题,很多笔试面试题是从上述a-h中的一个或者几个,答案如下:
a) int a; // An integer
b) int *a; // A pointer to an integer
c) int **a; // A pointer to a pointer to an integer
d) int a[10]; // An array of 10 integers
e) int *a[10]; // An array of 10 pointers to integers
f) int (*a)[10]; // A pointer to an array of 10 integers
g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer
h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return
an integer
21.成员函数通过什么来区分不同对象的成员数据?为什么它能够区分?
通过this指针来区分的, 因为它指向的是对象的首地址。
22.拷贝构造函数在哪几种情况下会被调用?
1).当类的一个对象去初始化该类的另一个对象时;
2).如果函数的形参是类的对象,调用函数进行形参和实参结合时;
3).如果函数的返回值是类对象,函数调用完成返回时。
23. 流运算符为什么不能通过类的成员函数重载?一般怎么解决?
因为通过类的成员函数重载必须是运算符的第一个是自己,而对流运算的重载要求第一个参数是流对象。一般通过友元来解决。
24. 虚拟函数与普通成员函数的区别?内联函数和构造函数能否为虚拟函数?
区别:虚拟函数有virtual关键字,有虚拟指针和虚函数表,虚拟指针就是虚拟函数的接口,而普通成员函数没有。内联函数和构造函数不能为虚拟函数。