前言
本篇文章继续带大家来刷题,秋招也快到了大家坚持刷题,相信大家都可以找到好工作。
一、SDK是什么
SDK是软件开发工具包(Software Development Kit)的缩写,它是一组用于开发软件应用程序的工具、库和文档的集合。SDK通常由软件开发平台或框架提供,旨在帮助开发人员更轻松、高效地创建应用程序。
SDK通常包含以下主要组件:
1.API(Application Programming Interface):SDK会提供一组API,即一组定义了如何与某个特定平台、操作系统或服务进行交互的编程接口。
2.工具和实用程序:SDK通常提供用于开发、构建、调试和测试应用程序的工具和实用程序,例如编译器、调试器、模拟器、测试框架等。
3.示例代码和示例应用程序:SDK通常提供一些示例代码和示例应用程序,以帮助开发人员理解和使用API和工具。
4.文档和教程:SDK通常提供详细的文档和教程,包括API文档、编程指南、开发者手册等,以帮助开发人员了解如何使用SDK中的组件和工具。
二、内存分配最小单位
当涉及内存分配时,有两个相关的概念需要区分:内存分配单位和内存分配函数。
1.内存分配单位:内存分配单位是指内存管理的最小单位。在操作系统中,内存通常以页面(Page)为最小分配单位。页面的大小通常是4KB或者更大,具体取决于操作系统和硬件。
2.内存分配函数:内存分配函数是编程语言提供的用于动态分配内存的函数。其中,malloc函数是C语言和C++语言中的一个常用内存分配函数。malloc函数通过请求操作系统为程序分配指定大小的连续字节的内存块。
区别:内存分配单位与内存分配函数之间的区别在于:
1.内存分配单位:指操作系统和硬件层面上内存管理的最小单位,通常是页面大小,如4KB。操作系统会根据页面的大小进行内存管理和分配,以支持虚拟内存等机制。
2.内存分配函数:是编程语言提供的接口,用于在程序运行时请求操作系统分配一定大小的内存。其中,malloc函数是一种常见的内存分配函数,它按字节为单位动态分配内存块。
3.内存分配函数(如malloc)使用字节作为分配单位,但在操作系统层面,内存管理以页面为最小分配单位。这是由于操作系统需要管理和映射页面,而应用程序通常只需要通过内存分配函数来分配适当大小的内存块,以满足其数据结构或算法的需求。
三、内联函数和宏函数的区别
内联函数和宏函数是两种在编程中实现函数展开的方式,它们有一些区别,包括以下几个方面:
1.语法和类型安全性:
内联函数是C++语言提供的一种函数声明方式,使用关键字inline进行声明。内联函数具有函数的语法结构,支持参数的类型检查和类型安全。内联函数可以像普通函数一样进行重载,可以包含复杂的语句和控制流程。
宏函数则是预处理器进行文本替换的方式,使用宏定义声明。宏函数是简单的文本替换,没有函数的语法结构,不进行类型检查。宏函数展开时只是进行简单的文本替换,可能会导致意外的副作用。
2.展开方式和底层实现:
内联函数在编译期间进行函数展开,将函数的实际代码嵌入到调用处,避免了函数调用的开销。内联函数的展开是由编译器控制的,并且可以对其进行内联优化。
宏函数在预处理阶段进行文本替换,将宏的实际文本替换到调用处。宏函数没有函数调用的开销,但可能会产生较大的代码体积。
3.命名空间和作用域:
内联函数位于命名空间中,具有自己的作用域。可以通过静态链接在多个源文件之间共享。内联函数也可以在类中定义,对于类的成员函数,可以在类声明中直接定义内联函数。
宏函数没有自己的作用域和命名空间,它是简单的文本替换。宏定义的作用范围是全局的,可能会导致命名冲突或不可预期的副作用。
4.调试和错误信息:
内联函数在调试过程中可以单步执行和观察,可以提供更好的调试信息和错误提示,因为内联函数是编译器处理的一部分。
宏函数无法单步执行和观察,可能会导致调试和错误定位困难,因为宏函数只是简单的文本替换。
总的来说,内联函数比宏函数更安全、更灵活,可以进行类型检查,有自己的作用域和命名空间,提供更好的调试和错误信息。而宏函数则更接近于简单的文本替换,没有类型检查和作用域的概念,可能会产生不可预期的副作用。因此,推荐在C++中使用内联函数,除非特殊情况下需要使用宏函数的特性和灵活性。
四、空指针和野指针的区别
1.空指针(Null Pointer):
空指针表示指针不指向任何有效的对象或函数。在C和C++中,空指针通常使用特殊的值0来表示,也可以使用宏定义NULL或C++11引入的nullptr关键字。
空指针指向的是一个已知的特殊地址,该地址被保留给表示空指针的目的。对空指针进行解引用操作(如访问指针指向的内存)将产生未定义的行为。
2.野指针(Dangling Pointer):
野指针是指指针变量持有一个无效的、未定义的内存地址。野指针可能是一个已被释放的内存地址,或者指向还未分配给有效对象的内存区域。
野指针是一种危险的编程错误,使用野指针可能导致程序崩溃、内存错误和不可预测的行为。
五、访问野指针和空指针会发生什么
访问野指针:
可能导致程序崩溃或异常终止。
可能读取到不可预测或无效的数据,导致意外结果。
可能引发安全漏洞,如信息泄露或缓冲区溢出。
访问空指针:
可能导致段错误(Segmentation Fault)或空指针异常,终止程序。
可能出现未定义的行为,造成程序行为无法预测。
六、C++面向对象的三要素
1.封装(Encapsulation):
封装是一种将数据和操作捆绑在一起的机制,以创建一个类(Class)。类将数据(成员变量)和操作(成员函数)组合在一起,可以隐藏实现的细节,只暴露必要的接口给外部使用。
封装通过访问控制(public、private、protected)提供了对数据的保护,使得数据只能通过类的接口进行访问和修改。
封装可以实现信息隐藏和数据隔离,提高代码的可维护性、灵活性和安全性。
2.继承(Inheritance):
继承是一种机制,允许一个类(称为子类或派生类)基于另一个类(称为父类或基类)建立,并且可以继承父类的属性和行为。
通过继承,子类可以重用父类的代码,避免代码的重复编写,并且可以扩展或修改父类的行为。
继承实现了类之间的关系,如“is-a”关系,其中子类可以视为父类的一种类型。
3.多态(Polymorphism):
多态是指能够使用基类(父类)的指针或引用来引用派生类(子类)对象,并根据上下文自动选择正确的方法执行。
多态允许使用相同的接口处理不同的对象,通过重写(覆盖)基类的虚函数实现特定类的行为。
多态提高了代码的可扩展性和灵活性,使得程序可以根据不同的对象类型采取适当的动作。
七、if(0 == x) 和 if(x == 0)
一般来说很多人习惯使用if(x == 0),但是也有一些人是使用if(0 == x)的,使用if(0 == x)这种写法能够保证 == 不被误写为 = 导致出现错误。
八、二维数组地址是否连续
一个二维数组的地址在内存中是连续的。二维数组在内存中以行主序(row-major order)连续存储其元素。
int arr[3][4];
在内存中,它会以如下的方式连续存储:
arr[0][0] arr[0][1] arr[0][2] arr[0][3] arr[1][0] arr[1][1] arr[1][2] arr[1][3] arr[2][0] arr[2][1] arr[2][2] arr[2][3]
因此,二维数组的地址是连续的,可以通过指针算术运算来访问数组中的元素。
例如,可以使用指针来访问二维数组中的元素:
int arr[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}; int *ptr = &arr[0][0]; for (int i = 0; i < 12; i++) { printf("%d ", *(ptr + i)); }
以上代码将打印出连续存储的二维数组的元素值:
1 2 3 4 5 6 7 8 9 10 11 12
所以,对于连续分配的二维数组,其地址是连续的,可以通过指针算术运算来访问和操作二维数组的元素。
总结
本篇文章就讲解到这里。