必背八股文-C/C++(4)
头文件的两种包含方式的区别
- 使用<>包含头文件名时,编译器会在系统默认的路径下寻找头文件。这些路径由编译器的环境变量所指定,通常包括标准库文件、系统头文件和其他系统支持的库。
- 使用""包含头文件名时,编译器会先在当前源代码文件所在的目录下查找头文件,如果找不到,再去系统默认路径下查找。通常,使用""包含头文件时,头文件是自己定义的或者是程序所在的项目中的头文件。
简述gcc编译过程
1.预处理(Preprocessing):预处理阶段主要处理源代码中的宏定义、条件编译和包含文件。预处理器会替换宏定义、处理条件编译指令(如、等)并将包含的头文件插入到源代码中。预处理后的输出是一个扩展后的源代码文件,通常具有(C语言)或(C++)扩展名。
2.编译(Compilation):编译阶段将预处理后的源代码文件转换为汇编代码。编译器会检查源代码的语法、语义是否正确,进行各种优化,并将源代码转换为目标平台的汇编指令。编译后的输出是一个汇编代码文件,通常具有扩展名。
3.汇编(Assembly):汇编阶段将汇编代码文件转换为目标代码(机器码)。汇编器会将汇编指令翻译为目标平台的机器指令,并生成一个目标文件。目标文件包含了程序的机器码、符号信息和其他元数据。汇编后的输出是一个目标文件,通常具有(Linux)或(Windows)扩展名。
4.链接(Linking):链接阶段将一个或多个目标文件和库文件组合成一个可执行文件或库文件。链接器会解析符号引用,将它们与正确的地址和实现关联,并处理程序中使用的库函数。链接器还负责组织程序的内存布局,如代码段、数据段等。链接后的输出是一个可执行文件,通常具有无扩展名(Linux)或(Windows)扩展名。
程序在执行int main(int argc, char *argv[])时的内存结构,你了解吗?
当程序执行int main(int argc, char *argv[])时,操作系统会为其分配一块内存区域作为进程的虚拟地址空间,该虚拟地址空间由下面几个部分组成:
• 代码段(text segment):存放程序的机器代码。
• 数据区:
初始化的全局变量和静态变量:存放程序中已经初始化的全局变量和静态变量。
未初始化的全局变量和静态变量:存放程序中未初始化的全局变量和静态变量。这部分区域在程序启动时通常会被清零。
• 堆(heap):动态申请的内存。
• 栈(stack):程序运行时的临时数据区。
其中,main函数的参数argc和argv存放在栈中,全局变量和静态变量存放在数据段中,而动态申请的内存存放在堆中。
main函数的返回值有什么值得考究之处吗?
main函数的返回值是整型,通常用来表示程序的执行状态,返回值为0表示程序正常退出,非0值表示程序异常退出或出现错误。在一些特殊的应用场合,main函数的返回值也可以被用来传递额外的信息,例如传递程序执行的时间、错误类型等信息。
形参和实参的区别
形参和实参都是函数中的变量,但是它们的含义和作用不同。形参是在函数定义时声明的变量,用来接收调用函数时传递的实参。而实参是在函数调用时传递给函数的值或变量。
指针和引用的区别,传引用和传指针区别
指针和引用的区别:
1.内存地址:指针是一个变量,存储的是指向内存地址的值。而引用是一个别名,本质上是已经存在的内存地址的另一个名称。
2.可空性:指针可以为空,即指向空地址,表示没有指向任何对象。而引用必须总是指向一个有效的对象,不能为NULL。
3.操作方式:指针需要用“*”运算符进行解引用操作,才能访问指向的内存地址中的值。而引用可以直接操作它所指向的对象。
传递参数时可以通过传值、传指针和传引用三种方式。传值方式是将实际参数的值拷贝到形式参数中进行处理,传指针和传引用方式则是将实际参数的内存地址传递给形式参数进行处理。它们之间的区别如下:
1.内存开销:传值方式会在内存中创建临时变量,需要占用额外的内存空间,而传指针和传引用方式则不需要创建临时变量,节省了内存开销。
2.对实参的影响:传值方式不会影响实际参数的值,而传指针和传引用方式会直接修改实际参数的值。
3.代码可读性:传值方式代码可读性较好,传指针和传引用方式代码可读性较差,需要使用“*”和“&”等符号进行操作。
野指针出现情况、怎么解决
野指针是指向无效内存地址或已释放内存区域的指针。野指针的出现可能导致程序崩溃、数据损坏或不可预测的行为。以下是一些常见的野指针产生情况:
1. 未初始化的指针:声明指针变量时未对其进行初始化,导致指针指向一个随机的内存地址。
int *ptr = NULL;
int *ptr = (int *) malloc(sizeof(int));
2. 已释放的内存:指针指向的内存区域已经被释放,但没有将指针置为NULL,仍然尝试访问该内存。
free(ptr);
ptr = NULL;
3. 指针越界:指针在数组或内存块中越过边界,访问了不属于它的内存区域。
if (ptr != NULL) {
// 使用指针
}
4. 内存泄漏:连续分配内存而未释放,导致原来的指针丢失,并且无法释放已分配的内存。
要解决野指针问题,可以采取以下措施:
1. 初始化指针:声明指针变量时,确保对其进行初始化。可以将指针初始化为NULL,或者分配内存并将地址赋给指针。
2. 释放内存后置空:当释放指针指向的内存后,应将指针设置为NULL,以避免悬空指针。
3. 使用指针前进行检查:在使用指针前,检查其是否为NULL,避免访问无效内存地址。
4. 避免指针越界:在使用指针访问数组或内存块时,确保不会越过边界。可以使用下标或指针算术进行访问,但要确保不超过分配的内存范围。
5. 及时释放内存:确保不再需要的内存被正确释放,以避免内存泄漏。
malloc与free的实现原理?
malloc和free是C语言中动态内存分配和释放函数,它们的实现原理可以概括为以下几个步骤:
1. 空闲链表:维护一个链表,存储已经被释放的内存块,以及它们的大小信息。
2. 内存分配:当需要申请一块内存时,malloc会遍历空闲链表,找到第一个大于等于所需内存的空闲块。如果该块大小超出所需内存,则将该块分裂成两部分,一部分返回给用户,另一部分仍然放回空闲链表中。
3. 内存释放:当用户释放一块内存时,free会将该块内存添加到空闲链表中。如果释放的内存与其他空闲块相邻,则会将相邻的空闲块合并成一个更大的空闲块。
4. 内存对齐:由于硬件对内存访问的要求,malloc和free函数对内存块的大小和地址都需要进行特殊处理,以保证内存访问的正确性和高效性。
5. new和delete的实现原理, delete是如何知道释放内存的大小的?
new和delete的实现原理, delete是如何知道释放内存的大小的?
new和delete是C++语言中动态内存分配和释放操作符,它们的实现原理可以概括为以下几个步骤:
1. new的实现原理:当使用new操作符申请内存时,C++运行时会调用operator new函数进行内存分配。在分配内存时,operator new会调用malloc函数来完成内存分配,并返回分配的内存地址。
2. delete的实现原理:当使用delete操作符释放内存时,C++运行时会调用operator delete函数进行内存释放。在释放内存时,operator delete会调用free函数来完成内存释放,并将该内存块标记为已释放状态。
3. delete如何知道释放内存的大小:在C++中,每个动态分配的对象都会存储一个额外的信息,用来标记该对象所占用的内存大小。在delete操作符释放内存时,C++运行时会读取这个信息,并传递给operator delete函数,以便准确地释放内存块。如果在使用new操作符时没有指定所分配的内存大小,C++运行时会根据对象的类型来自动计算所需要的内存大小。
malloc申请的存储空间能用delete释放吗?
malloc申请的存储空间不能用delete释放,应该使用free进行释放。同样,new申请的存储空间也不能使用free进行释放,应该使用delete进行释放。
既然有了malloc/free,C++中为什么还需要new/delete呢?
new/delete是C++中的内存分配和释放运算符,可以动态地分配和释放内存。与malloc/free相比,new/delete在使用上更方便、更安全、更易于维护,也更符合面向对象编程的理念。另外,C++中的new运算符可以自动调用对象的构造函数进行初始化,而delete运算符可以自动调用对象的析构函数进行清理。
C++命名空间namespace的作用
namespace是C++中的一种机制,用于防止命名冲突和代码模块化。通过将变量、函数、类等定义在命名空间中,可以有效地避免不同代码模块之间的命名冲突,同时也便于代码的维护和管理。例如,可以将所有与文件操作相关的函数和变量定义在一个命名空间中,以避免与其他模块中的同名函数和变量产生冲突。可以使用namespace关键字定义命名空间,使用using namespace或using关键字引用命名空间中的符号。
大小端基础
大小端是指在多字节的数据类型(例如整型、浮点型等)在内存中的存储方式。大端指高位字节存储在低地址,低位字节存储在高地址;小端则相反,低位字节存储在低地址,高位字节存储在高地址。
大小端各自的优点是什么?
大端和小端的优点主要在于不同的架构和应用场景。大端在网络通信中比较常用,因为网络传输时一般是按照大端字节序进行传输的。而小端在嵌入式领域比较常用,因为小端字节序与大部分处理器的寄存器和存储器排列方式一致,访问时效率更高。
如何用代码判断大小端存储?
#include <stdio.h> int main() { unsigned int x = 0x12345678; char *c = (char*) &x; if (*c == 0x78) printf
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
本专栏主要是介绍嵌入式软件开发岗位的相关知识和学习攻略,为大家提供一份笔试与面试手册。包括有嵌入式软件开发岗位介绍与学习攻略;校园招聘和offer疑惑问题的介绍;在笔试方面,如何刷题为笔试作准备,提供往年笔试真题;在面试方面,提供相关知识的复习重点,提供面试真题。包括有:华为、蔚来、文远、大疆、三一、深信服、亚马逊、Intel、百度、科大讯飞、OPPO、京东、中兴、比特大陆|算能、美团等等