C/C++面试知识点
提示:本文将记录遇到的C/C++问题,随时会增加
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
C/C++基础部分
-
Q:某文件中定义的静态全局变量(或称静态外部变量)其作用域是?
A:通常分析变量都是从两个方向分析。1、生命周期,普通局部变量的生命周期为该域的起止,全局变量和静态本地变量生命周期是程序运行的全过程。2、作用域,局部变量的作用域为定义改变量的域中,静态局部变量改变的是生命周期。全局变量生命周期为程序运行的全过程,静态全局变量限制其作用域为本文件。并且函数外的static变量不能被extern到别的文件。 -
Q:如何判断一段程序是C编译器编译还是C++编译器编译?
A:在程序编译过程会自动添加一些宏,其中一个可用于判断是否为C++编译器编译。
#ifdef __cplusplus //双下划线为非人为添加的
cout<<"cpp"<<endl;
#else
cout<<"C"<<endl;
#endif`
- Q:C和C++的不同?
A:C是面向过程的(也可以通过cobject一些方法编写面向对象的程序),C++是面向对象的。C体积小效率高,适合写对效率要求更加苛刻的地方。C++是C的plus所以更加的复杂和适合编写规模大的程序,侧重对象而不是过程,侧重类的设计而不是逻辑过程的设计。 - Q:C和C++中struct的不同点?以及C++中struct和class区别
A:
struct | 保护功能 | 能否定义函数 |
---|---|---|
C | 无 | 不能定义函数 |
C++ | 有 | 可以定义函数,but默认是public |
C++ | class | struct |
---|---|---|
默认访问属性 | private | public |
默认继承属性 | private | public |
- Q:如何判断机器是32位还是64位?
A:定义一个指针int* p,然后用sizeof§,返回的便是操作系统的位数。
或者
int tmp_num=~0;
if(a>65535){
cout<<"32bit"<<endl;
}else{
cout<<"16 bit"<<endl;
- C/C++内存的分配方式
C语言内存区 | 特点分析 |
---|---|
栈 | auto 在执行函数时,函数内局部变量的存储就是在栈上,函数执行结束时内存自动释放,效率高但是空间有限。 |
堆 | 动态内存分配,程序在运行时通过malloc申请内存,但需要手动free释放内存,使用灵活但容易泄露 |
全局静态区 | 存放全局变量和静态变量,空间由系统管理,(函数外部定义的变量,在定义的时候,这个空间就已经被开辟了)程序开始执行时开辟空间,程序结束执行时回收空间,程序执行期间一直存在。 |
常量区 | 存放常量,在整个程序运行期间不能改变,生命周期与程序同 |
代码区 | 存放程序执行的CPU指令,我们的代码最后在编译的时候都会转化为相应的CPU指令,告诉计算机如何执行程序 |
C++语言内存区 | 特点分析 |
---|---|
栈 | auto 在执行函数时,函数内局部变量的存储就是在栈上,函数执行结束时内存自动释放,效率高但是空间有限。 |
堆 | new,动态内存分配,程序在运行时通过new申请内存,但需要手动delete释放内存,使用灵活但容易泄露 |
自由存储区 | 就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。 |
全局/静态存储区 | 全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。 |
常量存储区 | 这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改,而且方法很多) |
-
Q:对于一个频繁使用的小函数,在C中和C++中怎样实现更好?
A: C中用宏,C++用内联函数(inline)。 -
Q:引用和指针的区别?
A:指针不必初始化,引用必须初始化(因为指针可以改变,引用不可以改变)。引用更加安全,指针更加灵活。 -
Q:有了malloc/free还要new/delete干嘛?
malloc/free是C/C++的标准库函数,new/delete是C++的运算符,二者皆可动态申请内存。
malloc/free适用于内置类型,因为其不会调用构造函数,和对应的析构,but new/delete可以。
由于malloc/free 是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于 malloc/free。
因此 C++语言需要一个能完成动态内存分配和初始化工作的运算符 new,以及一个能完成清理与释放内存工作的运算符 delete。
注意 new/delete 不是库函数。 -
Q:如果申请不到足够大的内存块,malloc和new会返回null指针,怎么处理?
A:申请完之后判断指针是否为null,如果是,则进行相应的处理,并结束相应的程序。 -
Q:C++是不是类型安全的?
A:不是类型安全的,两个不同的类型可以牵制类型转换。 -
转移字符’'的作用?
通常用于特殊格式的输出如\n等,同意忘记的是【+三个八进制】或者【+两个十六进制】。 -
Q:const关键字的作用?
A:const可以阻止对改变量的后续更改。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;
对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;`
const char* p;//p是指向const的指针,可以改变p的指向,但是不能通过P来改变指向的变量的值。
char const *p;//同上
char* const p;//p是一个指针,且不能改变指向
//其中区别可以看const在*前后。
在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量;`
first::int fun()const{return 0;}
对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。
14.Q: 交换两个变量的方法
A:
a=a^b;
b=a^b;//a ^ b ^ b = a
a=a^b;//a ^ b ^ a = b
- Q:打印当前文件的文件名,行号,函数名
A:`
cout<<__FILE__;//文件名
cout<<__LINE__;//行号
cout<<__FUNC__;//函数名
- Q:当一个类A 中没有生命任何成员变量与成员函数,这时sizeof(A)的值是多少?
A:是1.深度探索c++对象模型中是这样说的: 那是被编译器插进去的一个char ,使得这个class的不同实体(object)在内存中配置独一无二的地址。 也就是说这个char是用来标识类的不同对象的。 - Q: 为什么A++不能是左值?
A:首先对于i++的实现是:
int temp;
temp = i;
i = i+1;
return temp;
而++i的实现是:
i = i+1;
return i;
所以对于我们提出来的问题已经能得到解决了:
i++=5; 是错误的是因为i++返回的是编译器自动分配的临时变量temp,而这个temp并不是你程序中定义的可寻址变量的引用 ,也就是说你不能通
过地址对temp进行操作.(换句话说就是不能作为左值),因为temp是一个临时变量。++i=5;是正确的就是因为其返回值就是i。
- ?:,问号后的两个操作数必须是同一类型?
必须是同一类型。 - Q:内联函数是否做类型检查?
A:内联函数要做参数类型检查, 这是内联函数跟宏相比的优势。 - 函数 assert 的用法?
A:断言assert是仅在debug版本起作用的宏,用于检查“不应该“发生的情况。
程序员可以把assert看成一个在任何系统状态下都可以安全使用的无害测试手段。 - Q:const 与 #define 的比较 ,const有什么优点?
A:(1) const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。
而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误(边际效应) 。
(2)有些集成化的调试工具可以对 const 常量进行调试,但是不能对宏常量进行调试。 - Q:
void * ( * (*fp1)(int))[10]; //fp1是一个指针,指向一个函数,这个函数的参数为int型,函数的返回值是一个指针,这个指针指向一个数组,这个数组有10个元素,每个元素是一个void*型指针。
float (*(* fp2)(int,int,int))(int);//fp2是一个指针,指向一个函数,这个函数的参数为3个int型,函数的返回值是一个指针,这个指针指向一个函数,这个函数的参数为int型,函数的返回值是float型。
int (* ( * fp3)())[10](); //fp3是一个指针,指向一个函数,这个函数的参数为空,函数的返回值是一个指针,这个指针指向一个数组,这个数组有10个元素,每个元素是一个指针,指向一个函数,这个函数的参数为空,函数的返回值是int型。
- Q:你觉得如果不使用常量,直接在程序中填写数字或字符串,将会有什么麻烦?
A:1) 程序的可读性(可理解性)变差。程序员自己会忘记那些数字或字符串是什么意思,用户则更加不知它们从何处来、表示什么。
(2) 在程序的很多地方输入同样的数字或字符串,难保不发生书写错误。
(3) 如果要修改数字或字符串,则会在很多地方改动,既麻烦又容易出错。
C++特性
-
Q:面向对象的三个基本特征。
A:封装:
把客观事物封装成类,对自身的数据和函数进行保护。
继承:
为了代码复用,广义的继承分三类:实现继承(指使用基类的属性和方法而无需额外编码的能力)、可视继承(子窗体使用父窗体的外观和实现代码)、接口继承(仅使用属性和方法,实现滞后到子类实现)。
多态:是将父对象设置成为和一个或更多的与他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。
简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。 -
Q: 多态的作用?
A:隐藏实现细节,接口重用。 -
Q:虚函数表。
A:虚函数表是在编译时期建立的一个组织虚函数入口地址的数组。但是对象的虚指针是在运行期间(构造函数被调用时)进行初始化的。这是多态实现的关键。 -
虚函数。
用一代指针调用:
两代:
父类虚函数,子类非虚函数,则调用父类。
父类非虚函数,子类虚函数,则调用父类。
三代:
一代非虚函数,二三代虚函数,调用一代。
一代虚函数,二三无所谓,调用三代。
用二代指针调用:
一代或二代虚函数,三代无所谓,调用三代。 -
C++中有没有纯虚构造函数?
A:构造函数不能是虚的,只能有虚的析构函数 -
Q:析构函数和虚函数的用法和作用?
A:析构函数是特殊的类成员函数,它没有返回类型,没有参数,不能随意调用,也没有重载,只有在类对象的生命期结束的时候,由系统自动调用。有释放内存空间的作用。虚函数是C++多态的一种表现, 使用虚函数,我们可以灵活的进行动态绑定,当然是以一定的开销为代价。
派生类的析构函数必须是虚函数,否则会内存泄漏 -
Q:通常不能声明为虚函数的有?
A:普通函数非成员函数)、静态成员函数、内联成员函数、构造函数、友元函数。 -
Q:重载(overload)、重写(override,有的书也叫做“覆盖”)、重定义(redefinition)的区别?
A:重载 同一名字空间 是指允许存在多个同名函数,而这些函数的参数表不同。
重写/覆盖 不同名字空间,用于继承,子类重新定义父类虚函数的方法。
重定义/隐藏 不同名字空间 用于继承,派生类与基类的函数同名,屏蔽基类的函数。 -
全局变量的初始化,不是由 main 函数引起的。
-
Q:① return String(s1 + s2); 和 ②String temp(s1 + s2); return temp; 一样吗?
A:①这是临时对象的语法,表示“创建一个临时对象并返回它” 。
②将发生三件事。首先,temp 对象被创建,同时完成初始化;然后拷贝构造函数把 temp 拷贝到保存返回值的外部存储单元中;最后,temp 在函数结束时被销毁(调用析构函数) 。
然而“创建一个临时对象并返回它”的过程是不同的,编译器直接把临时对象创建并初始化在外部存储单元中,省去了拷贝和析构的化费,提高了效率。 -
Q:在 C++的一个类中声明一个 static 成员变量有没有用?
A:改变量是所有实例所共享,也就是无论多少个实例,该变量只有一份,属于类,不被继承。而类的静态成员函数也只能访问静态成员(变量或函数)。静态成员函数不可重写,通过类名调用。 -
Q:函数模板和类模板的区别?以及模板类
A:函数模板通过调用时参数的类型指定,用于函数的声明和函数实现中。类模板需要程序员显式指定,同样用于数据的类型参数化。
“一般来讲,类模版是结构相似但不同的类的抽象,是描述性的;形式:template class 类版名;模版类是 类模版铸造出来的类,是具体的类,是如结构等相似的类型;” 类模板只是一个抽象的描述,在应用时在内存中是不占空间的,
而模板类是一个具体的东西!
泛型编程。 -
Q:C++空类默认产生哪些成员函数?
A:
class Empty
{
public:
Empty();//缺省构造函数
Empty(const Empty&);//拷贝构造函数
virtual ~Empty();//虚析构函数
Empty& operator=(const Empty&)//赋值运算符
Empty& operator&();//取址运算符
const Empty& operator&()const;//const取址运算符
}
- Q:In C++, what does “explicit” mean?
A:C++中的 explicit 关键字用来修饰类的构造函数,表明该构造函数是显式的,在某些情况下,我们要求类的使用者必须显示调用类的构造函数时就需要使用 explicit,反之默认类型转换可能会造成无法预期的问题。 - Q:所有的运算符都能重载吗?
A:在 C++运算符集合中,有一些运算符是不允许被重载的。这种限制是出于安全方面的考虑,可防止错误和混乱。
(1)不能改变 C++内部数据类型(如 int,float 等)的运算符。
(2)不能重载‘.’,因为‘.’在类中对任何成员都有意义,已经成为标准用法。
(3)不能重载目前 C++运算符集合中没有的符号,如#,@,$等。原因有两点,一是难以理解,二是难以确定优先级。
(4)对已经存在的运算符进行重载时,不能改变优先级规则,否则将引起混乱。
其他
-
Q:VC 中,编译工具条内的 Debug 与 Release 选项是什么含义?
A:Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。
Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。
Debug 带有大量的调试代码,运行时需要相应的运行库,发布模式程序紧凑不含有调试代码和信息,直接可以运行(如果不需要运行库)