常见嵌入式/C/C++面试题100+集合(含参考答案)-更新中
Posted 虫无涯
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了常见嵌入式/C/C++面试题100+集合(含参考答案)-更新中相关的知识,希望对你有一定的参考价值。
常见嵌入式/C/C++面试题100+集合(含参考答案)
- 面试题1:变量的声明和定义有什么区别?
- 面试题2:sizeof和strlen的区别?
- 面试题3:C语言的关键字static和C++的关键字static有什么区别?
- 面试题4:C中的malloc和C++中的new有什么区别?
- 面试题5:写一个“标准”宏 MIN,这个宏输入两个参数并且返回较小的一个
- 面试题6:一个指针可以是volatile吗?
- 面试题7:简述 C、C++程序编译的内存分配情况
- 面试题8:简述strcpy、sprintf与memcpy的区别
- 面试题9:面向对象的三大特征?
- 面试题10:C++的空类有哪些成员函数?
- 面试题11:谈谈你对拷贝构造函数和赋值运算符的认识
- 面试题12:简述类成员函数的重写、重载和隐藏的区别
- 面试题13:简述多态实现的原理
- 面试题14:链表和数组有什么区别?
- 面试题15:简述队列和栈的异同
- 面试题16:&&和&、||和|有什么区别?
- 面试题17:C++的引用和 C 语言的指针有什么区别?
- 面试题18:typedef和define有什么区别?
- 面试题19:关键字const是什么?
- 面试题20:static有什么作用?
- 面试题21:extern有什么作用?
- 面试题22:流操作符重载为什么返回引用?
- 面试题23:简述指针常量与常量指针区别
- 面试题24:如何避免“野指针”?
- 面试题25:常引用有什么作用?
- 面试题26:构造函数能否为虚函数?
- 面试题27:关键字volatile有什么含意(举例说明)?
- 面试题28:程序什么时候应该使用线程,什么时候单线程效率高?
- 面试题29:Linux有内核级线程吗?
- 面试题30:C++中什么数据分配在栈或堆中,New分配数据是在近堆还是远堆中?
- 面试题31:使用线程是如何防止出现大的波峰?
- 面试题32:函数模板与类模板有什么区别?
- 面试题33:动态连接库的两种方式?
- 面试题34:什么是平衡二叉树?
- 面试题35:冒泡排序算法的时间复杂度是什么?
- 面试题36:C和C++中的struct有什么不同?
- 面试题37:用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)
- 面试题38:不能做switch()的参数类型是?
- 面试题39:全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?
- 面试题40:8086是多少位的系统?在数据总线上是怎么实现的?
- 面试题41:局部变量能否和全局变量重名?
- 面试题42:结构传递和返回是如何实现的 ?
- 面试题43:为什么sizeof返回的值大于结构大小的期望值 ,是不是尾部都有填充?
- 面试题44:枚举和一组预处理的 #define有什么不同 ?
- 面试题45:i++和++i有什么区别 ?
- 面试题46:*p++自增p还是p所指向的变量?
- 面试题47:通用指针类型是什么 ?当我把函数指针赋向void *类型的时候,编译通不过
- 面试题48:什么是空指针?
- 面试题49:调用malloc() 的时候,错误 “不能把void*转换为int*"是什么意思?
- 面试题50:调用malloc()为一个函数的局部指针分配内存时,还需要用free()显式地释放吗?
- 面试题51:sizeof操作符可以用在#if预处理指令中吗?
- 面试题52:main()的第3个参数envp是怎么回事?
- 面试题53:为什么不能对void*指针进行算术运算?
面试题1:变量的声明和定义有什么区别?
- 为变量分配地址和存储空间的称为定义,不分配地址的称为声明。
- 一个变量可以在多个地方声明,但是只在一个地方定义。加入
extern
修饰的是变量的声明,说明此变量将在文件以外或在文件后面部分定义。
说明:很多时候一个变量,只是声明不分配内存空间,直到具体使用时才初始化,分配内存空间,如外部变量。
面试题2:sizeof和strlen的区别?
sizeof
是一个操作符,strlen
是库函数。sizeof
的参数可以是数据的类型,也可以是变量,而strlen
只能以结尾为‘\\0
‘的字符串作参数。- 编译器在编译时就计算出了
sizeof
的结果。而strlen
函数必须在运行时才能计算出来。并且sizeof
计算的是数据类型占内存的大小,而 strlen 计算的是字符串实际的长度。 - 数组做
sizeof
的参数不退化,传递给strlen
就退化为指针了。
注意:有些是操作符看起来像是函数,而有些函数名看起来又像操作符,这类容易混淆的名称一定要加以区分,否则遇到数组名这类特殊数据类型作参数时就很容易出错。最容易混淆为函数的操作符就是sizeof。
面试题3:C语言的关键字static和C++的关键字static有什么区别?
- 在 C 中 static 用来修饰局部静态变量和外部静态变量、函数。
- 而 C++中除了上述功能外,还用来定义类的成员变量和函数。即静态成员和静态成员函数。
注意:编程时 static 的记忆性,和全局性的特点可以让在不同时期调用的函数进行通信,传递信息,而C++的静态成员则可以在多个对象实例间进行通信,传递信息。
面试题4:C中的malloc和C++中的new有什么区别?
new、delete
是操作符,可以重载,只能在 C++中使用。malloc、free
是函数,可以覆盖,C、C++中都可以使用。new
可以调用对象的构造函数,对应的delete
调用相应的析构函数。malloc
仅仅分配内存,free
仅仅回收内存,并不执行构造和析构函数new、delete
返回的是某种数据类型指针,malloc、free
返回的是void
指针。
注意:malloc 申请的内存空间要用 free 释放,而 new 申请的内存空间要用 delete
释放,不要混用。因为两者实现的机理不同。
面试题5:写一个“标准”宏 MIN,这个宏输入两个参数并且返回较小的一个
#define min(a,b)((a)<=(b)?(a):(b))
面试题6:一个指针可以是volatile吗?
- 可以,因为指针和普通变量一样,有时也有变化程序的不可控性。
- 常见例:子中断服务子程序修改一个指向一个
buffer
的指针时,必须用volatile
来修饰这个指针。
说明:指针是一种普通的变量,从访问上没有什么不同于其他变量的特性。其保存的数值是个整型数据,和整型变量不同的是,这个整型数据指向的是一段内存地址。
面试题7:简述 C、C++程序编译的内存分配情况
- 从静态存储区域分配:
内存在程序编译时就已经分配好,这块内存在程序的整个运行期间都存在。速度快、不容易出错, 因为有系统会善后。例如全局变量,static变量等。
- 在栈上分配:
在执行函数时,函数内局部变量的存储单元都在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
- 从堆上分配:
即动态内存分配。程序在运行的时候用 malloc 或 new 申请任意大小的内存,程序员自己负责在何时用 free 或 delete释放内存。动态内存的生存期由程序员决定,使用非常灵活。如果在堆上分配了空间,就有责任回收它,否则运行的程序会出现内存泄漏,另外频繁地分配和释放不同大小的堆空间将会产生堆内碎块。
一个 C、C++程序编译时内存分为 5 大存储区:堆区、栈区、全局区、文字常量区、程序代码区。
面试题8:简述strcpy、sprintf与memcpy的区别
- 操作对象不同:
strcpy
的两个操作对象均为字符串,sprintf
的操作源对象可以是多种数据类型,目的操作对象是字符串,memcpy
的两个对象就是两个任意可操作的内存地址,并不限于何种数据类型。 - 执行效率不同:
memcpy
最高,strcpy
次之,sprintf
的效率最低。 - 实现功能不同:
strcpy
主要实现字符串变量间的拷贝,sprintf
主要实现其他数据类型格式到字符串的转化,memcpy
主要是内存块间的拷贝。
说明:strcpy、sprintf 与 memcpy 都可以实现拷贝的功能,但是针对的对象不同,根据实际需求,来选择合适的函数实现拷贝功能。
面试题9:面向对象的三大特征?
面向对象的三大特征是封装性、继承性和多态性:
- 封装性:将客观事物抽象成类,每个类对自身的数据和方法实行
protection(private, protected,public)。
- 继承性:广义的继承有三种实现形式:实现继承(使用基类的属性和方法而无需额外编码的能力)、可视继承(子窗体使用父窗体的外观和实现代码)、接口继承(仅使用属性和方法,实现滞后到子类实现)。
- 多态性:是将父类对象设置成为和一个或更多它的子对象相等的技术。用子类对象给父类对象赋值之后,父类对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。
说明:面向对象的三个特征是实现面向对象技术的关键,每一个特征的相关技术都非常的复杂,程序员应该多看、多练。
面试题10:C++的空类有哪些成员函数?
- 缺省构造函数
- 缺省拷贝构造函数
- 缺省析构函数
- 缺省赋值运算符
- 缺省取址运算符
- 缺省取址运算符
const
注意:有些书上只是简单的介绍了前四个函数。没有提及后面这两个函数。但后面这两个函数也是空类的默认函数。另外需要注意的是,只有当实际使用这些函数的时候,编译器才会去定义它们。
面试题11:谈谈你对拷贝构造函数和赋值运算符的认识
拷贝构造函数和赋值运算符重载有以下两个不同之处:
- 拷贝构造函数生成新的类对象,而赋值运算符不能。
- 由于拷贝构造函数是直接构造一个新的类对象,所以在初始化这个对象之前不用检验源对象是否和新建对象相同。而赋值运算符则需要这个操作,另外赋值运算中如果原来的对象中有内存分配要先把内存释放掉。
注意:当有类中有指针类型的成员变量时,一定要重写拷贝构造函数和赋值运算符,不要使用默认的。
面试题12:简述类成员函数的重写、重载和隐藏的区别
- 重写和重载主要有以下几点不同。
1、范围的区别:被重写的和重写的函数在两个类中,而重载和被重载的函数在同一个类中。
2、参数的区别:被重写函数和重写函数的参数列表一定相同,而被重载函数和重载函数的参数列表一 定不同。
3、virtual的区别:重写的基类中被重写的函数必须要有 virtual 修饰,而重载函数和被重载函数可以被virtual 修饰,也可以没有。
- 隐藏和重写、重载有以下几点不同。
1、与重载的范围不同:和重写一样,隐藏函数和被隐藏函数不在同一个类中。
2、参数的区别:隐藏函数和被隐藏的函数的参数列表可以相同,也可不同,但是函数名肯定要相同。 当参数不相同时,无论基类中的参数是否被virtual 修饰,基类的函数都是被隐藏,而不是被重写。
说明:虽然重载和覆盖都是实现多态的基础,但是两者实现的技术完全不相同,达到的目的也是完全不同的,覆盖是动态态绑定的多态,而重载是静态绑定的多态。
面试题13:简述多态实现的原理
- 编译器发现一个类中有虚函数,便会立即为此类生成虚函数表
vtable
。虚函数表的各表项为指向对应虚函数的指针。 - 编译器还会在此类中隐含插入一个指针
vptr
(对vc
编译器来说,它插在类的第一个位置上)指向虚函数表。 - 调用此类的构造函数时,在类的构造函数中,编译器会隐含执行
vptr
与vtable
的关联代码,将vptr
指向对应的vtable
,将类与此类的vtable
联系了起来。 - 另外在调用类的构造函数时,指向基础类的指针此时已经变成指向具体的类的
this
指针,这样依靠此this
指针即可得到正确的vtable
。 - 如此才能真正与函数体进行连接,这就是动态联编,实现多态的基本原理。
注意:一定要区分虚函数,纯虚函数、虚拟继承的关系和区别。牢记虚函数实现原理,因为多态C++面试的重要考点之一,而虚函数是实现多态的基础。
面试题14:链表和数组有什么区别?
- 存储形式:数组是一块连续的空间,声明时就要确定长度。链表是一块可不连续的动态空间,长度可变,每个结点要保存相邻结点指针。
- 数据查找:数组的线性查找速度快,查找操作直接使用偏移地址。链表需要按顺序检索结点,效率低。
- 数据插入或删除:链表可以快速插入和删除结点,而数组则可能需要大量数据移动。
- 越界问题:链表不存在越界问题,数组有越界问题。
说明:在选择数组或链表数据结构时,一定要根据实际需要进行选择。数组便于查询,链表便于插入删除。数组节省空间但是长度固定,链表虽然变长但是占了更多的存储空间。
面试题15:简述队列和栈的异同
- 队列和栈都是线性存储结构,但是两者的插入和删除数据的操作不同,队列是“先进先出”,栈是“后进先出”。
注意:区别栈区和堆区。堆区的存取是“顺序随意”,而栈区是“后进先出”。栈由编译器自动分配释放
,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。堆一般由程序员分配释放, 若程序员不释放,程序结束时可能由 OS
回收。分配方式类似于链表。它与本题中的堆和栈是两回事。堆栈只是一种数据结构,而堆区和栈区是程序的不同内存存储区域。
面试题16:&&和&、||和|有什么区别?
&和|
对操作数进行求值运算,&&和||
只是判断逻辑关系。&&和||
在在判断左侧操作数就能确定结果的情况下就不再对右侧操作数求值。
注意:在编程的时候有些时候将&&或||替换成&或|没有出错,但是其逻辑是错误的,可能会导致不可预想的后果(比如当两个操作数一个是 1
另一个是 2 时)。
面试题17:C++的引用和 C 语言的指针有什么区别?
- 引用必须被初始化,但是不分配存储空间。指针不声明时初始化,在初始化的时候需要分配存储空间。
- 引用初始化以后不能被改变,指针可以改变所指的对象。
- 不存在指向空值的引用,但是存在指向空值的指针。
注意:引用作为函数参数时,会引发一定的问题,因为让引用作参数,目的就是想改变这个引用所指向地址的内容,而函数调用时传入的是实参,看不出函数的参数是正常变量,还是引用,因此可能会引发错误。所以使用时一定要小心谨慎。
面试题18:typedef和define有什么区别?
- 用法不同:
typedef
用来定义一种数据类型的别名,增强程序的可读性。define
主要用来定义常量,以及书写复杂使用频繁的宏。 - 执行时间不同:
typedef
是编译过程的一部分,有类型检查的功能。define
是宏定义,是预编译的部分,其发生在编译之前,只是简单的进行字符串的替换,不进行类型的检查。 - 作用域不同:
typedef
有作用域限定。define
不受作用域约束,只要是在define
声明后的引用都是正确的。 - 对指针的操作不同:
typedef
和define
定义的指针时有很大的区别。
注意:typedef 定义是语句,因为句尾要加上分号。而 define 不是语句,千万不能在句尾加分号。
面试题19:关键字const是什么?
const
用来定义一个只读的变量或对象。主要优点:便于类型检查、同宏定义一样可以方便地进行参数的修改和调整、节省空间,避免不必要的内存分配、可为函数重载提供参考。
说明:const 修饰函数参数,是一种编程规范的要求,便于阅读,一看即知这个参数不能被改变,实现时不易出错。
面试题20:static有什么作用?
static
在C中主要用于定义全局静态变量、定义局部静态变量、定义静态函数。在 C++中新增了两种作用:定义静态数据成员、静态函数成员。
注意:因为 static 定义的变量分配在静态区,所以其定义的变量的默认值为 0,普通变量的默认值为随机数,在定义指针变量时要特别注意。
面试题21:extern有什么作用?
extern
标识的变量或者函数声明其定义在别的文件中,提示编译器遇到此变量和函数时在其它模块中寻找其定义。
面试题22:流操作符重载为什么返回引用?
- 在程序中,流操作符
>>和<<
经常连续使用。因此这两个操作符的返回值应该是一个仍旧支持这两个操作符的流引用。其他的数据类型都无法做到这一点。
注意:除了在赋值操作符和流操作符之外的其他的一些操作符中,如+、-、*、/等却千万不能返回引用。因为这四个操作符的对象都是右值,因此,它们必须构造一个对象作为返回值。
面试题23:简述指针常量与常量指针区别
- 指针常量是指定义了一个指针,这个指针的值只能在定义时初始化,其他地方不能改变。常量指针是指定义了一个指针,这个指针指向一个只读的对象,不能通过常量指针来改变这个对象的值。
- 指针常量强调的是指针的不可改变性,而常量指针强调的是指针对其所指对象的不可改变性。
注意:无论是指针常量还是常量指针,其最大的用途就是作为函数的形式参数,保证实参在被调用函数中的不可改变特性。
面试题24:如何避免“野指针”?
“野指针”产生原因及解决办法如下:
- 指针变量声明时没有被初始化。解决办法:指针声明时初始化,可以是具体的地址值,也可让它指向
NULL
。 - 指针
p
被free
或者delete
之后,没有置为NULL
。解决办法:指针指向的内存空间被释放后指针应该指向NULL
。 - 指针操作超越了变量的作用范围。解决办法:在变量的作用域结束前释放掉变量的地址空间并且让指针指向
NULL
。
注意:“野指针”的解决方法也是编程规范的基本原则,平时使用指针时一定要避免产生“野指针”,在使用指针前一定要检验指针的合法性。
面试题25:常引用有什么作用?
- 常引用的引入主要是为了避免使用变量的引用时,在不知情的情况下改变变量的值。常引用主要用于定义一个普通变量的只读属性的别名、作为函数的传入形参,避免实参在调用函数中被意外的改变。
说明:很多情况下,需要用常引用做形参,被引用对象等效于常对象,不能在函数中改变实参的值,这样的好处是有较高的易读性和较小的出错率。
面试题26:构造函数能否为虚函数?
- 构造函数不能是虚函数。而且不能在构造函数中调用虚函数,因为那样实际执行的是父类的对应函数,因为自己还没有构造好。析构函数可以是虚函数,而且,在一个复杂类结构中,这往往是必须的。
- 析构函数也可以是纯虚函数,但纯虚析构函数必须有定义体,因为析构函数的调用是在子类中隐含的。
说明:虚函数的动态绑定特性是实现重载的关键技术,动态绑定根据实际的调用情况查询相应类的虚函数表,调用相应的虚函数。
面试题27:关键字volatile有什么含意(举例说明)?
- 一个定义为 volatile 的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。
- 精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。
- 下面是 volatile 变量的几个例子:
- 并行设备的硬件寄存器(如:状态寄存器)
- 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
- 多线程应用中被几个任务共享的变量
面试题28:程序什么时候应该使用线程,什么时候单线程效率高?
- 耗时的操作使用线程,提高应用程序响应 。
- 并行操作时使用线程,如 C/S 架构的服务器端并发线程响应用户的请求。
- 多 CPU 系统中,使用线程提高 CPU 利用率
- 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独 立的运行部分,这样的程序会利于理解和修改。
- 其他情况都使用单线程。
面试题29:Linux有内核级线程吗?
- 线程通常被定义为一个进程中代码的不同执行路线。从实现方式上划分,线程有两种类型:“用户级线程”和“内核级线程”。
- 用户线程指不需要内核支持而在用户程序中实现的线程,其不依赖于操作系统核心,应用进程利用线程库提供创建、同步、调度和管理线程的函数来控制用户线程。这种线程甚至在象
DOS
这样的操作系统中也可实现 ,但线程的调度需要用户程序完成,这有些类似Windows 3.x
的协作式多任务。 - 另外一 种则需要内核的参与,由内核完成线程的调度。其依赖于操作系统核心,由内核的内部需求进行创建和撤销,这两种模型各有其好处和缺点。
- 用户线程不需要额外的内核开支,并且用户态线程的实现方式可以被定制或修改以适应特殊应用的要求,但是当一个线程因
I/O
而处于等待状态时,整个进程就会被调度程序切换为等待状态,其他线程得不到运行的机会; - 而内核线程则没有各个限制,有利于发挥多处理器的并发优势,但却占用了更多的系统开支。
Windows NT 和 OS/2
支持内核线程。Linux
支持内核级的多线程。
面试题30:C++中什么数据分配在栈或堆中,New分配数据是在近堆还是远堆中?
- 栈: 存放局部变量,函数调用参数,函数返回值,函数返回地址,由系统管理 。
- 堆: 程序运行时动态申请,new 和 malloc 申请的内存就在堆上。
面试题31:使用线程是如何防止出现大的波峰?
- 意思是如何防止同时产生大量的线程,方法是使用线程池,线程池具有可以同时提 高调度效率和限制资源使用的好处,线程池中的线程达到最大数时,其他线程就会排队等候。
面试题32:函数模板与类模板有什么区别?
- 函数模板的实例化是由编译程序在处理函数调用时自动完成的。
- 而类模板的实例化 必须由程序员在程序中显式地指定。
面试题33:动态连接库的两种方式?
调用一个 DLL
中的函数有两种方法:
- 载入时动态链接(
load-time dynamic linking
),模块非常明确调用某个导出函数 ,使得他们就像本地函数一样。这需要链接时链接那些函数所在 DLL 的导入库,导入库向 系统提供了载入 DLL 时所需的信息及 DLL 函数定位。 - 运行时动态链接(
run-time dynamic linking
),运行时可以通过LoadLibrary
或LoadLibraryEx
函数载入DLL
。DLL
载入后,模块可以通过调用GetProcAddress
获取DLL
函数的 出口地址,然后就可以通过返回的函数指针调用 DLL 函数了。如此即可避免导入库文件了。
面试题34:什么是平衡二叉树?
- 左右子树都是平衡二叉树 且左右子树的深度差值的绝对值不大于 1。
面试题35:冒泡排序算法的时间复杂度是什么?
- 时间复杂度是
O(n^2)。
面试题36:C和C++中的struct有什么不同?
C 和 C++
中struct
的主要区别是C
中的struct
不可以含有成员函数,而C++
中的struct
可以。C++
中struct
和class
的主要区别在于默认的存取权限不同,struct
默认为public
,而class
默认为private
。
面试题37:用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)
#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
1). #define 语法的基本知识(例如:不能以分号结束,括号的使用,等等)
2). 预处理器计算常数表达式的值
3). 意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号 L,告诉编译器,这个常数是的长整型数。
4). 如果你的表达式中用到 UL(表示无符号长整型),那么你有了一个好的起点。
面试题38:不能做switch()的参数类型是?
switch
的参数不能为实型。
面试题39:全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?
- 可以,在不同的
C
文件中以static
形式来声明同名全局变量。 - 可以在不同的
C
文件中声明同名的全局变量,前提是其中只能有一个C
文件中对此变量赋初值,此时连接不会出错。
面试题40:8086是多少位的系统?在数据总线上是怎么实现的?
- 16位系统,8 位数据传送,通过总线控制逻辑实现数据线和地址线的分时复
用。 - 8086 是 Inter 的 16 位微处理器有 16 根数据线和 20 根地址线,它既能处理 16 位数据,也能处理 8 位数据内部数据总线都是按 16 位设计的,单外部数据总线只有 8 条。
面试题41:局部变量能否和全局变量重名?
- 能,局部会屏蔽全局。要用全局变量,需要使用
":: "
- 局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义一个同名的局部变量,而那个局部变量的作用域就在那个循环体内。
面试题42:结构传递和返回是如何实现的 ?
- 当结构作为函数参数传递的时候 ,通常会把整个结构都推进栈,需要多少空间就使用多少空间。(正是为了避免这个代价,程序员经常使用指针而不是结构。)
- 某些编译器仅仅传递一个结构的指针 ,但是为了保证按值传递的语义 ,它们可能不得不保留一份局部副本 。
- 编译器通常会提供一个额外的 “隐藏” 参数,用于指向函数返回的结构。有些老式的编译器使用一个特殊的静态位置来返回结构。这会导致返回结构的函数不可再入 ,这是
ANSIC
所不允许的 。
面试题43:为什么sizeof返回的值大于结构大小的期望值 ,是不是尾部都有填充?
- 为了确保分配连续的结构数组时正确对齐,结构可能有这种尾部填充 〈也可能有内部填充〉。
- 即使结构不是数组的成员 ,尾部填充也会保持 ,以便
sizeof
能够总是返回一致的大小。
面试题44:枚举和一组预处理的 #define有什么不同 ?
- 只有很小的区别。C标准表明枚举为整型,枚举常量为int型,因此它们都可以和其他整型类别自由混用而不会出错。
- 但是,假如编译器不允许在未经显式类型转换的情况下混用这些类型,则审慎地使用枚举可以捕捉到某些程序错误。
- 枚举的一些优点:自动赋值;调试器在检验枚举变量时 ,可以显示符号值;它们服从数据块作用域规则 。(当枚举变量被任意地和其他类型混用时,编译器也可以产生非致命的警 告信息,因为这被认为是坏风格。〉
- 一个缺点是程序员不能控制这些非致命的警告,有些程序员则反感于无法控制枚举变量的大小。
面试题45:i++和++i有什么区别 ?
- 简而言之 :
++i
在i
存储的值上增加1并向使用它的表达式 “返回” 新的、增加后的值; - 而
i++
对i
增加1,但返回的是原来的、未增加的值。
面试题46:*p++自增p还是p所指向的变量?
- 后缀
++
和--
操作符本质上比前缀一元操作符的优先级高 ,因此*p++
和*(p++)
等价,它自增 p并返回p 自增之前所指向的值。要自增p指向的值,则使用(*p)++
,如果副作用的顺序无关紧要,也可以使用++*p
。
面试题47:通用指针类型是什么 ?当我把函数指针赋向void *类型的时候,编译通不过
- 没有什么“通用指针类型”。
void*
指针只能保存对象 〈 也就是数据) 指针 。将函数指针转换为void*
指针是不可移植的。(在某些机器上 ,函数指针可能很大一一比任何数据指针都大。〉 - 但是,可以确保的是,所有的函数指针类型都可以相互转换,只要在调用之前转回了正确的类型即可 。
- 因此,可以使用任何函数类型 〈通常是
int (*) ()
或void(*)()
,即未指明参数、返回int
或void
的函数〉 作为通用函数指针 。 - 如果你需要一个既能容纳对象指针又能容纳函数指针的地方 ,可移植的解决方案是使用包含
void*
指针和通用函数指针 (任何 类型都可以)的联合。
面试题48:什么是空指针?
- 语言定义中说明 ,每一种指针类型都有一个特殊值 即“空指针” 一一它与同类型的其他 所有指针值都不相同 ,它 “保证与任何对象或函数的指针值都不相等” 。
- 也就是说 ,空指针不会指向任何地方 ,它不是任何对象或函数的地址。
- 取地址操作符
&
永远也不会返回空指针 。 - 同样对
malloc
的成功调用也不会返回空指针 。(如果失败 ,malloc
的确返回空指针,这是空指针的典型用法:表示 “未分配” 或者 “尚未指向任何地方” 的 “特殊” 指 针值 。〉 - 空指针在概念上不同于未初始化的指针。空指针可以确保不指向任何对象或函数 ,而未初始化的指针则可能指向任何地方 。
- 如上文所述,每种指针类型都有一个空指针,而不同类型的空指针的内部表示可能不尽相同。尽管程序员不必知道内部值,但编译器必须时刻明确需要哪种空指针,以便在需要的时候加以区分。
面试题49:调用malloc() 的时候,错误 “不能把void*转换为int*"是什么意思?
- 说明用的是C++编译器而不是C编译器。
面试题50:调用malloc()为一个函数的局部指针分配内存时,还需要用free()显式地释放吗?
- 是的。指针和它所指向的东西是完全不同的 。
- 局部变量在函数返回时就会释放 ,但是在指针变量这个问题上 ,这表示指针被释放,而不是它所指向的对象 。
- 用
malloc()
分配的内 存在你显式释放它之前 都会保留在那里 。一般地 ,每一个malloc()
都必须有个对应的free()
调用。
面试题51:sizeof操作符可以用在#if预处理指令中吗?
- 不行。预处理在编译过程之前进行,此时尚未对类型名称进行分析。作为替代,可以考虑使用
ANSI
的<limits .h>
中定义的常量,或者使用 “配置”(configure
) 脚本。更好的办法是编写与类型大小无关的代码。
面试题52:main()的第3个参数envp是怎么回事?
- 这是一个 (尽管很常见但却) 不标准的扩展。如果真的需要用标准的
getenv ( )
函数提供的方法之外的办法访问环境变量 ,可能使用全局变量environ
会更好 (尽管它也同样并不标准) 。
面试题53:为什么不能对void*指针进行算术运算?
- 编译器不知道所指对象的大小。(指针的算术运算总是基于所指对象的大小的。)
- 因此不允许对
void *
指针进行算术运算 (尽管有些编译器作为扩展允许这种运算)。 - 在作运算之前 ,可以把指针转化为
char*
型或准备操作的其他指针类型。
以上是关于常见嵌入式/C/C++面试题100+集合(含参考答案)-更新中的主要内容,如果未能解决你的问题,请参考以下文章