C++常见面试笔试题

https://www.cnblogs.com/aduck/articles/2272248.html

https://www.cnblogs.com/guozw/p/8732473.html

https://blog.csdn.net/owen7500/article/details/52693011

一、C 和 C++ 区别

1)C++ 在 c 的基础上添加类

2)C主要是面向过程,C + + 主要面向对象

3)C主要考虑通过一个过程将输入量经过各种运算后得到一个输出, C++ 主要考虑是如何构造一个对象模型,让这个模型契合与之对应的问题域, 这样就可以通过获取对象的状态信息得到输出。

扩展:如果被问到什么是c++就说是在 c 语言的基础上开发的一种面向对象编程的语言具有封装、继承和多态三个基本特性。如果详细一点就把3)的后半句加上去

 

二、面向过程与面向对象的区别

面向过程程序设计是围绕功能进行的,用一个函数实现一个功能。所有的数据都是公用的,一个函数可以使用任何一组数据,而一组数据又能被多个函数使用。程序设计者必须考虑每个细节,是什么时候对什么数据进行操作,这样当程序规模较大的时候就会觉得难以应付。

而面向对象是把数据和与其相关的操作放在一起,封装成了一个对象,与外界相对分隔,比如说把三角形三边和计算三角形面积和输出结果的操作的代码放在一起封装成一个对象,与其他无关操作分隔开来,这样就不必考虑过多无关细节,程序设计者只需要考虑把哪些数据和操作封装在一起以及怎样向有关对象发送消息完成所需的任务就行了,大大降低了工作难度并提高了效率。

 

三、封装、继承和多态

封装:将数据以及基于数据的一系列操作封装在一个对象中,各对象之间相互独立;隐藏对象的属性和实现细节,对外提供访问接口。像一个黑盒,外部人员并不了解内部细节,只通过接口访问。

继承:使子类具有父类的属性和方法,实现代码重用,节省开发时间。(子类也可定义自己的属性和方法,并根据自己需求重写父类方法,使自己获得与父类不同的功能)。

多态:由继承而产生的相关的类,其对象对同一消息会做出不同的响应。即同一操作作用于不同对象时产生不同的执行结果。

 

四、类和对象

类是对象的抽象,而对象是类的具体实例。

 

五、static、const、#define

static分为局部静态变量全局静态变量

局部静态变量:在静态存储区内分配内存单元,直到整个程序运行结束才会释放,该变量的内存只被分配一次,因此其值在下次调用函数时仍维持上次的值,未初始化的局部初始化为0,作用域是定义它的函数即静态局部变量只在定义它的函数内有效。

全局静态变量:静态存储区分配内存单元,未经初始化的全局静态变量会被程序自动初始化为0, 内存只被分配一次。在模块内(函数体外),该变量可以被模块内所有函数访问,但不能被模块外其他函数访问即静态全局变量只在定义它的文件内有效,而全局变量在整个工程文件内都有效。

修饰类时(C++和C的区别,C中无类,无法修饰类):用static修饰类的数据成员,会被类的所有对象共享都可以引用它,包括派生类的对象,在内存中只占一份空间而不是每个对象都为他保留一分空间,并不属于某个对象而是属于对象所属的类。

用static修饰类的成员函数称作静态成员函数,作用不是为了对象之间的沟通而是为了能处理静态数据成员,静态成员函数也不属于某一对象,没有this指针。

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

const:用来定义常量、修饰函数参数、修饰函数返回值,可以避免被修改,提高程序的健壮性。必须在定义的时候同时初始化,不能进行赋值。  

常引用:指向const对象的引用,为了避免实参在调用函数中被意外的改变。

#define:宏定义,在预处理阶段进行代码文本的替换。只是纯粹的替换,并无计算功能。(无法进行类型检查,因为是在编译前进行字符的替换,只有在编译时才能知道类型是否匹配)

const与#define差异

1)const定义的数据有数据类型,而宏常量没有数据类型。编译器可以对const常量进行类型检查。而对宏定义只进行字符替换,没有类型安全检查,所以字符替换时可能出错。

2)define只是用来进行单纯的文本替换,定义的常量生命周期止于编译期,不分配空间;而const定义的常量在堆栈中分配了空间,可以被调用和传递。

 

六、new/delete与malloc/free区别

1)new能够在自动计算需要分配的内存空间,而malloc需要手工计算字节数。

eg:int*p=new int[2],int*q = malloc(2*sizeof(int))

2)new可调用构造函数,malloc不能;delete可调用析构函数而free不能。

3)malloc/free是C/C++语言的标准库函数,需要库文件stdlib.h支持,delete/free是C++运算符,不能在C语言中使用

4)new/delete返回的是具体类型的指针,malloc/free返回void类型指针。

 

七、volatile关键字在程序设计中有什么作用

(编译期优化的时候可能会出现问题,如当遇到多线程编程时,变量的值可能因为别的线程而改变了,而该寄存器的值不会相应的改变,从而造成应用程序读取的值和实际的变量值不一样。)

volatile是一个类型修饰符,被volatile修饰的变量表示该变量可能会被意想不到的改变,变量被这个关键字声明后,系统对这种变量的处理不会做优化,从而可以提供对特殊地址的稳定访问。(准确的说,定义变量后系统每次用到它的时候都是直接从对应的内存当中提取,而不是使用保存在寄存器里的备份)

volatile一般用于修饰多线程间被多个任务共享的变量并行设备硬件寄存器等。

 

八、内存类别

五大存储区:堆区、栈区、全局区、文字常量区、程序代码区。

堆:用于存放进程运行中被动态分配的内存段,大小不固定,可动态扩张或缩减(编译器自动分配释放,其操作方式类似于数据结构中的栈 )。

栈:栈用户存放程序临时创建的局部变量。( 一般由程序员分配释放 , 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。)

全局区(静态区):存储全局变量和静态变量, 初始化的全局变量和static静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(BSS)。 - 程序结束后由系统释放 

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

 

程序代码区 :存放二进制代码

 

九、内存分配方式

1)从静态存储区域分配。该存储区域在程序编译的时候就已经分配好了,这块内存在程序的整个运行期间都存在,速度快,不易出错。例如全局变量,static 变量。

2)在栈上创建。在执行函数时,函数的局部变量存储在该区域,函数执行结束时会释放该存储空间。栈内存分配运算内置于处理器的指令集,效率高但分配的容量有限。

3)从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活。

 

十、内存泄漏、缓冲区溢出及原因

内存泄漏:程序中动态分配了内存,但是在程序结束时没有释放这部分内存,即未能释放已经不再使用的内存的情况。

malloc或new等动态分配内存后未进行释放free或delete,导致内存泄漏。

缓冲区溢出(堆栈溢出):指当向缓冲区填充数据位数超过了缓冲区自身的容量限制时,发生的溢出的数据覆盖在合法数据上的情况。

溢出原因:数组越界, 没有回收内存(动态分配后未释放), 深层次递归调用(每次调用都开辟空间,例如每次都new[100]直到耗尽存储空间)

 

十一、sizeof与strlen

1)sizeof是关键字,strlen是函数

2)sizeof的参数可以是数据的类型也可以是函数和变量;strlen只能用char*作为参数,且必须以”\0”结尾。

3)sizeof是在编译的时候计算的,计算的是数据类型占内存的大小;strlen是在运行时计算的,计算的是字符串实际长度。

sizeof(“\0”)=2;strlen(“\0”)=0

 

十二、指针和数组区别,指针和引用区别

指针和数组区别

指针可以随时指向任意类型的数据块,而数组在静态存储区被创建。

1)修改内容不同。char a[] = “hello”;通过下标方式修改元素值,a[0]=’x’;而char*p=”hello”;p[0]=’x’是错的

2)所占字节数不同。

Char a[] = “hello world”;char*p = a; sizeof(a) = 12,sizeof(p)=4;后者得到的是一个指针变量的字节数,因为没法知道指针所指的内存容量,除非在申请内存时标出来。

指针和引用区别

1)引用必须被初始化,指针不必。

2)引用初始化以后不能被改变,指针可以改变所指的对象。

3)不存在指向空值的引用,但是存在指向空值的指针

 

十三、野指针和空指针

野指针:指向不可用内存的指针。

1)任何指针变量在被创建时不会自动成为NULL指针(空指针),默认值是随机的,所以指针被创建时应被初始化或置为NULL,否者会成为野指针

2)free或delete释放内存,是把指针指向的内存释放了,但并没有把指针本身释放,指针如果未设置为NULL就会不指向合法的内存,成为野指针

3)指针操作超越了变量的作用范围。例如int a[4],指针指向了a[5]

空指针:是一个特殊的指针,唯一一个对任何指针类型都合法的指针。指针设置为0或NULL都代表空指针。

 

十四、头文件中的ifndef/define/endif 的作用

防止该头文件被重复定义。

 

十五、struct和class的区别,struct与union的区别

struct和class区别

1)class继承默认是private继承,而struct默认public继承

2)在默认情况下,struct的成员变量是公共(public)的;在默认情况下,class的成员变量是私有(private)的。

3)class还用于定义模板参数,就像typename,而关键字struct不能

struct与union的区别

1)一个union类型的变量,所有成员变量共享一块内存,该内存的大小有这些成员变量中长度最大的一个来决定,而struct中成员变量内存都是独立的。因此,对于union的一个成员赋值, 那么其它成员会重写,而struct则不会。

2)union分配的内存是连续的,而struct不能保证分配的内存是连续的

 

十六、枚举和define有什么不同

1)宏定义常量是在预编译阶段进行简单替换。枚举常量则是在编译的时候确定其值。

2)一般在编译器里,可以调试枚举常量,但是不能调试宏常量

3)枚举可以一次定义大量相关的常量,而宏一次只能定义一个

 

十七、typdef和define区别

1)原理不同。#define是预处理指令,在预处理时进行简单而机械的字符串替换,不做类型检查,只有在编译时才能发现错误;而typedef是关键字,在编译时处理,具有类型检查功能。

2)功能不同。typedef用来定义类型的别名,而#define不只是可以为类型区别名,还可以定义常量、变量等等

3)作用域不同。#define没有作用域限制,只要是之前定义过的,在以后的程序中都可以使用;而typedef有自己的作用域。

4)对指针的操作不同。

eg:#define INTPTR1 int*

typedef int* INTPTR2;

INTPTR1 p1,p2; #定义了一个int型指针p1,和一个int型变量 p2

INTPTR2 p1,p2; # p1,p2都是指针变量

 

十八、内联函数与宏有什么区别

1)宏定义是在预处理阶段进行代码替换,而内联函数是在编译阶段插入代码

2)宏定义没有类型检查,而内联函数有类型检查

 

十九、strcpy、memcpy和sprintf

strcpy只能拷贝字符串,遇到’\0′结束拷贝。

memcpy是内存拷贝函数,可以拷贝任何数据类型的对象,与strcpy相比它必须指定拷贝的长度遇到‘\0’不一定结束,而是一定会复制完指定长度的字符。

sprintf的功能是把格式化的数据写入某个字符串,即将其他数据类型转换成字符串类型。

 

二十、常量指针和指针常量的区别,函数指针和指针函数区别

常量指针:指向常量的指针,因为常量指针指向的对象是常量,因此这个对象的值不能改变,但可以改变指针的指向,即可以指向不同的常量,但不能改变所指的对象的值。

eg int const *p;或const int *p;

指针常量:定义的指针只能在定义的时候初始化,之后不能改变指向。Int* const p;

函数指针:指向函数的指针变量,即本质是一个指针变量,表示的是一个指针,指向的是一个函数。eg: int (*p)(int x,int y) (*p)指向一个函数

指针函数:指的是带指针的函数,本质是一个函数,返回类型是指针类型。

eg:int *f(int x,int y)

 

二十一、重载、覆盖(又称重写)和隐藏的区别

重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。
覆盖(重写):是指子类重新定义父类虚函数的方法,子类方法的名称参数和返回值类型必须和所覆盖的方法相同。

隐藏:派生类的函数屏蔽了与其同名的基类函数。

1)如果派生类的函数与基类的函数同名但参数不同,则不论有无virtual关键字,基类的函数将被隐藏(注意:重载是在一个类中发生的,注意二者区别)。

2)如果子类函数与基类同名且参数相同但基类函数没有virtual关键字,则基类函数被隐藏(注意:如果有virtual关键字,则是覆盖,注意其与覆盖的区别)

 

二十二、C++函数传递参数的方式有哪些

值传递、指针传递、引用传递、全局变量传递

 

二十三、变量定义与变量声明区别,全局变量和静态变量区别

定义与声明区别:

声明是告诉编译器变量的类型和名字(extern关键字可以进行声明),不为变量分配空间,而定义变量需要分配空间,同一个变量只能被定义一次,但可以被声明多次。

全局变量和静态变量区别:

全局变量在整个工程文件内都有效;静态全局变量只在定义它的文件内有效,即被static关键字修饰过的变量具有文件作用域,即使两个不同的源文件都定义了相同的静态全局变量,他们也是不同的变量。

 

二十四、C++文件编译与执行的四个阶段

第一阶段:预处理阶段。根据文件中的预处理指令来修改源文件的内容。如#include指令,作用是把头文件的内容添加到.cpp文件中。

第二阶段:编译阶段,将其翻译成等价的中间代码或汇编代码。

第三阶段:汇编阶段,把汇编语言翻译成目标机器指令。

第四阶段:是链接,例如,某个源文件中的函数可能引用了另一个源文件中定义的某个函数;在程序中可能调用了某个库文件中的函数。

 

二十五、delete和delete []的区别

都是用来调用析构函数的:

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

(2)delete与new配套,delete []与new []配套,用new分配的内存用delete删除用new[]分配的内存用delete[]删除

 

二十六、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及其它语言的混合编程

 

二十七、初始化列表和构造函数初始化区别

初始化列表是显示地初始化类的成员,这种方法方便简练,尤其是当需要初始化的数据成员较多时更显其优越性。

eg:Object::Object(int w,int h):weight(w),height(h){}

没有使用初始化列表的构造函数是对类成员的赋值。

eg: Object::Object(int w,int h)

{

       height = w;

    height = h;

}

注意:const成员或引用类型的成员,因为它们只能初始化不能对他们另外赋值,所以只能用初始化列表的方法。

 

二十八、虚函数、纯虚函数和抽象类

虚函数:那些被virtual关键字修饰的成员函数,就是虚函数。

虚函数的作用:实现多态性,即在程序运行阶段动态地选择合适的成员函数(根据不同的类对象调用其相应的函数)

 

纯虚函数:是在声明虚函数时被“初始化”为0的函数。(在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。为了解决这个问题,方便使用类的多态性,引入了纯虚函数的概念)

一般形式:virtual 函数类型 函数名(参数列表) = 0;

注意:1)纯虚函数没有函数体2)最后面的‘=0’并不表示函数返回值为0,它只是形式上的作用,告诉编译系统“这是纯虚函数”3)这是个声明语句,最后要加分号。

纯虚函数作用:在基类中为其派生类保留一个函数的名字,以便派生类根据需要对他进行定义。

如果在一个类中声明了纯虚函数,而在其派生类中,没有对该函数定义,则该函数在派生类中仍然为纯虚函数。

 

抽象类:不用来定义对象而只作为一种基本类型用作继承的类。凡是包含纯虚函数的类都是抽象类。抽象类不能被实例化,即不能用来定义对象。

如果在派生类中没有对抽象类中的纯虚函数进行定义,则此派生类仍是抽象类,不能用来定义对象。

 

二十九、构造函数和析构函数能否为虚函数

构造函数不能为虚函数,因为虚函数的执行依赖于虚函数表,调用构造函数后,虚表才建立,因而构造函数不可能成为虚函数。

 

析构函数可以为虚函数,在类的继承中,如果有基类指针指向派生类,那么用基类指针delete时,如果不定义成虚函数,派生类中派生的那部分无法析构,即系统只会执行基类的析构函数而不执行派生类的析构函数。

  三十、指针和引用区别,什么是引用,常引用作用,什么是指针

(1)引用必须被初始化,指针不必。

(2)引用初始化以后不能被改变,指针可以改变所指的对象。

(3)不存在指向空值的引用,但是存在指向空值的指针。

引用:就是某个目标变量的“别名”,对应用的操作与变量直接操作效果完全相同。

常引用:用const修饰引用,主要是为了避免实参在调用函数中被意外的改变。

指针

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

注意:因为不存在空引用,并且引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用,而指针可能为空或产生野指针,所以传引用比传指针安全。

 

三十一、深拷贝和浅拷贝

深拷贝:指源对象与拷贝对象互相独立,有各自存储空间,其中任何一个对象的改动都不会对另外一个对象造成影响。

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

浅拷贝引发问题:

1)如果对象的数据成员是指针,浅拷贝只是拷贝了指针,使得两个指针指向同一个地址,在调用析构函数时会delete一块内存两次,造成程序崩溃。

2)因为指向同一块内存,所以任何一方的改动都会影响另一方。

 

三十二、复制构造函数与赋值运算符的区别是什么

复制(copy,又叫拷贝)构造函数,分为浅拷贝和深拷贝,浅拷贝与原对象公用一个实体,深拷贝会有独立的内存空间。如果没有显示的定义一个复制构造函数,则默认自动生成一个,默认是浅拷贝。

赋值操作符用已存在的对象来创建另一个对象,给对象赋予一个新的值,由于赋予的是新值,所以该对象原来就有值,赋值函数只能被已经存在了的对象调用而不能凭空产生。如果没有显示声明,系统也会默认生成一个赋值函数,但指向同一内存,与浅拷贝类似。

 

二者区别:

1) 复制构造函数生成新的类对象,而赋值运算符不能。

2) 2)由于复制构造函数是直接构造一个新的类对象,所以在初始化这个对象之前不用检查源对象是否和新建对象相同。而赋值运算符则需要这个操作,另外赋值运算中如果原来的对象中有内存分配,要先把内存释放掉。

3) 注意:当类中有指针类型的成员变量时一定要重写赋值和复制构造函数,不能使用默认的,因为默认的指向同一内存块,delete指针变量时会调用两次一块内存两次,导致程序崩溃。

三十三、printf()是否有返回值

返回一个int值,表示被打印的字符数

a = 1234

print(“%d\n”,printf(“%d\n”,printf(“%d\n”,a)))

结果:

1234

5

2;

 

三十四、main()主函数执行完毕后,是否可能会再执行一段代码

可以。可以用_onexit注册一个函数,它会在main之后执行所注册的函数。

使用_onexit()函数要添加头文件stdlib.h,否则会报编译错误

 

三十五、#include<filename.h>和#define“filename.h”区别

前者<>编译器先从标准库路径开始搜索filename.h,使得系统文件调用较快

后者“”编译器先从用户的工作路径开始搜索filename.h,然后去寻找系统路径,使得自定义文件较快。

 

三十六、处理器标识#error的目的是什么?
编译时输出一条错误信息,并中止继续编译。

 

三十七、对于一个频繁使用的短小函数,在C语言中应用什么实现,在C++中应用什么实现?
c用宏定义,c++用inline(内联函数)

 

三十八、常见的STL容器有哪些?算法用过哪几个?

STL是Standard Template Library的简称,中文名标准模板库

STL包括两部分内容:容器和算法

容器,即存放数据的地方。比如array等。

在STL中,容器分为两类:序列式容器关联式容器

序列式容器,其中的元素不一定有序,但都可以被排序。如:vector(向量)、list(双向链表)、deque(双端队列)、stack(栈)、queue(队列)、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的精髓,我们这样描述它:迭代器提供了一种方法,使它能够按照顺序访问某个容器所含的各个元素,但无需暴露该容器的内部结构。它将容器和算法分开,好让这二者独立设计。


三十九 (1)不调用库函数,实现strcat,strcmp,strstr,strcpy,strncpy

https://blog.csdn.net/qq_34793133/article/details/80908264

char * strcpy( char *strDest, const char *strSrc )
{
 assert( (strDest != NULL) && (strSrc != NULL) );
 char *address = strDest;
 while( (*strDest++ = * strSrc++) != ‘\0’ );
 return address;
}
 char * my_strncpy(char *strDest, const char *strSrc, int num)
{
assert((strDest != NULL) && (strSrc != NULL));
//if (strDest == NULL || strSrc == NULL) return NULL;
//保存目标字符串的首地址
char *strDestcopy = strDest;
while ((num--)&&(*strDest++ = *strSrc++) != '\0');
//如果num大于strSrc的字符个数,将自动补'\0'
if (num > 0)
{
while(--num)
{
*strDest++ = '\0';
}
}
return strDestcopy;
char *strstr( const char *s1, const char *s2 )
{
    int len2;
    if ( !(len2 = strlen(s2)) )
        return (char *)s1;
    for ( ; *s1; ++s1 )
    {
        if ( *s1 == *s2 && strncmp( s1, s2, len2 )==0 )
            return (char *)s1;
    }
    return NULL;
}
int  strcmp ( const  char * str1,  const  char * str2)
{
     int  ret = 0;
     while (!(ret=*(unsigned  char *)str1-*(unsigned  char *)str2) && *str1)
     {
         str1++;
         str2++
     }
     if  (ret < 0)
     {
         return  -1;
     }
     else  if  (ret > 0)
     {
         return  1;
     }
     return  0;
}


(2)已知类String 的原型为:
class String
{
public:
String(const char *str = NULL); // 普通构造函数
String(const String &other); // 拷贝构造函数
~ String(void); // 析构函数
String & operate =(const String &other); // 赋值函数
private:
char *m_data; // 用于保存字符串
};

请编写String 的上述4 个函数。

https://blog.csdn.net/qq_34793133/article/details/80940745

四十、 头文件中的ifndef/define/endif 干什么用?

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

四十一、#include<filename.h> 和 #include"filename.h"有什么区别

对于#include<filename.h>,编译器先从标准库路径开始搜索filename.h,使得系统文件调用比较快;

对于#include"filename.h",编译器先从用户的工作路径开始搜索filename.h,后去寻找系统路径,使得自定义文件较快。所以在写代码的过程中要根据实际情况选择是<>还是""


引申:头文件的作用有哪些?头文件的作用主要表现为以下两个方面:

(1)**通过头文件来调用库功能**。出于对源代码保密的考虑,源代码不便(或不准)向用户公布,只要向用户提供头文件和二进制的库即可。用户只需要按照头文件中的接口声明来调用库功能,而不必关心接口是怎么实现的,编译器会从库中提取相应的代码;

(2)**头文件能加强类型安全检查**。如果某个接口被实现或被调用时,其方式与头文件中的声明不一致,编译器就会指出错误,能大大减轻程序员调试、改错的负担。


四十二、在C++ 程序中调用被C 编译器编译后的函数,为什么要加extern “C”?
答:C++语言支持函数重载,C 语言不支持函数重载。函数被C++编译后在库中的名字与C 语言的不同。假设某个函数的原型为: void foo(int x, int y);
该函数被C 编译器编译后在库中的名字为_foo , 而C++ 编译器则会产生像_foo_int_int 之类的名字。

C++提供了C 连接交换指定符号extern“C”来解决名字匹配问题。


四十三、一个类有基类、内部有一个其他类的成员对象,构造函数的执行顺序是怎样的。(Autodesk)

答:先执行基类的(如果基类当中有虚基类,要先执行虚基类的,其他基类则按照声明派生类时的顺序依次执行),再执行成员对象的,最后执行自己的。

四十四、malloc/free与new/delete的区别

https://blog.csdn.net/qq_34793133/article/details/80942425

四十五、C++中指针和引用的区别

https://blog.csdn.net/qq_34793133/article/details/80945335

四十六#define DOUBLE(x) x+x (Autodesk)
i = 5*DOUBLE(10); i 是多少?正确的声明是什么?

答案:i 为60。正确的声明是#define DOUBLE(x) (x+x)

四十七  C++是不是类型安全的? (Autodesk)

答案:不是。两个不同类型的指针之间可以强制转换。C#是类型安全的。

四十八、main 函数执行以前,还会执行什么代码? (Autodesk)

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

四十九  C/C++常见笔试面试题之常考关键字

https://blog.csdn.net/owen7500/article/details/52693011

1.描述volatile的作用

答:volatile主要是用来指示编译器不要对自己修饰的对象执行优化,警告编译器被修饰的变量可能被出乎意料的修改。volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知因素更改,比如:操作系统、硬件或者其他线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再执行优化,从而可以提供对特殊地质的稳定访问。对于一般情况下,变量可能被优化存储在寄存器中,最近的下次需要使用时直接读取寄存器即可,但是这个过程中,该变量所在的内存地址存储的变量可能已经被外部改变,如操作系统、硬件等。而volatile修饰的变量就不会被优化存储在寄存器中,而是每次直接读取内存中的最新值。

2.const关键字有哪些用途,它和static有哪些区别?

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

对于static关键字,则是用于声明一个具有全局生命周期的静态变量。且该值只可以被初始化一次,但可以被修改。static关键字至少有下列n个作用: 
(1)设置变量的存储域,函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值; 
(2)限制变量的作用域,在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问; 
(3)限制函数的作用域,在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内; 
(4)在类中的static成员变量意味着它为该类的所有实例所共享,也就是说当某个类的实例修改了该静态成员变量,其修改值为该类的其它所有实例所见; 
(5)在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。

3.const和#define有什么区别,大多数情况下那种更优?

答:const是用于定义一个有类型的只读常变量。而#define是宏定义,是在编译期间对定义的变量进行直接文本替换,不做类型检查。变量定义时,大多数情况下const相对于#define来说有着更优的表现效果。基于以下几点原因: 
1.const对数据进行类型检查,#define不进行类型检查; 
2.某些编译器支持对const对象进行调试,所有编译器都不支持对#define进行调试; 
3.const变量存放在内存的静态数据区域,所以在程序运行期间const变量只有一个拷贝,而#define修饰的变量则会在每处都进行展开,拥有多个拷贝;

其余区别: 
1.#define可以用来定义宏函数,而const不行。 
2.const除了可以修饰常变量外,还可以用于修饰指针、函数形参等等,修饰功能更强大。

4.C++中引用编译过的C代码为什么需要“extern C”

答:主要目的是为了解决函数名字匹配保证兼容性。由于C++支持函数重载,而C语言不支持函数重载,C语言中函数编译后得到的汇编代码中函数名与原函数名基本相同,如func()编译后成为_func,重载函数经过编译后得到函数名并不是原来的函数名,而是函数与参数类型的结合,因此两者函数调用的方式不一样。为了C++中能够调用C中的代码,使用extern C来表明对于包括函数采用C语言的方式调用,而不采用C++的方式调用。 
extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明。例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可。这样,模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但是并不会报错;它会在连接阶段中从模块A编译生成的目标代码中找到此函数extern “C”是连接申明(linkage declaration),被extern “C”修饰的变量和函数是按照C语言方式编译和连接的。

5.sizeof是函数还是关键字?

答:sizeof 是运算符关键字,sizeof可以采用 sizeof int;的使用方式。

6.new/delete是函数吗?它们和malloc/free有何区别?

答:new/delete是C++中的运算符,malloc/free是C标准库中提供的函数。他们都是用于动态申请内存和释放内存的组合。它们的主要区别有: 
1.malloc/free是C/C++语言的标准库函数,new/delete是C++的运算符 
2.new能够自动分配空间大小 
3.对于用户自定义的对象而言,用maloc/free无法满足动态管理对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此C++需要一个能对对象完成动态内存分配和初始化工作的运算符new,以及一个能对对象完成清理与释放内存工作的运算符delete—简而言之 new/delete能进行对对象进行构造和析构函数的调用进而对内存进行更加详细的工作,而malloc/free不能。

7.new/delete和malloc/free会初始化内存和清空内存内容吗?

答:new操作会调用对象的构造函数初始化内存区域,但malloc只负责申请内存不会初始化。(但在vs平台上测试时,发现malloc申请的内存会被初始化为一个特殊的数:cdcdcdcd(4字节整数))。delete操作会调用对象的析构函数,并释放空间,而free只负责释放空间,两者在释放空间时,默认情况下不会清空原有内容(这就是新内存中值为随机数的原因)。(vs平台下测试发现,delete和free两者操作后都会清除原来的空间,并赋值为另一个特殊的数:feeefeee(四字节整数)) 
测试代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;

int main()
{
    int *p = (int*)malloc(10 * sizeof(int));
    int *q = new int[10];

    for (int i = 0; i < 10;++i)
    {
        p[i] = i;
        q[i] = i;
    }

    delete q;
    free(p);
    system("pause");
    return 0;
}

测试截图: 
申请内存之后的结果: 
这里写图片描述
赋值之后内存区域结果: 
这里写图片描述
释放之后内存区域结果: 
这里写图片描述

7.struct和class有哪些区别?struct可以实现class的功能吗?

答:struct关键字继承自C语言,class是C++独有。在C++中两者最主要区别在于申明类时的默认权限不同,struct默认的权限是public,class的默认权限是private。在C++中class还可以用于申明模板类型,而struct不能。

五十、描述内存分配方式以及它们的区别。 (Autodesk , Microsoft)

内存的三种分配方式:
1. 从静态存储区分配:此时的内存在程序编译的时候已经分配好,并且在程序的整个运行期间都存在。全局变量,static变量等在此存储。
2. 在栈区分配:相关代码执行时创建,执行结束时被自动释放。局部变量在此存储。栈内存分配运算内置于处理器的指令集中,效率高,但容量有限。
3. 在堆区分配:动态分配内存。用new/malloc时开辟,delete/free时释放。生存期由用户指定,灵活。但有内存泄露等问题。

常见内存错误及对策
1. 内存分配未成功,却被使用。
对策:使用内存之前检查是否分配成功。用p!=NULL判断。
2. 内存分配成功,未初始化就被使用。
内存的缺省值没有统一的标准。大部分编译器以0作为初始值,但不完全是。
对策:内存初始化时赋初值。
3. 内存操作越界。
对策:只能是小心了。
4. 释放了内存,仍然使用。
(1) 使用显示delete和free的野指针。
对策:释放完内存,将指针置为NULL
(2) 使用隐式delete和free的野指针。主要是指函数返回指向栈内存的指针或引用。
对策:当然是不要返回就可以了。
5. 未释放内存,导致内存泄露。
用new/malloc开辟了内存,没用delete/free释放.
对策:new和delete的个数一定相同;malloc和free的个数一定相同;new[]和[]delete一定对应
五十一、C++ 值传递、指针传递、引用传递https://blog.csdn.net/qq_34793133/article/details/80819690





猜你喜欢

转载自blog.csdn.net/qq_34793133/article/details/80940052