C/C++语言常见面试题汇总
1、变量的声明和定义有什么区别?
变量的定义为变量分配地址和存储空间,变量的声明不会分配地址,一个变量可以在多个地方声明,但是只能在一个地方定义,加入extern关键字修饰的是变量的声明,说明此变量将在文件以外或者文件后面部分定义。
说明:很多时候一个变量,只是声明不分配内存空间,直到具体使用时才初始化,分配内存空间,如外部变量。
int main() { extern int A; //这是个声明而不是定义,声明A是一个已经定义了的外部变量 dosth();//执行函数 } int A; //定义A变量
2、请简述#ifdef、#else、#endif、和#ifndef的作用是?
- 利用#ifdef、#endif将某程序功能模块包括进去,以向特定用户提供该功能,在不需要时,用户可以轻易将其屏蔽;
#ifdef MATH #include "math.c" #endif
- 在子程序上加上标记,以便于追踪和调试;
#ifdef DEBUG printf("Indebugging.......!\r\n"); #endif
- 应对硬件的限制,由于一些具体应用环境的硬件不一样,限于条件,本地缺乏某种设备,只能绕过硬件,直接写出预期结果。
注意:虽然不用条件编译命令而直接用if语句也能到达要求,但是这样做的话,目标程序长(所有的语句都需要编译),运行时间长(在程序运行时会对if语句进行测试),采用条件编译,可以减少被编译的语句,减少目标程序的长度,减少程序的运行时间。
3、请写出int、bool、float、指针变量与"零值"比较的if语句?
//int类型与零值比较 if (n == 0) if (n != 0) //bool类型与零值比较 if (flag) //表示flag为真 if (!flag) //表示flag为假 //float类型与零值比较 const float EPSINON = 0.00001; if ((x >= -EPSINON) && (x <= EPSINON)) //其中EPSINON是允许的误差 //指针变量与零值比较 if (p == NULL) if (p != NULL)
4、结构体是否可以直接赋值?
声明时可以直接初始化,同一结构体的不同对象之间也可以直接赋值,但是当结构体中含有指针成员时一定要小心。
注意:当有多个指针指向同一段内存时,某个指针释放这段内存可能会导致其它指针的非法操作,因此在释放前一定要确保其它指针不再使用这段内存空间。
5、sizeof和strlen的区别?
- sizeof是一个操作符,strlen是库函数;
- sizeof的参数可以是数据的类型,也可以是变量,而strlen只能以结尾为'\0'的字符串作为参数;
- 编译器在编译时就计算出了sizeof的结果,而strlen函数必须在程序运行时才能计算出来,并且sizeof计算的是数据类型占内存的大小,而strlen计算的是字符串实际占用的内存大小;
- 数组做sizeof的参数不退化,数组传递给strlen就退化为指针了。
6、C语言和C++语言中的关键字static有什么区别?
在C语言中static用来修饰局部静态变量和外部静态变量、函数,而C++中的static关键字除了具有上述功能外,还能用来定义类的成员变量和函数,也就是静态成员和静态成员函数。
注意:编程时static的记忆性和全局性特点可以让在不同时期调用的函数进行通信、传递信息,而C++的静态成员则可以在多个对象实例间进行通信、传递信息。
7、C语言的malloc和C++中的new有什么区别?
- new和delete是操作符,可以重载,只能在C++中使用;
- malloc和free是函数,可以覆盖,C和C++都能使用;
- new可以调用对象的构造函数,对应的delete调用对象的析构函数;
- malloc仅仅分配内存,free则是回收内存,并不会执行构造函数和析构函数;
- new、delete返回的是某种数据类型指针,malloc、free返回的是void指针。
注意:malloc申请的内存空间要使用free进行释放,而new申请的内存空间要使用delete释放,不能够混用。
8、请写一个标准宏MIN?
#define min(a, b) ((a) <= (b) ? (a) : (b))
9、++i和i++的区别?
++i先自增1,再返回,i++先返回i,再自增1。
10、关键字volatile有什么作用?
- 状态寄存器一类的并行设备硬件寄存器;
- 中断服务子程序会访问到的非自动变量;
- 多线程间被几个任务访问共享变量。
注意:虽然volatile在嵌入式方面应用比较多,但是在PC软件的多线程中,volatile修饰的临界变量也是非常实用的。
11、一个参数可以既是const又是volatile吗?
一个参数既可以是const又可以是volatile的,当使用const和volatile同时修饰变量时,表示这个变量在程序内部是只读的,不能改变的,只在程序外部条件变化下改变,并且编译器不会优化这个变量,每次使用这个变量时,都会小心地去内存读取这个变量的值,而不是去寄存器读取它的备份。
注意:在这一定要注意const的意思,const只是不允许程序中的代码改变某一变量,其在程序编译器发挥作用,它并没有实际地禁止某段内存的读写特性。
12、*a和&a有什么区别?
&a:含义就是取变量a的地址
*a:使用在不同的地方,含义也不一样
- 在声明语句中,*a只说明a是一个指针变量,例如int *a;
- 在其它语句中,*a前面没有操作数并且a是一个指针时,*a代表指针a指向的内存地址存放的数据,如b=*a;
- *a前面有操作数并且a是一个普通变量时,*a代表乘以a,如c=b*a。
13、用C语言编写一个死循环程序?
while (1) { dosth(); } for (;;) { dosth(); }
注意:很多种途径都可以实现同一种功能,但是不同的方式,时间和空间占用度不同,特别是对于嵌入式软件,处理器速度比较慢,存储空间有限,所以时间和空间优势是选择各种方法的首要考虑条件。
14、结构体内存对齐问题?
https://www.cnblogs.com/Cqlismy/p/11440057.html
15、全局变量和局部变量有什么区别?是怎么实现的?操作系统和编译器是怎么知道的?
- 全局变量是整个程序都可以访问的变量,生存期是从整个程序运行道结束,在程序结束时,全局变量所占用的内存会被释放;
- 局部变量存在于模块(子程序、函数)中,只有所在的模块可以访问,其它模块不能访问,模块结束(函数调用完成),局部变量所占用的内存被释放;
- 操作系统和编译器,可能是通过内存分配的位置来知道的,全局变量会被分配在全局数据段,并且在程序开始运行的时候被加载,局部变量则是在堆栈中进行分配的。
16、请简述C/C++程序编译的内存分配情况?
- 从静态存储区域分配:内存在程序编译时就已经分配好,这块内存在程序的整个运行期间都存在,速度快,不容易出错,例如:全局变量、static变量,常量字符串等;
- 从栈上分配:在执行函数时,函数内部局部变量的存储单元都在栈上创建,函数执行结束时,这些存储单元自动释放,栈内存分配运算内置于处理器的指令中,效率很高,但是分配的内存容量有限;
- 从堆上分配:也就是动态内存分配,程序在运行时使用malloc或者new申请任意大小的内存,程序员自己负责何时使用free或者delete进行内存释放,动态内存的生存期是由程序员决定的,使用非常灵活,如果在堆上分配了内存空间,就有责任回收它,否则运行的程序会出现内存泄漏,另外频繁地分配和释放不同大小的堆空间将会产生堆内碎块。
- 一个C/C++程序编译时内存分为5大存储区,分别是栈区、堆区、全局区、文字常量区、程序代码区。
17、请简述strcpy、sprintf和memcpy的区别?
- 操作对象不同,strcpy的两个操作对象均为字符串,sprintf的操作源对象可以是多种数据类型,目的操作对象是字符串,memcpy的两个对象就是任意可操作的内存地址,并不限于何种数据类型;
- 执行效率不同,memcpy的效率最高,strcpy次之,sprintf的效率最低;
- 实现功能不同,strcpy主要实现字符串变量间的拷贝,sprintf主要实现其它数据类型格式化到字符串,memcpy主要用于内存块间的拷贝;
注意:strcpy、sprintf和memcpy都可以实现拷贝的功能,但是针对的对象不同,需要根据实际需求,来选择合适的函数区实现拷贝功能。
18、请解释(*(void (*)())0)()的含义?
- void (*0)():是一个返回值为void,参数为空的函数指针0;
- (void (*)())0:把0转变成一个返回值为void,参数为空的函数指针;
- *(void (*)())0:在上句的基础上加*表示整个是一个返回值为void,无参数,并且起始地址为0的函数的名字;
- ((void (*)())0)():上句的函数名所对应的函数的调用。
19、C语言的指针和引用和C++的有什么区别?
- 指针有自己的一块空间,而引用只是一个别名;
- 使用sizeof看一个指针的大小是4字节,而引用则是被引用对象的大小;
- 作为参数传递时,指针需要被解引用才可以对对象进行操作,而直接对引用的修改都会改变引用所指向的对象;
- 可以有const指针,但是没有const引用;
- 指针在使用中可以指向其它对象,但是引用只能是一个对象的引用,不能被改变;
- 指针可以有多级指针,而引用只有一级;
- 指针和引用使用++运算符的意义不一样;
- 如果返回动态内存分配的对象或者内存,必须使用指针,引用可能会引起内存泄漏。
20、typedef和define有什么区别?
- 用法不同:typedef用来定义一种数据类型的别名,增强程序的可读性,define主要用来定义常量以及书写复杂使用频繁的宏;
- 执行时间不同:typedef是编译过程的一部分,有类型检查的功能,define是宏定义,是预编译的部分,其发生在编译之前,只是简单的进行字符串替换,不会进行类型的检查;
- 作用域不同:typedef有作用域限定,define不受作用域限定,只要是在define声明后的引用,都是正确的;
- 对指针的操作不同:typedef和define定义指针时有很大区别。
注意:typedef定义是语句,句尾需要加上分号,而define不是语句,不能在句尾加上分号。
21、指针常量和常量指针有什么区别?
指针常量是指定义了一个指针,这个指针的值只能在定义时初始化,其它地方不能改变。常量指针是指定义了一个指针,这个指针指向一个只读的对象,不能通过常量指针来改变这个对象的值。指针常量强调的是指针的不可变性,而常量指针强调的是指针对其所指对象的不可变性。
注意:无论是指针常量还是常量指针,其最大的用途就是作为函数的形式参数,保证实参在被调用函数中的不可改变特性。
22、请简述队列和栈的异同?
队列和栈都是线性存储结构,但是两者的插入和删除数据的操作不同,队列是"先进先出",栈是“先进后出”。
注意:区别栈区和堆区,堆区的存取是"顺序随意",而栈区是"先进后出",栈是由编译器自动分配释放,存放函数的参数值,局部变量的值等,其操作方式类似于数据结构中的栈,堆一般是由程序员分配释放,若程序员不释放,程序结束时可能由OS回收,分配方式类似于链表,它与本题中的堆和栈是两回事,堆栈只是一种数据结构,而堆区和栈区是程序的不同内存存储区域。
23、如何设置地址为0x67a9的整型变量的值为0xaa66?
int *ptr; ptr = (int *)0x67a9; *ptr = 0xaa66;
注意:这道题就是强制类型转换的典型例子,无论在什么平台,地址长度和整型数据的长度是一样的,既一个整型数据可以强制转换成地址指针类型,只要有意义即可。
24、请编程实现字符串转换为数字?
编码实现函数atoi(),设计一个程序,把一个字符串转化为一个整型数值,例如:字符串"5486321",转化成整型5486321。
int myatoi(const char *str) { int num = 0; //保存转换后的数值 int isNegative = 0; //记录字符串中是否有负号 int n = 0; char *p = str; if (p == NULL) //判断指针的合法性 return -1; while (*p++ != '\0') //计算字符串长度 n++; p = str; if (p[0] == '-') //判断数组是否有负号 isNegative = 1; for (int i = 0; i < n; i++) { char temp = *p++; if (temp > '9' || temp < '0') //滤除非数字字符 continue; if (num != 0 || temp != '0') //滤除字符串开始的'0'字符 { temp -= 0x30; //将数字字转换为数值 num += temp * int(pow(10, n - 1 - i)); } } if (isNegative) return (0 - num); else return num; }
25、C语言的结构体和C++的有什么区别?
- C语言的结构体中是不能有函数成员的,而C++的类可以有;
- C语言的结构体中数据成员是没有private、public和protected访问限定的,而C++的类成员有这些访问限定;
- C语言的结构体是没有继承关系的,而C++的类却有丰富的继承关系。
注意:虽然C的结构体和C++的类有很大的相似度,但是类是实现面向对象的基础,而结构体只可以简单地理解为类的前身。
26、简述指针常量与常量指针的区别?
- 指针常量是指定义了一个指针,这个指针的值只能在定义时初始化,其它地方不能改变,常量指针是指定义了一个指针,这个指针指向一个只读的对象,不能通过常量指针来改变这个对象的值;
- 指针常量调调的是指针的不可变性,而常量指针强调的是指针对其所值对象的不可改变性。
注意:无论是指针常量还是常量指针,其最大的用途就是作为函数的形式参数,保证实参在被调用函数中的不可改变特性。
27、如何避免"野指针"?
- 指针变量声明时没有被初始化,解决办法为,指针声明时初始化,初始值可以是具体的地址值,也可以让它指向NULL;
- 指针p被free或者delete之后,没有设置为NULL,解决办法为,指针指向的内存空间被释放后指针应该指向NULL;
- 指针操作超越了变量的作用范围,解决办法为,在变量的作用域结束前释放掉变量的地址空间,并且让指针指向NULL。
28、句柄和指针的区别和联系是什么?
句柄和指针其实是两个截然不同的概念,Windows系统用句柄标记系统资源,隐藏系统的信息,只要知道有这个东西,然后去调用就行了,是32bit的uint。指针则标记某个内存地址,两者是不同的概念。
29、new/delete与malloc/free的区别是什么?
- new能自动计算需要分配的内存空间,而malloc需要手工计算字节数;
int *p = new int[2]; int *q = (int *)malloc(2 * sizeof(int));
- new与delete直接带具体类型的指针,malloc与free返回void类型的指针;
- new类型是安全的,而malloc不是,例如,int * p = new float[2];就会报错,而int *p = malloc(2 * sizeof(float));编译时编译器无法指出错误来;
- new一般分为两步,new操作和构造,new操作对应与malloc,但new操作可以重载,可以自定义内存分配策略,步做内存分配,甚至分配到非内存设备上,而malloc步行;
- new调用构造函数,malloc不会调用构造函数,delete调用析构函数,而free不会调用析构函数;
- malloc/free需要库文件stdlib.h支持,而new/delete则不需要。
注意:delete和free被调用后,内存不会立即回收,指针也不会指向空,delete或free仅仅是告诉操作系统,这一块内存被释放了,可以用作其它用途,但是由于没有重新对这块内存进行写操作,所以内存中的变量数值并没有发生变化,出现野指针的情况,因此,释放完内存后,应该将该指针指向NULL。
30、请说一说extern "C"?
extern "C"的主要作用就是为了能够正确实现C++代码调用其它C语言代码,加上extern "C"后,会指示编译器这部分代码按C语言的方式进行编译,而不是C++。由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名,而C语言不支持函数重载,因此编译C语言代码的函数时,不会带上函数的参数类型,一般只包括函数名。
该功能十分有用处,因为在C++出现以前,很多代码都是C语言编写的,而且很底层的库也是C语言写的,为了更好地支持原来的C代码和已经写好的C语言库,需要在C++中尽可能地支持C,而extern "C"就是其中的一个策略。
- C++代码调用C语言代码;
- 在C++的头文件中使用;
- 在多个人的协同开发时,可能有人擅长C语言,有的人擅长C++,在这样的情况下也会用到。
31、请说一说C++中struct和class的区别是什么?
在C++中,class和struct做类型定义时只有两点区别:
- 默认的继承权限不同,class默认继承权限是private继承,而struct默认是public继承;
- class还可用于定义模板参数,像typename,但是关键字struct不能定义模板参数。
C++之所以保留struct关键字,原因是:
- 保证与C语言的向下兼容性,C++必须提供一个struct;
- C++中的struct定义必须百分百地保证与C语言中的sruct的向下兼容性,把C++中的最基本的对象单元规定为class而不是struct,就是为了避免各种兼容性要求的限制;
- 对struct定义的拓展性使C语言的代码能够更容易地移植到C++中。
32、C++类内可以定义引用数据成员吗?
33、C++中类成员的访问权限?
34、什么是右值引用,跟左值又有什么区别?
35、面向对象的三大特征?
36、请说一说C++中四种cast转换?
37、C++中的空类有哪些成员函数?
38、对C++中的smart pointer四个智能指针:shared_ptr、unique_ptr、weak_ptr、auto_ptr的理解?
39、请说说强制类型转换运算符?
40、谈谈你对拷贝构造函数和赋值运算符的认识?
41、在C++中,使用malloc申请的内存是否能通过delete释放?使用new申请的内存是否能用free释放?
42、用C++设计一个不能被继承的类?
43、用C++自己实现一个String类?
44、访问基类的私有虚函数?
45、对虚函数和多态的理解?
46、请简述类成员函数的重写、重载和隐藏的区别?
47、链表和数组有什么区别?
48、用两个栈实现一个队列的功能?
49、vector的底层原理?
50、vector中的reserve和resize的区别是什么?