目录
- 2.1 C 和 C++ 的区别、概念相关面试题
- 2.1.1 **new 和 malloc 的区别** ⭐⭐⭐⭐⭐
- 2.1.2 **malloc 的底层实现** ⭐⭐⭐⭐
- 2.1.3 **在 1G 内存的计算机中能否 malloc(1.2G)?为什么?** ⭐⭐
- 2.1.4 **指针与引用的相同和区别;如何相互转换?** ⭐⭐⭐⭐⭐
- 2.1.5 **C 语言检索内存情况,内存分配的方式** ⭐⭐⭐
- 2.1.6 **`extern "C"` 的作用** ⭐⭐⭐
- 2.1.7 **头文件声明时加 extern 定义时不要加 因为 extern 可以多次声明,但只有一个定义** ⭐⭐⭐⭐
- 2.1.8 **函数参数压栈顺序,即关于 `__stdcall` 和 `__cdecl` 调用方式的理解** ⭐⭐⭐
- 2.1.9 **重写 `memcpy()` 函数需要注意哪些问题** ⭐⭐
- 2.1.10 **数组到底存放在哪里** ⭐⭐⭐
- 2.1.11 **`struct` 和 `class` 的区别** ⭐⭐⭐⭐⭐
- 2.1.12 **`char` 和 `int` 之间的转换** ⭐⭐⭐
- 2.1.13 **`static` 的用法(定义和用途)** ⭐⭐⭐⭐⭐
- 2.1.14 **`const` 常量和 `#define` 的区别(编译阶段、安全性、内存占用等)** ⭐⭐⭐⭐
- 2.1.16 **`volatile` 的作用和用法** ⭐⭐⭐⭐⭐
- 2.1.17 **有常量指针、指针常量、常量引用,没有引用常量** ⭐⭐⭐
- 2.1.18 **没有指向引用的指针,因为引用是没有地址的,但有指针的引用** ⭐⭐⭐
- 2.1.19 **C/C++ 中变量的作用域** ⭐⭐⭐⭐⭐
- 2.1.20 **C++ 中类型转换机制?各适用什么环境?`dynamic_cast` 转换失败时会出现什么情况?** ⭐⭐⭐
2.1 C 和 C++ 的区别、概念相关面试题
2.1.1 new 和 malloc 的区别 ⭐⭐⭐⭐⭐
- new 是 C++ 的运算符,malloc 是 C 语言的库函数。
- new:
- 会调用构造函数来初始化对象,并返回具体的对象指针。
- 返回对象的实际类型,不需要进行强制类型转换。
- 内存分配失败时会抛出异常
std::bad_alloc
。
- malloc:
- 仅仅分配内存,不调用构造函数进行初始化。
- 需要强制转换返回的
void*
类型指针。 - 内存分配失败时返回
NULL
。 malloc
只分配内存,free 用于释放。new
配合 delete 使用,自动调用析构函数。
2.1.2 malloc 的底层实现 ⭐⭐⭐⭐
malloc
的底层实现主要依赖于操作系统提供的 brk() 或 mmap() 系统调用。它们可以向内核申请动态内存。内存通常来自堆区,通过链表或分块的方式进行管理。具体实现通常会使用 glibc 中的 ptmalloc 或其他类似算法。它们通过分配内存块和合并空闲内存来提高效率。
2.1.3 在 1G 内存的计算机中能否 malloc(1.2G)?为什么? ⭐⭐
不一定能成功。因为 malloc 分配的是虚拟内存,并不要求物理内存立即足够,但这也依赖于操作系统的虚拟内存管理和交换空间(swap)。操作系统可以使用虚拟内存技术分配超过物理内存大小的空间,但如果超过了系统可用虚拟内存的范围,malloc 就会失败,返回 NULL
。
2.1.4 指针与引用的相同和区别;如何相互转换? ⭐⭐⭐⭐⭐
-
相同点:
- 都可以用于操作其他变量或对象。
-
区别:
- 指针:
- 可以改变所指向的对象。
- 可以是
NULL
,不一定指向有效对象。 - 使用
*
访问指向的对象,使用&
获取变量地址。
- 引用:
- 必须在声明时初始化,不可以改变所引用的对象。
- 不能是
NULL
。 - 更像是别名,不需要使用特殊符号直接访问。
- 指针:
-
转换:
- 可以通过
&
运算符将引用的地址转为指针。 - 可以通过
*
运算符将指针转换为引用指向的对象。
- 可以通过
2.1.5 C 语言检索内存情况,内存分配的方式 ⭐⭐⭐
C 语言中的内存分配可以通过以下方式:
- 静态分配:在编译时分配固定大小的内存,如全局变量、静态局部变量。
- 动态分配:在运行时分配内存,如
malloc
、calloc
、realloc
。
2.1.6 extern "C"
的作用 ⭐⭐⭐
extern "C"
是用来告知编译器该块代码或函数遵循 C 语言的链接规范,而不是 C++ 的。C++ 使用的是名字修饰(name mangling),而 C 语言不使用。通过 extern "C"
可以让 C++ 和 C 函数互相调用。
2.1.7 头文件声明时加 extern 定义时不要加 因为 extern 可以多次声明,但只有一个定义 ⭐⭐⭐⭐
extern
关键字通常用于声明变量或函数,而不是定义。它告诉编译器该变量或函数的定义在其他地方,因此同一个变量或函数可以在多个文件中声明但只需要定义一次。
2.1.8 函数参数压栈顺序,即关于 __stdcall
和 __cdecl
调用方式的理解 ⭐⭐⭐
__cdecl
:函数的参数由调用者清理,参数从右到左入栈,这是 C/C++ 的默认调用约定。__stdcall
:函数的参数由被调用者清理,参数也是从右到左入栈。
2.1.9 重写 memcpy()
函数需要注意哪些问题 ⭐⭐
- 避免内存重叠,应该考虑内存区域的重叠问题,可以通过检查源地址和目标地址来解决。
- 考虑指针的对齐问题,避免未对齐访问导致性能问题或崩溃。
- 需要正确处理不同数据类型的内存拷贝,如字节、整数、浮点数等。
2.1.10 数组到底存放在哪里 ⭐⭐⭐
数组在内存中的存放位置取决于数组的类型:
- 局部数组:存放在栈中。
- 全局数组或静态数组:存放在数据段(静态内存区)。
- 动态分配的数组:存放在堆中。
2.1.11 struct
和 class
的区别 ⭐⭐⭐⭐⭐
-
区别:
- struct:默认成员的访问权限是公有(
public
)。 - class:默认成员的访问权限是私有(
private
)。
- struct:默认成员的访问权限是公有(
-
相同点:
- 两者都可以包含数据和函数成员。
2.1.12 char
和 int
之间的转换 ⭐⭐⭐
在 C/C++ 中,char
和 int
之间的转换通常是隐式的。char
占 1 个字节,而 int
占多个字节。当将 char
转换为 int
时,字符的 ASCII 值被提升为 int
,反之亦然。
2.1.13 static
的用法(定义和用途) ⭐⭐⭐⭐⭐
-
static
变量:- 在函数内声明时,函数调用结束后变量的值不会丢失。
- 在全局作用域内,
static
变量的作用域只限于当前文件。
-
static
函数:- 限定函数的作用范围,使其只能在声明它的文件中可见。
2.1.14 const
常量和 #define
的区别(编译阶段、安全性、内存占用等) ⭐⭐⭐⭐
const
:有类型检查,属于编译时常量,存储在内存中。#define
:没有类型检查,属于预处理指令,不占用内存,只是简单的文本替换。
2.1.16 volatile
的作用和用法 ⭐⭐⭐⭐⭐
volatile
告诉编译器不要对该变量进行优化,强制每次访问都从内存中读取值,避免因为编译器优化导致的错误。常用于硬件寄存器或多线程程序中被其他线程修改的变量。
2.1.17 有常量指针、指针常量、常量引用,没有引用常量 ⭐⭐⭐
- 常量指针:指针指向的内容不能更改。
- 指针常量:指针本身不能修改,但指向的内容可以修改。
- 常量引用:引用的值不能被修改。
- 没有引用常量的概念,引用是别名,指向对象的常量属性。
2.1.18 没有指向引用的指针,因为引用是没有地址的,但有指针的引用 ⭐⭐⭐
引用在底层实现上是指向对象的指针,但 C++ 语法上不允许存在指向引用的指针。不过,可以有“指针的引用”,即引用指向一个指针。
2.1.19 C/C++ 中变量的作用域 ⭐⭐⭐⭐⭐
- 局部变量:在函数或代码块中声明,作用范围是函数或块的内部。
- 全局变量:在函数外部声明,作用范围是整个文件和其他引用的文件。
- 静态局部变量:在函数中声明的静态变量,生命周期贯穿整个程序执行过程,但作用域依然是该函数。
- 静态全局变量:只能在声明的文件中访问。
2.1.20 C++ 中类型转换机制?各适用什么环境?dynamic_cast
转换失败时会出现什么情况? ⭐⭐⭐
C++ 提供了四种类型转换方式:
static_cast
:用于
基础数据类型之间的转换和父类子类之间的指针/引用转换。
dynamic_cast
:用于多态类型之间的安全转换。如果转换失败,返回NULL
指针(对于指针)或抛出bad_cast
异常(对于引用)。const_cast
:用于添加或移除对象的const
限定符。reinterpret_cast
:用于不同类型指针之间的转换,不安全的强制转换。