海康威视 软件开发- C++ 一面
1、自我介绍
2、为什么实习只有两个月
3、struct 和 class 的区别
答案:C++ 里 struct 和 class 本质上都可以定义成员变量、成员函数、构造析构、继承和模板,能力上没有本质差别。最核心的区别主要有两个:
struct默认访问权限是publicclass默认访问权限是private
还有一个区别是默认继承方式:
struct默认是public继承class默认是private继承
所以很多时候它们的差别更偏风格。如果是简单的数据聚合类型,很多人习惯用 struct;如果更强调封装和对象语义,通常会用 class。
代码:
struct A {
int x; // 默认 public
};
class B {
int x; // 默认 private
};
4、malloc / new / free / delete 的区别是什么
答案:malloc 和 free 是 C 里的内存管理函数,new 和 delete 是 C++ 里的运算符。它们最关键的区别不只是“一个分配内存,一个释放内存”,而是对象语义不同。
malloc 只是按字节申请一块原始内存,不会调用构造函数,返回 void*;free 只是释放这块内存,不会调用析构函数。
new 在分配内存之后,还会调用构造函数完成对象初始化;delete 在释放内存之前,会先调用析构函数清理对象资源。
另外还有几个常见区别:
malloc失败返回nullptrnew默认失败会抛出std::bad_allocnew[]/delete[]要成对使用malloc/free和new/delete不能混用
代码:
#include <cstdlib>
#include <iostream>
using namespace std;
class Test {
public:
Test() { cout << "construct\n"; }
~Test() { cout << "destruct\n"; }
};
int main() {
Test* p1 = new Test;
delete p1;
Test* p2 = (Test*)malloc(sizeof(Test));
free(p2);
}
5、开辟内存的实际大小和要求开辟的大小不一样,怎么去看
答案:这个问题本质上是在问:你申请了 1 字节、3 字节、17 字节,为什么实际分配往往不是这么精确。原因主要有两个:
- 分配器本身会按固定粒度或对齐规则管理内存
- 还要额外存管理信息,比如块头、链表指针、元数据等
所以用户申请大小和底层实际分配大小通常不一样。如果想观察这个现象,在 Linux 下常见有几种办法:
- 用
malloc_usable_size()看 glibc 实际给你的可用大小 - 用
gdb配合堆分析 - 用
mallinfo/mallinfo2 - 用 jemalloc / tcmalloc 的工具接口看分配信息
代码:
#include <malloc.h>
#include <iostream>
using namespace std;
int main() {
void* p = malloc(13);
cout << "usable size = " << malloc_usable_size(p) << endl;
free(p);
}
6、怎么看栈的内存空间大小
答案:栈空间大小分几种情况。如果是进程级别看栈上限,在 Linux 下可以直接用 ulimit -s 查看当前 shell 启动进程的栈大小限制。如果想在程序里看,可以用 getrlimit(RLIMIT_STACK, ...)。如果是线程栈,尤其是 pthread 创建的线程,还可以通过线程属性查看和设置线程栈大小。
代码:
#include <sys/resource.h>
#include <iostream>
using namespace std;
int main() {
rlimit rl;
getrlimit(RLIMIT_STACK, &rl);
cout << "stack size: " << rl.rlim_cur << endl;
return 0;
}
7、C 语言底层内存分配是怎么样的
答案:从进程视角看,内存大致会分成代码段、全局/静态区、堆、栈、共享库映射区这些区域。C 语言里动态分配主要说的是堆区,也就是 malloc/calloc/realloc/free 这一套。
底层上,分配器并不是每次申请一点内存就立刻向内核要一点,它通常会维护自己的堆管理结构。早期常见方式是通过 brk/sbrk 扩展堆顶;大块内存很多实现会直接走 mmap。分配器内部还会维护空闲链表、块头信息、对齐规则、分裂合并逻辑,所以你看到的 malloc 很简单,底下实际做的事情并不少。
如果面试官继续追问,可以顺着说:
- 小块分配如何避免碎片
- 大块为什么直接走
mmap calloc为什么能清零realloc为什么有时候原地扩,有时候搬迁
8、内存泄漏用什么工具,怎么发现
答案:内存泄漏排查常见工具主要有:
valgrind --leak-check=full- AddressSanitizer,简称 ASan
- LeakSanitizer,简称 LSan
- 一些线上场景会配合
gdb、pmap、smem、heap profiler
如果是开发阶段,我一般更倾向于 ASan/LSan,因为开销相对更可控,集成也方便;如果是比较细致地查泄漏路径,valgrind 也很直接。
判断有没有泄漏通常不是只看“程序内存大”,而是结合几个现象:
- 进程 RSS 持续增长且不回落
- 某类对象创建次数和释放次数不匹配
- 压测时间越长内存越高
- 工具直接报未释放块和调用栈
代码:
g++ -fsanitize=address -g test.cpp -o test ./test
或者:
valgrind --leak-check=full ./test
9、类的内存对齐规则
答案:类的内存对齐,本质上和结构体对齐规则一致。通常要记住这几个原则:
- 每个成员的起始地址要满足该成员对齐要求
- 整个类的大小要是“最大对齐数”的整数倍
- 编译器可能会在成员之间插入填充字节
- 基类子对象、虚函数指针这些也会影响整体布局
比如:
代码:
#include <iostream>
using namespace std;
struct A {
char c;
int i;
};
struct B {
char c1;
double d;
char c2;
};
int main() {
cout << sizeof(A) << endl; // 一般是 8
cout << sizeof(B) << endl; // 常见是 24
}
A 里 char 后面通常会补齐到 4 字节边界;B 里因为有 double,整体对齐要求会更高,所以尾部也可能补齐。
10、类可以手动定义对齐数吗,怎
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
本专栏系统梳理C++方向, 大中厂高频高频面试考点 , 内容皆来自真实面试经历,从基础语法、内存管理、STL与设计模式,到操作系统与项目实战,结合真实面试题深度解析,帮助开发者高效查漏补缺,提升技术理解与面试通过率,打造扎实的C++工程能力.
查看8道真题和解析