您的位置:首页 > 编程语言 > C语言/C++

C++笔试面试常考知识点汇总(一)

2016-11-13 20:48 169 查看
1:大端与小端?与寻常习惯的区别?

大端:高地址存储低位字节。

小端:低地址存储低位字节。

如对于数据0x1234,其32位为0x00001234。

对于大端来说:(地址由低到高存储)00 00 12 34

对于小端来说:(地址由低到高存储)34 12 00 00

由此可以得出结论:大端的存储方式与寻常习惯相一致,而小端的存储方式为按照字节倒排。

2:#include< >与#include” “的区别?

优先搜索路径不同。

前者优先搜索标准库文件目论,因此对于标准库文件搜索效率高。

后者优先搜索自定义文件目录,然后搜索整个磁盘,对于自定义文件的搜索比较快。

3:C++将基类的析构函数定义为虚函数的目的?

若将基类的析构函数定义为虚函数,当删除一个指向派生类的基类指针时,首先会调用派生类的析构函数,然后再调用基类的析构函数;否则只会调用基类的析构函数。

4:mutable定义数据成员的意义?

mutable定义的数据成员,可以在const成员函数中进行修改。

5:怎样定义一个纯虚函数?纯虚函数与抽象类的关系?抽象类的特点?

将虚函数赋值为0即可得到一个纯虚函数;包含纯虚函数的类是抽象类。抽象类有以下特点:不能实例化;不能作为函数参数及函数返回类型;可以定义抽象类类型的指针。

抽象类一般作为基类使用。对于一个抽象基类,如果其派生类没有重新定义基类的虚函数,则派生类也还是抽象类,只有派生类重新定义了基类的虚函数,派生类才不再是抽象类,才是一个可以建立对象的具体类。

6:malloc与new的区别?

1)malloc是函数,而new是操作符

2)malloc申请内存时,需要我们指定申请的空间大小,且返回的类型为void*,需要将其强制转换为所需类型指针;new申请内存时,会根据所申请的类型自动计算申请空间的大小,且可直接返回指定类型的指针

3)malloc释放内存时,用free函数,而new删除对象时,用的是delete操作符

4)malloc/free申请释放内存时,不需要需要调用析构函数,而new/delete申请释放内存时需要调用析构函数

7:STL算法中的next_permutation()算法的用法?

如果序列已经是最后一个排列,则next_permutation将序列重排为最小的序列,并返回false。否则,它将输入序列转换为字典序中下一个排列,并返回true。

8:用位逻辑运算实现位向量的set、clr、test?

#define BITSPERWORD 32 //表示一个整型含有32个bit
#define SHIFT 5 //单位位移量
#define MASK 0x1F   //掩码
#define N 10000000  //表示有1000万个数
int a[1+N/BITSPERWORD]; //使用整型数组模拟定义1000万个位的数组

//i>>SHIFT指的是右移5位,也就是除以32,指的是该位存在于哪个数组中
//i&MASK指的是i%32,剩下的数字为多少,1<<(i&MASK))表示1左移i&MASK位
void set(int i){a[i>>SHIFT]|=(1<<(i&MASK));}
void clr(int i){a[i>>SHIFT]&=~(1<<(i&MASK));}
int test(int i){return a[i>>SHIFT]&(1<<(i&MASK));}


解析:拿set函数为例:对于形参i,首先将i除以32(i>>SHIFT)以找到i位于数组中哪一位置;i&MASK指的是i%32后剩下的数字为多少,然后进行左移(因为对于任意一个a[m],只有32位,现其已经限定到具体的某个a[m],因此其移位范围为0~32);最后通过或运算将a[m]中的0变为1。

clr函数通过非运算(该位置1取非成为0)与与运算(该位置1与0相与后成为0),将相应位置的1转化为0。test函数探测i位置上数据到底是1还是0(i位置上的数据与1相与,返回其真实值)。

9:二维数组作为参数传递的注意事项?

需要指明数组的第二维,第一维不是必须要显式指定。

10:建立动态二维数组的两种方法?在删除用new建立的二维数组时注意事项?

法1://申请空间

int ** a = new int *[row];//比一维的等号左右多了2个*而已
for(int i = 0;i < row;i++)
a[i] = new int[column];


该方法定义的动态二维数组的释放需先释放指针数组的每个元素指向的数组,然后再释放该指针数组:


for(int i = 0;i < row;i++)
{
delete a[i];
a[i] = NULL;
}
delete [row]a;
a = NULL;


vector<vector<int> > vec(row,vector<int>(column));//法2


11:sizeof运算符在编译阶段处理的特性?

sizeof操作符在其作用范围内,内容不会被编译,而只是简单替换成其类型。

12:sizeof(字符串序列)和sizeof(字符串数组)的区别?

sizeof(字符串序列)=字符串长度+1;(算上最后面的’\0’)

sizeof(字符串数组)=字符串长度;(用{}表示时)

13:sizeof(动态数组)?

动态数组实质上是一个指针,其占用空间为4/8(32位/64位系统)。

14:用strlen得到字符串的长度?

对于字符串序列,到第一个’\0’为止(’\0’自动添加且不计算在内);对于字符串数组,必须要显示指定’\0’。对于string型字符串,不能直接使用strlen,而需要使用strlen(s.c_str())计算。

char p[]="abcdefg";
char a[]={'a','b','c','\0'};
cout<<strlen(p)<<endl;//输出7
cout<<strlen(a)<<endl;//输出3


15:sizeof(联合)需要注意的占用空间最大的成员及对齐方式问题?

联合与结构的区别:结构中各成员有各自的内存空间,结构变量所占用内存空间是各成员所占用内存空间之和;联合中,各成员共享一段内存空间,一个联合变量所占用内存空间等于各成员中所占用内存空间最大的变量所占用内存空间(需考虑内存对齐问题),此时共享意味着每次只能赋一种值,赋入新值则冲去旧值。

16:sizeof(class)需注意情况?

sizeof只计算数据成员的大小;不计算static数据成员的大小;继承时需要考虑基类数据成员问题,考虑虚函数问题,无论有多少个虚函数,计算空间时虚函数表只计算一次。

补充:当派生类存在多重继承时,sizeof运算结果?

class A
{
int a;
int b;
virtual void fun()
{
}
};
class B
{
int c;
virtual void fun1()
{
}
virtual void fun2()
{
}
};
class C:public A,public B
{
int d;
};

int main( )
{
cout<<sizeof(A)<<endl;//输出12
cout<<sizeof(B)<<endl;//输出8
cout<<sizeof(C)<<endl;//输出24
return 0;
}


对于同一个类的不同虚函数,只需考虑一次即可,而对于不同类的虚函数,派生类需要都考虑(每一个类虚函数只需考虑一次)。可参考虚函数表里的内存分配原则。


17:rand()函数的用法?srand函数的用法?

rand();
srand((unsigned int)(time NULL));


18:++a与a++的问题

++a得到左值,而a++得到右值。

19:静态分配与动态分配的区别?

静态分配是指在编译期间就能确定内存的大小,由编译器分配内存。动态分配是指在程序运行期间,由程序员申请的内存空间。堆和栈都可以动态分配,但静态分配只能是栈。

20:构造函数的列表初始化的初始化顺序?哪三种情况是必须的?

1:const数据成员;2:引用数据成员;3:没有默认构造函数的成员对象

对于static成员:若是static const成员,可以在类内初始化,也可以在类外初始化;若是普通的static成员,必须在类外初始化。


21:C++的转义序列?\abc与\xabc分别表示什么?

\abc表示8进制转义序列(\后最多跟三位数字);\xabc表示16进制的转义序列。

补充:\\也是一个转义字符

22:当编译器处理一个const时会将其转化成一个立即数。

23:为什么不能重新定义一个继承而来的默认参数?

因为从基类继承来的缺省参数值都是静态绑定的,而virtual函数——你唯一应该覆盖的东西,却是动态绑定。

24:为什么不能重新定义继承而来的非虚函数?

因为非虚函数是静态绑定的,如果重新定义继承而来的非虚函数,则指向派生类的基类指针在调用该非虚函数时,将可能会产生行为异常。

25:(做题经验)短路求值问题一定要细心运算。

一定要细心,判断是否一定会对&&或||后面的操作进行运算

26:深拷贝与浅拷贝的区别?

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

换种解释:浅拷贝,只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。

浅拷贝会出现什么问题呢?

其一,浅拷贝只是拷贝了指针,使得两个指针指向同一个地址,这样在对象块结束,调用函数析构时,会造成同一份资源析构2次,即delete同一块内存2次,造成程序崩溃。

其二,浅拷贝使得源对象和拷贝对象指向同一块内存,任何一方的变动都会影响到另一方。

其三,在释放内存的时候,会造成拷贝对象原有的内存没有被释放造成内存泄露。(源对象内存被释放后,由于源对象和拷贝对象指向同一个内存空间,拷贝对象的空间不能再被利用了,删除拷贝对象不会成功,无法操作该空间,所以导致内存泄露)

类的静态成员是所有类的实例共有的,存储在全局(静态)区,只此一份,不管继承、实例化还是拷贝都是一份。因此类的静态成员不允许深拷贝。

27:如何只让类对象只在栈(堆)上分配对象?

1:只在堆上分配对象

动态建立对象,是使用new运算符将对象建立在堆空间中。过程分为2步,第一步是执行operator new()函数,在堆空间中搜索适合的内存并进行分配;第二步是调用构造函数构造对象,初始化这片内存空间。这种方法,间接调用类的构造函数。

将构造函数设为私有,将会阻止在堆上分配对象,是不行的。

将析构函数设为私有(为了考虑继承,可将析构函数设为受保护的)。当对象建立在栈上面时,是由编译器分配内存空间的,调用构造函数来构造栈对象。当对象使用完后,编译器会调用析构函数来释放栈对象所占的空间。编译器管理了对象的整个生命周期。编译器在为类对象分配栈空间时,会先检查类的析构函数的访问性,其实不光是析构函数,只要是非静态的函数,编译器都会进行检查。如果类的析构函数是私有的,则编译器不会在栈空间上为类对象分配内存。因此,将析构函数设为私有,类对象就无法建立在栈上了。

2:只在栈上分配对象

只要禁用new运算符(设为私有)就可以实现类对象只能建立在栈上。

28:char扩展为int的两种情况及注意事项?

将char转换为int时关键看char是unsigned还是signed,如果是unsigned就执行0扩展,如果是signed就执行符号位扩展。跟int本身是signed还是unsiged无关。

而同等位数的类型之间的赋值表达式不会改变其在内存之中的表现形式,即如果signed char a = 0xe0; unsigned char c = a;则c=0xe0;

29:面向过程static全局变量与全局变量的区别?

1)全局变量默认是有外部链接性的,作用域是整个工程,在一个文件内定义的全局变量,在另一个文件中,通过extern 全局变量名的声明,就可以使用全局变量。

2)静态全局变量是显式用static修饰的全局变量,作用域是声明此变量所在的文件,其他的文件即使用extern声明也不能使用。

30:面向过程静态局部变量的特点?

1)在全局数据区分配内存;

2)在程序执行到该对象的声明处时被首次初始化,且以后不再初始化;

3)一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0;

4)它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束;

31:面向对象静态数据成员的特点?

静态数据成员被当作是类的成员。只分配一次内存,供所有对象共用。

静态数据成员存储在全局数据区。静态数据成员定义时要分配空间,所以不能在类声明中定义(const static成员可以在类中定义)。

不属于特定的类对象,在没有产生类对象时其作用域就可见,即在没有产生类的实例时,我们就可以操作它。

静态数据成员初始化与一般数据成员初始化不同。静态数据成员初始化的格式为:

数据类型 类名:: 静态数据成员名=值

类的静态数据成员有两种访问形式:

类对象名.静态数据成员名 或 类类型名::静态数据成员名

静态数据成员主要用在各个对象都有相同的某项属性的时候。比如对于一个存款类,每个实例的利息都是相同的。所以,应该把利息设为存款类的静态数据成员。这有两个好处,第一,不管定义多少个存款类对象,利息数据成员都共享分配在全局数据区的内存,所以节省存储空间。第二,一旦利息需要改变时,只要改变一次,则所有存款类对象的利息全改变过来了;

同全局变量相比,使用静态数据成员有两个优势:

不存在与程序中其它全局名字冲突的可能性;

可以实现信息隐藏。静态数据成员可以是private成员,而全局变量不能;

32:面向对象静态成员函数的特点?

普通的成员函数一般都隐含了一个this指针,this指针指向类的对象本身,因为普通成员函数总是具体的属于某个类的具体对象的。但是与普通函数相比,静态成员函数由于不与任何的对象相联系,因此它不具有this指针。从这个意义上讲,它无法访问属于类对象的非静态数据成员,也无法访问非静态成员函数,它只能调用其余的静态成员。

注意:出现在类体外的函数定义不能指定关键字static;

使用方法:可以用成员访问操作符(.)和(->;)为一个类的对象或指向类对象的指针调用静态成员函数,也可以直接:类名::静态成员函数名(参数表)

33:静态成员与非静态成员之间的可访问性?

静态成员之间可以互相访问;非静态成员可以访问静态成员及非静态成员,而静态成员不能访问非静态成员。

34:memcpy的用法与strcpy之间的区别?

memcpy函数的功能是从源指针所指的内存地址的起始位置开始拷贝n个字节到目标指针所指的内存地址的起始位置中。

void *memcpy(void *dest, const void *src, size_t n); //函数返回指向dest的指针

strcpy和memcpy主要有以下3方面的区别。

1)复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。

2)复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符”\0”才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。

3)用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy。

35:前置递增递减运算符的运算对象必须是左值。

36:不能声明为虚函数的几种情况?

1)普通函数(非类成员函数)(不能被覆盖)

2)友元函数(C++不支持友元函数继承)

3)内联函数(编译期间展开,虚函数是在运行期间绑定)

4)构造函数(没有对象不能使用虚函数,先有构造函数后有虚函数,虚函数是对对象的动作(构造函数不能继承))

5)静态成员函数(只有一份大家共享)

37:虚函数的定义及功能?

定义:在某基类中声明为 virtual 并在一个或多个派生类中被重新定义的非static成员函数。

功能:实现多态性,通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数。

38:抽象类的特点?

含有纯虚拟函数的类称为抽象类,它不能生成对象。

在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。如动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。

抽象类只能作为基类来使用,其纯虚函数的实现由派生类给出。如果派生类中没有重新定义纯虚函数,而只是继承基类的纯虚函数,则这个派生类仍然还是一个抽象类。如果派生类中给出了基类纯虚函数的实现,则该派生类就不再是抽象类了,它是一个可以建立对象的具体的类。

39:malloc、alloc、calloc、realloc的区别?

alloc:唯一在栈上申请内存的,无需释放;

malloc:在堆上申请内存,最常用;

calloc:malloc+初始化为0;

realloc:将原本申请的内存区域扩容,参数size大小即为扩容后大小,因此此函数要求size大小必须大于ptr内存大小。

40:STL算法中partition算法的用法?

对指定范围内的元素重新排序,使用输入的函数,把结果为true的元素放在结果为false的元素之前。stable_partition版本保留原始容器中的相对顺序。如使数组中奇数位于偶数前面。

41:什么是多态?

多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向派生类的基类的指针或引用,来调用实现派生类中的方法。

42:dynamic_cast的作用?

dynamic_cast将一个基类对象指针(或引用)(需指向派生类或为派生类的引用)cast到派生类指针。dynamic_cast会根据基类指针是否真正指向继承类对象来做相应处理,即会作一定的判断。对指针进行dynamic_cast,失败返回null,成功返回正常cast后的对象指针; 对引用进行dynamic_cast,失败抛出一个异常,成功返回正常cast后的对象引用。

43:在32位系统中和64为中long分别占用多少字节?

32位:4字节;64位:8字节。

44:#define与typedef的区别?

1) #define是预处理指令,在编译预处理时进行简单的替换,不作正确性检查。

2)typedef是在编译时处理的。它在自己的作用域内给一个已经存在的类型一个别名。

3)typedef int * int_ptr;与#define int_ptr int * (注意没有分号,否则连同分号一起替换)

作用都是用int_ptr代表 int * ,但是二者不同,
c444
正如前面所说 ,#define在预处理时进行简单的替换,而typedef不是简单替换 ,而是采用如同定义变量的方法那样来声明一种类型。

这也说明了为什么下面观点成立

typedef int * pint ;
#define PINT int *


那么:

const pint p ;//p不可更改,但p指向的内容可更改
const PINT p ;//p可更改,但是p指向的内容不可更改。


pint是一种指针类型 const pint p 就是把指针给锁住了 p不可更改

而const PINT p 是const int * p 锁的是指针p所指的对象。

45:堆和栈的区别?

1)栈,由编译器自动管理,无需我们手工控制;堆,申请释放工作由程序员控制

2)堆的生长方向是向上的,也就是向着内存地址增加的方向;栈的生长方向是向下的,是向着内存地址减小的方向增长。

3)对于堆来讲,频繁的 new/delete 势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题

4)一般来讲在 32 位系统下,堆内存可以达到4G的空间,但是对于栈来讲,一般都是有一定的空间大小的(2M)。

46:C、C++源代码要经过哪些步骤才生成可执行文件?各步骤的作用?

C/C++源代码要经过:预处理、编译、连接三步才能变成相应平台下的可执行文件。

预处理:主要是编译器对各种预处理命令进行处理,包括头文件的包含、宏定义的扩展、条件编译的选择等。

编译:进行词法与语法分析,首先编译成纯汇编语句,再将之汇编成跟CPU相关的二进制码,生成各个目标文件。

连接:链接是处理可重定位文件,把它们的各种符号引用和符号定义转换为可执行文件中的合适信息(一般是虚拟内存地址) 的过程。

47:公有继承、私有继承后基类public、protected、private成员在派生类中的访问权限?

有如下表格解释

基类中的public成public继承public
基类中的protected成员public继承protected
基类中的private成员public继承不可访问
基类中的public成员protected继承protected
基类中的protected成员protected继承protected
基类中的private成员protected继承不可访问
基类中的public成员private继承private
基类中的protected成员private继承private
基类中的private成员private继承不可访问
注意:派生类的对象只可访问public继承的基类中的public成员。

48:指针和引用的相同点与区别?

相同点:

1. 都是地址的概念;

指针指向一块内存,它的内容是所指内存的地址;引用是某块内存的别名。

区别:

1. 指针是一个实体,而引用仅是个别名;

2. 引用使用时无需解引用(*),指针需要解引用;

3. 引用只能在定义时被初始化一次,之后不可变;指针可变;

4. 引用不能为空,指针可以为空;

5. “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小;

6. 指针和引用的自增(++)运算意义不一样;引用的++实际上是绑定引用的对象的++,而指针的++,将指向指针之后的内存

7.从内存分配上看:程序为指针变量分配内存区域,而引用不需要分配内存区域。

49:初始化与赋值的区别?

初始化不是赋值,初始化的含义是创建变量时赋予其一个初始值,而赋值的含义是把对象的当前值擦除,而以一个新值来替代。

50:声明与定义的区别?

为了支持分离式编译,C++将声明和定义区分开来。声明使得名字为程序所知,一个文件如果想使用别处定义的名字则必须包含对那个名字的声明。定义负责创建与名字关联的实体。

简单来说,声明规定了变量的类型和名字,在这一点上与定义相同。但定义还申请了内存空间,也可能会为变量赋一个初始值。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息