常见C++面试题

声明

  • 本文包含三篇转自互联网的文章
  • 不同文章中有可能出现相同问题,也许解释相似,或许不同,建议仔细推敲,暂时因时间关系无法筛选
  • 文章中或许有未解释问题,若别的文章里没有,有空时我会更新
  • 略有改动,不影响阅读

C++ 面试出现频率最高的 30 道题目

new、delete、malloc、free 关系

  • delete 会调用对象的析构函数,和 new 对应 free 只会释放内存,new 调用构造函数。
  • malloc 与 free 是 C++/C 语言的标准库函数,new/delete 是 C++ 的运算符。
  • 它们都可用于申请动态内存和释放内存。
  • 对于非内部数据类型的对象而言,光用 maloc/free 无法满足动态对象的要求。
  • 对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。
  • 由于 malloc/free 是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于 malloc/free。
  • 因此 C++ 语言需要一个能完成动态内存分配和初始化工作的运算符 new,以及一个能完成清理与释放内存工作的运算符 delete。
  • 注意 new/delete 不是库函数。

delete 与 delete[]区别

  • delete 只会调用一次析构函数,而 delete[] 会调用每一个成员的析构函数。
  • 在 More Effective C++ 中有更为详细的解释:“当 delete 操作符用于数组时,它为每个数组元素调用析构函数,然后调用 operator delete 来释放内存。”
  • delete 与 new 配套,delete[] 与 new[] 配套
  • delete 和 delete[] 功能是相同的。对于自定义的复杂数据类型,delete 和 delete[]不能互用。delete[] 删除一个数组,delete 删除一个指针。
  • 简单来说,用 new 分配的内存用 delete 删除;用 new[] 分配的内存用 delete[] 删除。elete[] 会调用数组元素的析构函数。内部数据类型没有析构函数,所以问题不大。如果你在用 delete 时没用括号,delete 就会认为指向的是单个对象,否则,它就会认为指向的是一个数组。

C++有哪些性质(面向对象特点)

封装,继承和多态。

子类析构时要调用父类的析构函数吗?

  • 析构函数调用的次序是先派生类的析构后基类的析构,也就是说在基类的的析构调用的时候,派生类的信息已经全部销毁了。
  • 定义一个对象时先调用基类的构造函数、然后调用派生类的构造函数;析构的时候恰好相反:先调用派生类的析构函数、然后调用基类的析构函数。

多态,虚函数,纯虚函数

  • 多态:是对于不同对象接收相同消息时产生不同的动作。C++的多态性具体体现在运行和编译两个方面:
    • 在程序运行时的多态性通过继承和虚函数来体现;
    • 在程序编译时多态性体现在函数和运算符的重载上;
  • 虚函数:在基类中冠以关键字 virtual 的成员函数。 它提供了一种接口界面。允许在派生类中对基类的虚函数重新定义。
  • 纯虚函数的作用:在基类中为其派生类保留一个函数的名字,以便派生类根据需要对它进行定义。作为接口而存在 纯虚函数不具备函数的功能,一般不能直接被调用。
    • 从基类继承来的纯虚函数,在派生类中仍是虚函数。如果一个类中至少有一个纯虚函数,那么这个类被称为抽象类(abstract class)。
    • 抽象类中不仅包括纯虚函数,也可包括虚函数。抽象类必须用作派生其他类的基类,而不能用于直接创建对象实例。但仍可使用指向抽象类的指针支持运行时多态性。

求下面函数的返回值(微软)

1
2
3
4
5
6
7
8
9
10
int func(x) 
{
int countx = 0;
while(x)
{
countx ++;
x = x&(x-1);
}
return countx;
}

假定 x = 9999。 答案:8
思路:将 x 转化为 2 进制,看含有的 1 的个数。

什么是“引用”?申明和使用“引用”要注意哪些问题?

  • 引用就是某个目标变量的“别名” (alias),对应用的操作与对变量直接操作效果完全相同。
  • 申明一个引用的时候,切记要对其进行初始化。
  • 引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,不能再把该引用名作为其他变量名的别名。
  • 声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。
  • 不能建立数组的引用。

将“引用”作为函数参数有哪些特点?

  1. 传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。
  2. 使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。
  3. 使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用”*指针变量名”的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。

在什么时候需要使用“常引用”? 

如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。

常引用声明方式:const 类型标识符 &引用名 = 目标变量名;

  • 例1
1
2
3
4
int a ;
const int &ra=a;
ra=1; //错误
a=1; //正确
  • 例2
1
2
string foo();
void bar(string & s);

那么下面的表达式将是非法的:

1
2
bar(foo());
bar("hello world");

原因在于 foo() 和 “hello world” 串都会产生一个临时对象,而在 C++ 中,这些临时对象都是 const 类型的。因此上面的表达式就是试图将一个 const 类型的对象转换为非 const 类型,这是非法的。引用型参数应该在能被定义为 const 的情况下,尽量定义为 const。

将“引用”作为函数返回值类型的格式、好处和需要遵守的规则?

  • 格式:类型标识符 &函数名(形参列表及类型说明){ //函数体 }
  • 好处:在内存中不产生被返回值的副本;(注意:正是因为这点原因,所以返回一个局部变量的引用是不可取的。因为随着该局部变量生存期的结束,相应的引用也会失效,产生runtime error!
  • 注意事项:
    • 不能返回局部变量的引用。这条可以参照Effective C++[1]的Item 31。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了”无所指”的引用,程序会进入未知状态。
    • 不能返回函数内部new分配的内存的引用。这条可以参照Effective C++[1]的Item 31。虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其它尴尬局面。例如,被函数返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。
    • 可以返回类成员的引用,但最好是const。这条原则可以参照Effective C++[1]的Item 30。主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。
  • 流操作符重载返回值申明为“引用”的作用:
    • 流操作符<<和>>,这两个操作符常常希望被连续使用,例如:cout << “hello” << endl; 因此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。
    • 可选的其它方案包括:返回一个流对象和返回一个流对象指针。
    • 但是对于返回一个流对象,程序必须重新(拷贝)构造一个新的流对象,也就是说,连续的两个<<操作符实际上是针对不同对象的!这无法让人接受。对于返回一个流指针则不能连续使用<<操作符。因此,返回一个流对象引用是惟一选择。这个唯一选择很关键,它说明了引用的重要性以及无可替代性,也许这就是C++语言中引入引用这个概念的原因吧。
  • 赋值操作符=。这个操作符象流操作符一样,是可以连续使用的,例如:x = j = 10;或者(x=10)=100;赋值操作符的返回值必须是一个左值,以便可以被继续赋值。因此引用成了这个操作符的惟一返回值选择。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<iostream>
int &put(int n);
int vals[10];
int error=-1;
int main()
{
put(0)=10; //以put(0)函数值作为左值,等价于vals[0]=10;
put(9)=20; //以put(9)函数值作为左值,等价于vals[9]=20;
cout<<vals[0];
cout<<vals[9];
}
int &put(int n)
{
if (n>=0 && n<=9 ) return vals[n];
else { cout<<"subscript error"; return error; }
}
  • 在另外的一些操作符中,却千万不能返回引用:+-*/ 四则运算符。它们不能返回引用,Effective C++[1] 的 Item23 详细的讨论了这个问题。主要原因是这四个操作符没有 side effect,因此,它们必须构造一个对象作为返回值,可选的方案包括:返回一个对象、返回一个局部变量的引用,返回一个 new 分配的对象的引用、返回一个静态对象引用。根据前面提到的引用作为返回值的三个规则,第 2、3 两个方案都被否决了。静态对象的引用又因为 ((a+b)==(c+d)) 会永远为 true 而导致错误。所以可选的只剩下返回一个对象了。

结构与联合有和区别?

  1. 结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)。
  2. 对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的。

试写出程序结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int  a=4;

int &f(int x)
{ a=a+x;
return a;
}

int main()
{
int t=5;
cout<<f(t)<<endl; //a = 9
f(t)=20; //a = 20
cout<<f(t)<<endl; //t = 5,a = 20 -> a = 25
t=f(t); //a = 30 t = 30
cout<<f(t)<<endl; //t = 60
}

重载 ( overload ) 和重写( overried,有的书也叫做“覆盖”)的区别?

  • 从定义上来说:

    • 重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。
    • 重写:是指子类重新定义父类虚函数的方法。
  • 从实现原理上来说:

    • 重载:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:function func(p:integer):integer;和function func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的:int_func、str_func。对于这两个函数的调用,在编译器间就已经确定了,是静态的。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!
    • 重写:和多态真正相关。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚绑定)。

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

当类中含有const、reference 成员变量;基类的构造函数都需要初始化表。

C++ 是不是类型安全的?

不是。两个不同类型的指针之间可以强制转换(用reinterpret cast)。C#是类型安全的。

main 函数执行以前,还会执行什么代码?

全局对象的构造函数会在main 函数之前执行。

描述内存分配方式以及它们的区别?

  1. 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。
  2. 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。
  3. 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多。

分别写出 bool,int,float,指针类型的变量 a 与“零”的比较语句。

  • bool : if(!a) or if(a)
  • int : if(a==0) 或 if(!a)
  • float : const EXPRESSION EXP=0.000001 if (a<EXP && a>-EXP)
  • pointer : if (a != NULL) or if(a == NULL)

请说出 const 与 #define 相比,有何优点?

const作用:定义常量、修饰函数参数、修饰函数返回值三个作用。被Const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。

  1. const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
  2. 有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。

简述数组与指针的区别?

数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。

  1. 修改内容上的差别

    1
    2
    3
    4
    char a[] = “hello”;
    a[0] = ‘X’;
    char *p = “world”; // 注意p 指向常量字符串
    p[0] = ‘X’; // 编译器不能发现该错误,运行时错误
  2. 用运算符sizeof 可以计算出数组的容量(字节数)。sizeof(p),p 为指针得到的是一个指针变量的字节数,而不是p 所指的内存容量。C++/C 语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    char a[] = "hello world";
    char *p = a;
    cout<< sizeof(a) << endl; // 12 字节
    cout<< sizeof(p) << endl; // 4 字节
    //计算数组和指针的内存容量
    void Func(char a[100])
    {
    cout<< sizeof(a) << endl; // 4 字节而不是100 字节
    }

int (*s[10])(int) 表示的是什么?

int (*s[10])(int) 函数指针数组,每个指针指向一个int func(int param)的函数。

栈内存与文字常量区

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
char str1[] = "abc";
char str2[] = "abc";

const char str3[] = "abc";
const char str4[] = "abc";

const char *str5 = "abc";
const char *str6 = "abc";

char *str7 = "abc";
char *str8 = "abc";

cout << ( str1 == str2 ) << endl;//0 分别指向各自的栈内存
cout << ( str3 == str4 ) << endl;//0 分别指向各自的栈内存
cout << ( str5 == str6 ) << endl;//1指向文字常量区地址相同
cout << ( str7 == str8 ) << endl;//1指向文字常量区地址相同
  • 结果是:0 0 1 1
  • 解答:str1,str2,str3,str4是数组变量,它们有各自的内存空间;而str5,str6,str7,str8是指针,它们指向相同的常量区域。

将程序跳转到指定内存地址

要对绝对地址0x100000赋值,我们可以用(unsigned int*)0x100000 = 1234;那么要是想让程序跳转到绝对地址是0x100000去执行,应该怎么做?

1
2
3
4
5
6
7
8
*((void (*)( ))0x100000) ( );
//首先要将0x100000强制转换成函数指针,即:
(void (*)())0x100000
//然后再调用它:
*((void (*)())0x100000)();
//用typedef可以看得更直观些:
typedef void(*)() voidFuncPtr;
*((voidFuncPtr)0x100000)();

int id[sizeof(unsigned long)]; 这个对吗?为什么?

正确 这个 sizeof是编译时运算符,编译时就确定了 ,可以看成和机器有关的常量。

引用与指针有什么区别?

  1. 引用必须被初始化,指针不必。
  2. 引用初始化以后不能被改变,指针可以改变所指的对象。
  3. 不存在指向空值的引用,但是存在指向空值的指针。

const 与 #define 的比较,const有什么优点?

  1. const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误(边际效应) 。
  2. 有些集成化的调试工具可以对 const 常量进行调试,但是不能对宏常量进行调试。

复杂声明

1
2
3
void * ( * (*fp1)(int))[10];
float (*(* fp2)(int,int,int))(int);
int (* ( * fp3)())[10]();

分别表示什么意思?

  1. void * ( * (*fp1)(int))[10];

    fp1是一个指针,指向一个函数,这个函数的参数为int型,
    函数的返回值是一个指针,这个指针指向一个数组,
    这个数组有10个元素,每个元素是一个void*型指针。

  2. float (( fp2)(int,int,int))(int);

    fp2是一个指针,指向一个函数,这个函数的参数为3个int型,
    函数的返回值是一个指针,这个指针指向一个函数,
    这个函数的参数为int型,函数的返回值是float型。

  3. int (* ( * fp3)())[10]();

    fp3是一个指针,指向一个函数,这个函数的参数为空,
    函数的返回值是一个指针,这个指针指向一个数组,
    这个数组有10个元素,每个元素是一个指针,指向一个函数,
    这个函数的参数为空,函数的返回值是int型。

内存的分配方式有几种?

  1. 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量。
  2. 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
  3. 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。

基类的析构函数不是虚函数,会带来什么问题?

派生类的析构函数用不上,会造成资源的泄漏。

全局变量和局部变量有什么区别?是怎么实现的?操作系统和编译器是怎么知道的?

生命周期不同:

  • 全局变量随主程序创建和创建,随主程序销毁而销毁;
  • 局部变量在局部函数内部,甚至局部循环体等内部存在,退出就不存在;

使用方式不同:

  • 通过声明后全局变量程序的各个部分都可以用到;
  • 局部变量只能在局部使用;分配在栈区。

操作系统和编译器通过内存分配的位置来知道的,全局变量分配在全局数据段并且在程序开始运行的时候被加载。局部变量则分配在堆栈里面 。

C++面试集锦( 面试被问到的问题 )

C 和 C++ 区别

  • C 语言是面向过程的语言
  • C++ 是基于 C 语言开发的面向对象的语言,兼具面向对象与面向过程

const 有什么用途

主要有三点:

  1. 定义只读变量,即常量
  2. 修饰函数的参数和函数的返回值
  3. 修饰函数的定义体,这里的函数为类的成员函数,被 const 修饰的成员函数代表不修改成员变量的值

指针和引用的区别

  1. 引用是变量的一个别名,内部实现是只读指针
  2. 引用只能在初始化时被赋值,其他时候值不能被改变,指针的值可以在任何时候被改变
  3. 引用不能为 NULL,指针可以为 NULL
  4. 引用变量内存单元保存的是被引用变量的地址
  5. “sizeof 引用” = 指向变量的大小 , “sizeof 指针”= 指针本身的大小
  6. 引用可以取地址操作,返回的是被引用变量本身所在的内存单元地址
  7. 引用使用在源代码级相当于普通的变量一样使用,做函数参数时,内部传递的实际是变量地址

C++中有了 malloc/free , 为什么还需要 new/delete

  1. malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。
  2. 对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。
    1. 对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。
    2. 由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
  3. 因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。

编写类 String 的构造函数,析构函数,拷贝构造函数和赋值函数

多态的实现

单链表的逆置

堆和栈的区别

一个由c/C++编译的程序占用的内存分为以下几个部分

  1. 栈区(stack)― 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

  2. 堆区(heap)― 一般由程序员分配释放, 若程序员不释放,程序结束时可能由 OS 回收 。

    注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。

  3. 全局区(静态区)(static)― 全局变量和静态变量的存储是strcpy放在一块的,

    初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域
    程序结束后有系统释放

  4. 文字常量区 ― 常量字符串就是放在这里的。 程序结束后由系统释放

  5. 程序代码区―存放函数体的二进制代码。

不调用 C/C++ 的字符串库函数,编写 strcpy

strcpy函数的实现

1
2
3
4
5
6
7
8
9
char * strcpy(char * strDest,const char * strSrc)
{
if ((strDest==NULL)||strSrc==NULL))
return NULL;
char * strDestCopy=strDest;
while ((*strDest++=*strSrc++)!='\0');
*strDest = '\0';
return strDestCopy;
}

关键字 static 的作用

  1. 函数体内 static 变量的作用范围为该函数体,不同于 auto 变量, 该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值
  2. 在模块内的 static 全局变量可以被模块内所有函数访问,但不能被模块外其他函数访问
  3. 在模块内的 static 函数只可被这一模块内的其他函数调用,这个函数的使用范围被限制在声明它的模块内
  4. 在类的 static 成员变量属于整个类所拥有,对类的所以对象只有一份拷贝
  5. 在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的 static 成员变量

介绍它最重要的一条:隐藏。(static函数,static变量均可) –> 对应上面的 2、3 项

当同时编译多个文件时,所有未加 static 前缀的全局变量和函数都具有全局可见性。

举例来说明。同时编译两个源文件,一个是 a.c,另一个是 main.c。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//a.c
char a = 'A'; // global variable
void msg()
{
printf("Hello\n");
}
//main.c
int main()
{
extern char a; // extern variable must be declared before use
printf("%c ", a);
(void)msg();
return 0;
}

程序的运行结果是:
A Hello

为什么在 a.c 中定义的全局变量 a 和函数 msg能在 main.c 中使用?

  • 前面说过,所有未加static前缀的全局变量和函数都具有全局可见性,其它的源文件也能访问。此例中,a 是全局变量,msg是函数,并且都没有加 static 前缀,
  • 因此对于另外的源文件 main.c 是可见的。
  • 如果加了 static,就会对其它源文件隐藏。例如在 a 和 msg 的定义前加上 static,main.c 就看不到它们了。
  • 利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。static 可以用作函数和变量的前缀,对于函数来讲,static 的作用仅限于隐藏

在 C++ 程序中调用被 C 编译器编译后的函数,为什么要加 extern “C”

C++语言支持函数重载,C语言不支持函数重载,函数被C++编译器编译后在库中的名字与C语言的不同

假设某个函数原型为:

void foo(int x, inty);
  • 该函数被C编译器编译后在库中的名字为: _foo
  • 而C++编译器则会产生像: _foo_int_int 之类的名字。
  • 为了解决此类名字匹配的问题,C++提供了C链接交换指定符号 extern “C”。

头文件种的 ifndef/define/endif 是干什么用的

防止头文件被重复包含

线程和进程的联系和区别

秒杀多线程第一篇 多线程笔试面试题汇总

线程有哪几种状态

秒杀多线程第一篇 多线程笔试面试题汇总

进程间的通信方式

管道、有名管道、信号、共享内存、消息队列、信号量、套接字、文件.

线程同步和线程互斥的区别

秒杀多线程第一篇 多线程笔试面试题汇总

线程同步的方式

Linux: 互斥锁、条件变量和信号量

Linux 线程同步的三种方法

网络七层

物理层-数据链路层-网络层-运输层-会话层-表示层-应用层

TCP 和 UDP 有什么区别

  • TCP—传输控制协议,提供的是面向连接、可靠的字节流服务。
    • 当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。
    • TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。
  • UDP—用户数据报协议,是一个简单的面向数据报的运输层协议。
    • UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。
    • 由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快

编写 Socket 套接字的步骤

TCP 三次握手和四次挥手, 以及各个状态的作用

TCP三次握手四次挥手详解

HTTP 协议

  • http(超文本传输协议)是一个基于请求与响应模式的、无状态的、应用层的协议,常基于TCP的连接方式,
  • HTTP1.1版本中给出一种持续连接的机制,绝大多数的Web开发,都是构建在HTTP协议之上的Web应用。
  • TCP 和 HTTP区别:TCP和Http的区别

使用过的 shell 命令

  • pwd ,man ,cd
  • cp ,mv ,rm ,mkdir ,touch ,ls ,cat ,tail ,less ,df ,du ,find
  • top ,kill ,sudo

使用过的 vim 命令

  • wq!, dd , dw , yy , p , i
  • %s/old/new/g
    • /abc 向后搜索字符串ab
    • ?abc 向前搜索字符串abc

使用过的 gdb 命令

比较全面的gdb调试命令

常见算法

快速排序、堆排序和归并排序

C 库函数实现

静态链表和动态链表的区别

静态链表和动态链表

大并发 (epoll)

海量数据处理的知识点(Hash表,Hash统计)

什么时候要用虚析构函数

通过基类的指针来删除派生类的对象时,基类的析构函数应该是虚的。否则其删除效果将无法实现。

一般情况下,这样的删除只能够删除基类对象,而不能删除子类对象,形成了删除一半形象,从而千万内存泄漏。

原因:

在公有继承中,基类对派生类及其对象的操作,只能影响到那些从基类继承下来的成员。
如果想要用基类对非继承成员进行操作,则要把基类的这个操作(函数)定义为虚函数。
那么,析构函数自然也应该如此:如果它想析构子类中的重新定义或新的成员及对象,当然也应该声明为虚的。

注意:

如果不需要基类对派生类及对象进行操作,则不能定义虚函数(包括虚析构函数),因为这样会增加内存开销。

C++ 怎样让返回对象的函数不调用拷贝构造函数

拷贝构造函数前加 “explicit” 关键字

孤儿进程和僵尸进程

孤儿进程与僵尸进程-总结

请用简单的语言告诉我 C++ 是什么?

  • C++ 是在 C 语言的基础上开发的一种面向对象编程语言,应用广泛。
  • C++ 支持多种编程范式 --面向对象编程、泛型编程和过程化编程。
  • 其编程领域众广,常用于系统开发,引擎开发等应用领域,是最受广大程序员受用的最强大编程语言之一
  • 支持类、封装、重载等特性!

C 和 C++ 的区别?

  • C++ 在 C 的基础上增添类,C 是一个结构化语言,它的重点在于算法和数据结构。
  • C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现过程(事务)控制)
  • 而对于 C++,首要考虑的是如何构造一个对象模型,让这个模型能够契合与之对应的问题域,这样就可以通过获取对象的状态信息得到输出或实现过程(事务)控制。

    什么是面向对象(OOP)?

    面向对象是一种对现实世界理解和抽象的方法、思想,通过将需求要素转化为对象进行问题处理的一种思想。

什么是多态?

  • 多态是指相同的操作或函数、过程可作用于多种类型的对象上并获得不同的结果。
  • 不同的对象,收到同一消息可以产生不同的结果,这种现象称为多态。

设计模式懂嘛,简单举个例子?

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。

  • 比如单例模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点。

    适用于:当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时;当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。

  • 比如工厂模式,定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。

    适用于:当一个类不知道它所必须创建的对象的类的时候;当一个类希望由它的子类来指定它所创建的对象的时候;当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。

STL 库用过吗?常见的STL容器有哪些?算法用过哪几个?

STL 包括两部分内容:容器和算法。(重要的还有融合这二者的迭代器)

  • 容器,即存放数据的地方。比如 array 等。
    • 在 STL 中,容器分为两类:序列式容器和关联式容器。
    • 序列式容器,其中的元素不一定有序,但都可以被排序。如:vector、list、deque、stack、queue、heap、priority_queue、slist;
    • 关联式容器,内部结构基本上是一颗平衡二叉树。所谓关联,指每个元素都有一个键值和一个实值,元素按照一定的规则存放。如:RB-tree、set、map、multiset、multimap、hashtable、hash_set、hash_map、hash_multiset、hash_multimap。
    • 下面各选取一个作为说明。
      • vector:它是一个动态分配存储空间的容器。区别于c++中的array,array分配的空间是静态的,分配之后不能被改变,而vector会自动重分配(扩展)空间。
      • set:其内部元素会根据元素的键值自动被排序。区别于map,它的键值就是实值,而map可以同时拥有不同的键值和实值。
  • 算法,如排序,复制……以及个容器特定的算法。这点不用过多介绍,主要看下面迭代器的内容。
  • 迭代器是STL的精髓,我们这样描述它:
    迭代器提供了一种方法,使它能够按照顺序访问某个容器所含的各个元素,但无需暴露该容器的内部结构。
    它将容器和算法分开,好让这二者独立设计。

数据结构会吗?项目开发过程中主要用到那些?

数据结构中主要会用到数组,链表,树(较少),也会用到栈和队列的思想。

const 知道吗?解释其作用。

  1. const 修饰类的成员变量,表示成员常量,不能被修改。
  2. const 修饰函数承诺在本函数内部不会修改类内的数据成员,不会调用其它非 const 成员函数。
  3. 如果 const 构成函数重载,const 对象只能调用 const 函数,非 const 对象优先调用非 const 函数。
  4. const 函数只能调用 const 函数。非 const 函数可以调用 const 函数。
  5. 类体外定义的 const 成员函数,在定义和声明处都需要 const 修饰符。。

类的 static 变量在什么时候初始化?函数的 static 变量在什么时候初始化?

类的静态成员变量在类实例化之前就已经存在了,并且分配了内存。函数的 static 变量在执行此函数时进行初始化。

堆和栈的区别?堆和栈的生命周期?

  1. 堆栈空间分配区别:
    1. 栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈
    2. 堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由 OS 回收,分配方式倒是类似于链表
  2. 堆栈缓存方式区别:
    1. 栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放
    2. 堆是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些
  3. 堆栈数据结构区别:
    1. 堆(数据结构):堆可以被看成是一棵树,如:堆排序
    2. 栈(数据结构):一种先进后出的数据结构

解释下封装、继承和多态?

  1. 封装:
    1. 封装是实现面向对象程序设计的第一步,封装就是将数据或函数等集合在一个个的单元中(我们称之为类)
    2. 封装的意义在于保护或者防止代码(数据)被我们无意中破坏
  2. 继承:
    1. 继承主要实现重用代码,节省开发时间
    2. 子类可以继承父类的一些东西
  3. 多态
    1. 同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果
    2. 在运行时,可以通过指向基类的指针,来调用实现派生类中的方法

指针和引用的区别?

  1. 指针是一个变量,只不过这个变量存储的是一个地址,指向内存的一个存储单元;而引用仅是个别名;
  2. 引用使用时无需解引用(*),指针需要解引用;
  3. 引用只能在定义时被初始化一次,之后不可变;指针可变;
  4. 引用没有 const,指针有 const;
  5. 引用不能为空,指针可以为空;
  6. “sizeof 引用” 得到的是所指向的变量(对象)的大小,而 “sizeof 指针” 得到的是指针本身的大小;
  7. 指针和引用的自增 (++) 运算意义不一样;
  8. 指针可以有多级,但是引用只能是一级(int **p 合法 而 int &&a 是不合法的)
  9. 从内存分配上看:程序为指针变量分配内存区域,而引用不需要分配内存区域。

什么是内存泄漏?面对内存泄漏和指针越界,你有哪些方法?你通常采用哪些方法来避免和减少这类错误?

  • 用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元即为内存泄露。
  • 使用的时候要记得指针的长度。
  • malloc 的时候得确定在那里 free.
  • 对指针赋值的时候应该注意被赋值指针需要不需要释放.
  • 动态分配内存的指针最好不要再次赋值.

常用的排序算法有哪些?简单描述几个排序算法的优缺点?

选择、冒泡、快速、希尔、归并、堆排等。

  1. 快排:是冒泡排序的一种改进。
    1. 优点:快,数据移动少
    2. 缺点:稳定性不足
  2. 归并:分治法排序,稳定的排序算法,一般用于对总体无序,但局部有序的数列。
    1. 优点:效率高O(n),稳定
    2. 缺点:比较占用内存
    3. new 和 malloc 的区别?

  3. malloc 与 free 是 C++/C 语言的标准库函数,new/delete 是 C++ 的运算符。它们都可用于申请动态内存和释放内存。
  4. 对于非内部数据类型的对象而言,光用 maloc/free 无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。
  5. 由于 malloc/free 是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于 malloc/free。因此 C++ 语言需要一个能完成动态内存分配和初始化工作的运算符 new,以一个能完成清理与释放内存工作的运算符 delete。注意 new/delete 不是库函数。
  6. C++ 程序经常要调用 C 函数,而 C 程序只能用 malloc/free 管理动态内存。
  7. new 可以认为是 malloc 加构造函数的执行。new 出来的指针是直接带类型信息的。而 malloc 返回的都是 void 指针。

TCP 和 UDP 通信的差别?什么是 IOCP?

  1. TCP 面向连接, UDP 面向无连接的
  2. TCP 有保障的,UDP 传输无保障的
  3. TCP 是效率低的,UDP 效率高的
  4. TCP 是基于流的,UDP 基于数据报文
  5. TCP 传输重要数据,UDP 传输不重要的数据
  6. IOCP 全称I/O Completion Port,中文译为 I/O 完成端口。
  7. IOCP 是一个异步 I/O 的 API,它可以高效地将 I/O 事件通知给应用程序。

与使用 select() 或是其它异步方法不同的是,一个套接字 [socket] 与一个完成端口关联了起来,然后就可继续进行正常的 Winsock 操作了。然而,当一个事件发生的时候,此完成端口就将被操作系统加入一个队列中。然后应用程序可以对核心层进行查询以得到此完成端口。

同步 IO 和异步 IO 的区别?

  1. 同步

    所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。
    按照这个定义,其实绝大多数函数都是同步调用(例如sin isdigit等)。
    但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务。
    最常见的例子就是 SendMessage。
    该函数发送一个消息给某个窗口,在对方处理完消息之前,这个函数不返回。
    当对方处理完毕以后,该函数才把消息处理函数所返回的值返回给调用者。

  2. 异步

    异步的概念和同步相对。
    当一个异步过程调用发出后,调用者不会立刻得到结果。
    实际处理这个调用的部件是在调用发出后,通过状态、通知来通知调用者,或通过回调函数处理这个调用。

解释 C++ 中静态函数和静态变量?

  • 类静态数据成员在编译时创建并初始化:在该类的任何对象建立之前就存在,不属于任何对象,而非静态类成员变量则是属于对象所有的。类静态数据成员只有一个拷贝,为所有此类的对象所共享。
  • 类静态成员函数属于整个类,不属于某个对象,由该类所有对象共享。
  1. static 成员变量实现了同类对象间信息共享。

  2. static 成员类外存储,求类大小,并不包含在内。

  3. static 成员是命名空间属于类的全局变量,存储在 data 区的 rw 段。

  4. static 成员只能类外初始化。

  5. 可以通过类名访问(无对象生成时亦可),也可以通过对象访问。

  6. 静态成员函数的意义,不在于信息共享,数据沟通,而在于管理静态数据成员,完成对静态数据成员的封装。

  7. 静态成员函数只能访问静态数据成员。

    原因:非静态成员函数,在调用时 this指针时被当作参数传进。而静态成员函数属于类,而不属于对象,没有 this 指针。

说下你对内存的了解?

  1. 栈 - 由编译器自动分配释放
  2. 堆 - 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收
  3. 全局区(静态区),全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。- 程序结束释放
  4. 另外还有一个专门放常量的地方。- 程序结束释放
  5. 程序代码区,存放 2 进制代码。

在函数体中定义的变量通常是在栈上,用 malloc,calloc,realloc 等分配内存的函数分配得到的就是在堆上。在所有函数体外定义的是全局量,加了 static 修饰符后不管在哪里都存放在全局区(静态区),在所有函数体外定义的 static 变量表示在该文件中有效,不能 extern 到别的文件用,在函数体内定义的 static 表示只在该函数体内有效。另外,函数中的 “adgfdf” 这样的字符串存放在常量区。

C++ 面试 100 经典

面向对象的程序设计思想是什么?

把数据结构和对数据结构进行操作的方法封装形成一个个的对象。

什么是类?

把一些具有共性的对象归类后形成一个集合,也就是所谓的类。

对象都具有的两方面特征是什么?分别是什么含义?

  • 对象都具有的特征是:静态特征和动态特征。
  • 静态特征是指能描述对象的一些属性(成员变量),动态特征是指对象表现出来的行为(成员函数)

在头文件中进行类的声明,在对应的实现文件中进行类的定义有什么意义?

这样可以提高编译效率,因为分开的话只需要编译一次生成对应的 .obj 文件后,再次应用该类的地方,这个类就不会被再次编译,从而大大的提高了编译效率。

在类的内部定义成员函数的函数体,这种函数会具备那种属性?

这种函数会自动为内联函数,这种函数在函数调用的地方在编译阶段都会进行代码替换。

成员函数通过什么来区分不同对象的成员数据?为什么它能够区分?

通过this指针指向对象的首地址来区分的。

C++ 编译器自动为类产生的四个缺省函数是什么?

默认构造函数,拷贝构造函数,析构函数,赋值函数。

拷贝构造函数在哪几种情况下会被调用?

  1. 当类的一个对象去初始化该类的另一个对象时;
  2. 如果函数的形参是类的对象,调用函数进行形参和实参结合时;
  3. 如果函数的返回值是类对象,函数调用完成返回时。

构造函数与普通函数相比在形式上有什么不同?(构造函数的作用,它的声明形式来分析)

  • 构造函数是类的一种特殊成员函数,一般情况下,它是专门用来初始化对象成员变量的。
  • 构造函数的名字必须与类名相同,它不具有任何类型,不返回任何值。

什么时候必须重写拷贝构造函数?

当构造函数涉及到动态存储分配空间时,要自己写拷贝构造函数,并且要深拷贝。

构造函数的调用顺序是什么?

  1. 先调用基类构造函数
  2. 按声明顺序初始化数据成员
  3. 最后调用自己的构造函数。

哪几种情况必须用到初始化成员列表?

  1. 类的成员是常量成员初始化;
  2. 类的成员是对象成员初始化,而该对象没有无参构造函数。
  3. 类的成员为引用时。

什么是常对象?

常对象是指在任何场合都不能对其成员的值进行修改的对象。

静态函数存在的意义?

  • 静态私有成员在类外不能被访问,可通过类的静态成员函数来访问;
  • 当类的构造函数是私有的时,不像普通类那样实例化自己,只能通过静态成员函数来调用构造函数。

在类外有什么办法可以访问类的非公有成员?

友元,继承,公有成员函数。

什么叫抽象类?

不用来定义对象而只作为一种基本类型用作继承的类。

运算符重载的意义?

为了对用户自定义数据类型的数据的操作与内定义的数据类型的数据的操作形式一致。

不允许重载的5个运算符是哪些?

  1. .* (成员指针访问运算符号)
  2. :: 域运算符
  3. Sizeof 长度运算符号
  4. ?: 条件运算符号
  5. . (成员访问符)

运算符重载的三种方式?

普通函数,友元函数,类成员函数。

流运算符为什么不能通过类的成员函数重载?一般怎么解决?

因为通过类的成员函数重载必须是运算符的第一个是自己,而对流运算的重载要求第一个参数是流对象。所以一般通过友元来解决。

赋值运算符和拷贝构造函数的区别与联系?

  • 相同点:都是将一个对象 copy 到另一个中去。
  • 不同点:拷贝构造函数涉及到要新建立一个对象。

在哪种情况下要调用该类的析构函数?

对象生命周期结束时。

对象间是怎样实现数据的共享的?

通过类的静态成员变量来实现对象间的数据共享。静态成员变量占有自己独立的空间不为某个对象所私有。

友元关系有什么特性?

单向的,非传递的,不能继承的。

对对象成员进行初始化的次序是什么?

它的次序完全不受它们在初始化表中次序的影响,只有成员对象在类中声明的次序来决定的。

类和对象之间的关系是什么?

类是对象的抽象,对象是类的实例。

对类的成员的访问属性有什么?

public,protected,private

const char *p和char * const p; 的区别

如果 const 位于星号的左侧,则 const 就是用来修饰指针所指向的变量,即指针指向为常量;
如果 const 位于星号的右侧,const 就是修饰指针本身,即指针本身是常量。

是不是一个父类写了一个 virtual 函数,如果子类覆盖它的函数不加 virtual ,也能实现多态?

  • virtual 修饰符会被隐形继承的。
  • virtual 可加可不加,子类覆盖它的函数不加 virtual ,也能实现多态。

函数重载是什么意思?它与虚函数的概念有什么区别?

  • 函数重载是一个同名函数完成不同的功能,编译系统在编译阶段通过函数参数个数、参数类型不同,函数的返回值来区分该调用哪一个函数,即实现的是静态的多态性。但是记住:不能仅仅通过函数返回值不同来实现函数重载。
  • 而虚函数实现的是在基类中通过使用关键字 virtual 来申明一个函数为虚函数,含义就是该函数的功能可能在将来的派生类中定义或者在基类的基础之上进行扩展,系统只能在运行阶段才能动态决定该调用哪一个函数,所以实现的是动态的多态性。它体现的是一个纵向的概念,也即在基类和派生类间实现。

构造函数和析构函数是否可以被重载,为什么?

构造函数可以被重载,析构函数不可以被重载。因为构造函数可以有多个且可以带参数,而析构函数只能有一个,且不能带参数。

如何定义和实现一个类的成员函数为回调函数?

  • 所谓的回调函数,就是预先在系统的对函数进行注册,让系统知道这个函数的存在,以后,当某个事件发生时,再调用这个函数对事件进行响应。
  • 定义一个类的成员函数时在该函数前加 CALLBACK 即将其定义为回调函数,函数的实现和普通成员函数没有区别。

虚函数是怎么实现的?

简单说来使用了虚函数表.

抽象类不会产生实例,所以不需要有构造函数。

从一个模板类可以派生新的模板类,也可以派生非模板类。

main 函数执行以前,还会执行什么代码?

全局对象的构造函数会在main 函数之前执行。

当一个类 A 中没有生命任何成员变量与成员函数,这时 sizeof(A) 的值是多少,如果不是零,请解释一下编译器为什么没有让它为零。(Autodesk)

肯定不是零。举个反例,如果是零的话,声明一个 class A[10] 对象数组,而每一个对象占用的空间是零,这时就没办法区分 A[0],A[1]… 了。

delete 与 delete[]区别:

delete 只会调用一次析构函数,而 delete[] 会调用每一个成员的析构函数。

子类析构时要调用父类的析构函数吗?

会调用。析构函数调用的次序是先派生类的析构后基类的析构,也就是说在基类的的析构调用的时候,派生类的信息已经全部销毁了

继承的优缺点。

  • 优点
    1. 类继承是在编译时刻静态定义的,且可直接使用,
    2. 类继承可以较方便地改变父类的实现。
  • 缺点
    1. 因为继承在编译时刻就定义了,所以无法在运行时刻改变从父类继承的实现
    2. 父类通常至少定义了子类的部分行为,父类的任何改变都可能影响子类的行为
    3. 如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性。

解释堆和栈的区别。

  • 栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。
  • 堆(heap)一般由程序员分配释放, 若程序员不释放,程序结束时可能由 OS 回收 。

一个类的构造函数和析构函数什么时候被调用,是否需要手工调用?

构造函数在创建类对象的时候被自动调用,析构函数在类对象生命期结束时,由系统自动调用。

何时需要预编译:

  • 总是使用不经常改动的大型代码体。
  • 程序由多个模块组成,所有模块都使用一组标准的包含文件和相同的编译选项。在这种情况下,可以将所有包含文件预编译为一个预编译头。

多态的作用?

  1. 隐藏实现细节,使得代码能够模块化;扩展代码模块,实现代码重用;
  2. 接口重用:为了类在继承和派生的时候,保证使用家族中任一类的实例的某一属性时的正确调用

虚拟函数与普通成员函数的区别?内联函数和构造函数能否为虚拟函数?

  • 区别:虚拟函数有 virtual 关键字,有虚拟指针和虚函数表,虚拟指针就是虚拟函数的接口,而普通成员函数没有。
  • 内联函数和构造函数不能为虚拟函数。

构造函数和析构函数的调用顺序? 析构函数为什么要虚拟?

  • 构造函数的调用顺序:基类构造函数—对象成员构造函数—派生类构造函数;
  • 析构函数的调用顺序与构造函数相反。
  • 析构函数虚拟是为了防止析构不彻底,造成内存的泄漏。

C++ 中类型为 private 的成员变量可以由哪些函数访问?

只可以由本类中的成员函数和友元函数访问

请说出类中 private,protect,public 三种访问限制类型的区别

  • private 是私有类型,只有本类中的成员函数访问;
  • protect 是保护型的,本类和继承类可以访问;
  • public 是公有类型,任何类都可以访问.

类中成员变量怎么进行初始化?

可以通过构造函数的初始化列表或构造函数的函数体实现。

在什么时候需要使用“常引用”?

如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。

引用与指针有什么区别?

  1. 引用必须被初始化,指针不必。
  2. 引用初始化以后不能被改变,指针可以改变所指的对象。
  3. 不存在指向空值的引用,但是存在指向空值的指针。

描述实时系统的基本特性

在特定时间内完成特定的任务,实时性与可靠性。

全局变量和局部变量在内存中是否有区别?如果有,是什么区别?

全局变量储存在静态数据区,局部变量在堆栈中。

堆栈溢出一般是由什么原因导致的?

没有回收垃圾资源

什么函数不能声明为虚函数?

构造函数(constructor)

IP地址的编码分为哪俩部分?

答 IP 地址由两部分组成,网络号和主机号。

不能做switch()的参数类型是:

switch 的参数不能为实型。

如何引用一个已经定义过的全局变量?

可以用引用头文件的方式,也可以用 extern 关键字,如果用引用头文件方式来引用某个在头文件中声明的全局变理,假定你将那个变写错了,那么在编译期间会报错,如果你用 extern 方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错

对于一个频繁使用的短小函数,在 C 语言中应用什么实现,在 C++ 中应用什么实现?

C 用宏定义,C++ 用 inline

C++ 是不是类型安全的?

不是。两个不同类型的指针之间可以强制转换(用 reinterpret cast)

当一个类 A 中没有生命任何成员变量与成员函数,这时 sizeof(A) 的值是多少,请解释一下编译器为什么没有让它为零。

为1。举个反例,如果是零的话,声明一个 class A[10] 对象数组,而每一个对象占用的空间是零,这时就没办法区分 A[0],A[1]… 了。

简述数组与指针的区别?

数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。

  1. 修改内容上的区别

    char a[] = “hello”;
    a[0] = ‘X’;
    char *p = “world”; // 注意p 指向常量字符串
    p[0] = ‘X’; // 编译器不能发现该错误,运行时错误

  2. 用运算符 sizeof 可以计算出数组的容量(字节数)。sizeof(p),p 为指针得到的是一个指针变量的字节数,而不是 p 所指的内存容量。

C++ 函数中值的传递方式

三种方式:值传递、指针传递、引用传递

内存的分配方式

分配方式有三种

  1. 静态存储区,是在程序编译时就已经分配好的,在整个运行期间都存在,如全局变量、常量。
  2. 栈上分配,函数内的局部变量就是从这分配的,但分配的内存容易有限。
  3. 堆上分配,也称动态分配,如我们用 new,malloc 分配内存,用 delete,free 来释放的内存。

extern“C” 有什么作用?

  • Extern “C” 是由 C++ 提供的一个连接交换指定符号,用于告诉 C++ 这段代码是 C 函数。这是因为 C++ 编译后库中函数名会变得很长,与C生成的不一致,造成 C++ 不能直接调用C函数,加上 extren “c” 后,C++ 就能直接调用 C 函数了。
  • Extern “C” 主要使用正规DLL函数的引用和导出 和 在 C++ 包含C函数或C头文件时使用。使用时在前面加上 extern “c” 关键字即可。可以用一句话概括 extern “C” 这个声明的真实目的:实现 C++ 与 C 及其它语言的混合编程。

用什么函数开启新进程、线程。

  • 线程:CreateThread/AfxBeginThread 等
  • 进程:CreateProcess 等

SendMessage 和 PostMessage 有什么区别

  • SendMessage 是阻塞的,等消息被处理后,代码才能走到 SendMessage 的下一行。
  • PostMessage 是非阻塞的,不管消息是否已被处理,代码马上走到 PostMessage 的下一行。

CMemoryState 主要功能是什么

查看内存使用情况,解决内存泄露问题。

#include <filename.h> 和 #include “filename.h” 有什么区别?

  • 对于 #include <filename.h> ,编译器从标准库路径开始搜索 filename.h
  • 对于 #include “filename.h” ,编译器从用户的工作路径开始搜索 filename.h

处理器标识 #error 的目的是什么?

编译时输出一条错误信息,并中止继续编译。

#if!defined(AFX_…_HADE_H) #define(AFX_…_HADE_H) … #endif 作用?

防止该头文件被重复引用。

在定义一个宏的时候要注意什么?

定义部分的每个形参和整个表达式都必须用括号括起来,以避免不可预料的错误发生

数组在做函数实参的时候会转变为什么类型?

数组在做实参时会变成指针类型。

系统会自动打开和关闭的 3 个标准的文件是?

  1. 标准输入—-键盘— stdin
  2. 标准输出—-显示器— stdout
  3. 标准出错输出—-显示器— stderr

在 Win32 下 char, int, float, double 各占多少位?

  1. Char 占用8位
  2. Int 占用32位
  3. Float 占用32位
  4. Double 占用64位

strcpy() 和 memcpy() 的区别?

strcpy() 和 memcpy() 都可以用来拷贝字符串,strcpy() 拷贝以’\0’结束,但 memcpy() 必须指定拷贝的长度。

说明define和const在语法和含义上有什么不同?

  1. #define 是 C 语法中定义符号变量的方法,符号常量只是用来表达一个值,在编译阶段符号就被值替换了,它没有类型;
  2. Const 是 C++ 语法中定义常变量的方法,常变量具有变量特性,它具有类型,内存中存在以它命名的存储单元,可以用 sizeof 测出长度。

说出字符常量和字符串常量的区别,并使用运算符sizeof计算有什么不用?

字符常量是指单个字符,字符串常量以 ‘\0’ 结束,使用运算符 sizeof 计算多占一字节的存储空间。

简述全局变量的优缺点?

全局变量也称为外部变量,它是在函数外部定义的变量,它属于一个源程序文件,它保存上一次被修改后的值,便于数据共享,但不方便管理,易引起意想不到的错误。

总结static的应用和作用?

  1. 函数体内 static 变量的作用范围为该函数体,不同于 auto 变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
  2. 在模块内的 static 全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
  3. 在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;
  4. 在类中的 static 成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
  5. 在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的 static 成员变量。

总结 const 的应用和作用?

  1. 欲阻止一个变量被改变,可以使用 const 关键字。在定义该 const 变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;
  2. 对指针来说,可以指定指针本身为 const,也可以指定指针所指的数据为 const,或二者同时指定为 const;
  3. 在一个函数声明中,const 可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
  4. 对于类的成员函数,若指定其为 const 类型,则表明其是一个常函数,不能修改类的成员变量;
  5. 对于类的成员函数,有时候必须指定其返回值为 const 类型,以使得其返回值不为“左值”

什么是指针?谈谈你对指针的理解?

  • 指针是一个变量,该变量专门存放内存地址;
  • 指针变量的类型取决于其指向的数据类型,在所指数据类型前加*
  • 指针变量的特点是它可以访问所指向的内存。

什么是常指针,什么是指向常变量的指针?

  • 常指针的含义是该指针所指向的地址不能变,但该地址所指向的内容可以变化,使用常指针可以保证我们的指针不能指向其它的变量,
  • 指向常变量的指针是指该指针的变量本身的地址可以变化,可以指向其它的变量,但是它所指的内容不可以被修改。指向长变量的指针定义,

函数指针和指针函数的区别?

  • 函数指针是指向一个函数入口的指针;指针函数是函数的返回值是一个指针类型。

简述 Debug 版本和 Release 版本的区别?

  • Debug 版本是调试版本,Release 版本是发布给用户的最终非调试的版本,

指针的几种典型应用情况?

  • int *p[n]; -指针数组,每个元素均为指向整型数据的指针。
  • int (*)p[n]; -p为指向一维数组的指针,这个一维数组有n个整型数据。
  • int *p(); -函数带回指针,指针指向返回的值。
  • int (*)p(); -p为指向函数的指针。

static 函数与普通函数有什么区别?

static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝

struct (结构)和 union (联合)的区别?

  1. 结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)。
  2. 对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的。

class 和 struct 的区别?

struct 的成员默认是公有的,而类的成员默认是私有的。

简述枚举类型?

枚举方便一次定义一组常量,使用起来很方便;

assert() 的作用?

ASSERT() 是一个调试程序时经常使用的宏,在程序运行时它计算括号内的表达式,如果表达式为 FALSE (0), 程序将报告错误,并终止执行。如果表达式不为 0,则继续执行后面的语句。这个宏通常原来判断程序中是否出现了明显非法的数据,如果出现了终止程序以免导致严重后果,同时也便于查找错误。

局部变量和全局变量是否可以同名?

能。局部会屏蔽全局。要用全局变量,需要使用 “::”(域运算符)。

程序的局部变量存在于(堆栈)中,全局变量存在于(静态区 )中,动态申请数据存在于( 堆)中。

在什么时候使用常引用?

如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。

类的声明和实现的分开的好处?

  1. 起保护作用;
  2. 提高编译的效率。

windows 消息系统由哪几部分构成?

3 部分组成:

  1. 消息队列:操作系统负责为进程维护一个消息队列,程序运行时不断从该消息队列中获取消息、处理消息;
  2. 消息循环:应用程序通过消息循环不断获取消息、处理消息。
  3. 消息处理:消息循环负责将消息派发到相关的窗口上使用关联的窗口过程函数进行处理。

什么是消息映射?

消息映射就是让程序员指定MFC类(有消息处理能力的类)处理某个消息。然后由程序员完成对该处理函数的编写,以实现消息处理功能。

什么是 UDP 和 TCP 的区别是什么?

  • TCP 的全称为传输控制协议。这种协议可以提供面向连接的、可靠的、点到点的通信。
  • UDP 全称为用户报文协议,它可以提供非连接的不可靠的点到多点的通信。
  • 用 TCP 还是 UDP,那要看你的程序注重哪一个方面?可靠还是快速?