C/C++ 高频八股文面试题1000题(一)
原作者:Linux教程,原文地址:C/C++ 高频八股文面试题1000题(一)
在准备技术岗位的求职过程中,C/C++始终是绕不开的核心考察点。无论是互联网大厂的笔试面试,还是嵌入式、后台开发、系统编程等方向的岗位,C/C++ 都扮演着举足轻重的角色。
本系列文章将围绕 “C/C++ 笔试面试中出现频率最高的 1000 道题目” 进行深入剖析与讲解。由于篇幅限制,整个系列将分为 多 篇陆续发布,每篇50道题,本文为 第一篇。
通过系统梳理这些高频考点,帮助你在面对各大厂笔试、技术面试时真正做到 心中有数、下笔有神、对答如流!
建议学习方式:
- 结合实际项目或刷题平台反复巩固
- 对照每道题目的解析查漏补缺
- 动手编写示例代码加深理解
本系列将持续更新,欢迎关注+收藏,一起攻克这 1000 道 C/C++ 高频题,拿下心仪 Offer!
面试题1:变量的声明和定义有什么区别?
✅ 简要回答:
- 定义(Definition):为变量分配存储空间和地址,是创建变量的实际过程。一个变量只能被定义一次。
- 声明(Declaration):用于告诉编译器该变量已经存在,不会分配存储空间。变量可以多次声明。
关键区别:
类型 | 是否分配内存 | 是否可重复 |
定义 | 是 | 否 |
声明 | 否 | 是 |
extern int a; // 声明,不分配内存空间
int a; // 定义,分配内存空间
int a = 10; // 定义并初始化
面试题2:如何用if语句正确判断bool、int、float和指针变量是否为零值?
bool型数据: if(flag) { A; } else { B;}
int型数据: if(0==flag) { A; } else { B; }
指针变量: if(NULL==flag) { A; } else { B; }
float型数据: #define NORM (0.000001) if((flag>=-NORM) && (flag<=NORM)) { A; } else { B; }
面试题3:sizeof 和 strlen 的主要区别是什么?
- sizeof 是一个操作符,而 strlen 是 C 标准库中定义的一个函数。
- sizeof 的操作对象既可以是数据类型,也可以是变量;而 strlen 只能接受以 \0 结尾的字符串作为输入参数。
- sizeof 在编译阶段就会被计算出结果,而 strlen 必须等到程序运行时才能得出结果。此外,sizeof 计算的是数据类型或变量所占内存的大小,而 strlen 测量的是字符串实际字符的数量(不包括结尾的 \0)。
- 当数组作为 sizeof 的参数时,它表示整个数组的大小,不会退化为指针;而当数组传递给 strlen 时,则会退化为指向数组首地址的指针。
注意: 有些操作符(如 sizeof)看起来像函数,而有些函数名又类似操作符,这类名称容易引起混淆,使用时需要特别注意区分。尤其是在处理数组名等特殊类型时,这种差异可能导致意料之外的错误。
面试题4:C 语言中的 static 和 C++ 中的 static 有什么区别?
在 C 语言中,static 主要用于修饰:
- 局部静态变量:延长局部变量的生命周期,使其在程序运行期间一直存在。
- 外部静态变量和函数:限制变量或函数的作用域为当前文件,实现封装和信息隐藏。
而在 C++ 中,除了具备 C 语言中所有的功能之外,static 还被扩展用于:
- 定义类中的静态成员变量和静态成员函数。这些静态成员属于整个类,而不是类的某个对象,可以在多个对象之间共享。
注意: 在编程中,static 所具有的“记忆性”和“全局性”特点,使得不同调用之间可以共享数据、传递信息。在 C++ 中,类的静态成员则能够在不同的对象实例之间进行通信和数据共享。
面试题5:C 中的 malloc 和 C++ 中的 new 有什么区别?
- new 和 delete 是 C++ 中的操作符,支持重载,只能在 C++ 程序中使用;而 malloc 和 free 是标准库函数,可以在 C 和 C++ 中通用,并且可以被覆盖(如自定义内存管理)。
- new 在分配内存的同时会自动调用对象的构造函数,完成初始化;相应的,delete 在释放内存时会调用对象的析构函数。而 malloc 只负责分配内存空间,free 仅用于释放内存,它们都不会触发构造函数或析构函数的执行。
- new 返回的是具体类型的指针,具有类型安全性;而 malloc 返回的是 void* 类型,需要显式地进行类型转换才能使用。
注意: 使用 malloc 分配的内存必须通过 free 来释放,而使用 new 分配的内存则必须使用 delete 来释放,两者不可混用。因为它们底层实现机制不同,混用可能导致未定义行为或内存泄漏。
面试题6:写一个“标准”宏 MIN
#define MIN(a,b) ((a)<=(b)?(a):(b))
注意:在调用时一定要注意这个宏定义的副作用。
面试题7:指针可以被声明为 volatile 吗?
可以,指针可以被声明为 volatile 类型。因为从本质上讲,指针本质上也是一种变量,它保存的是一个内存地址(即整型数值),这一点与普通变量并没有本质区别。
在某些特殊场景下,比如硬件寄存器访问、中断服务程序中共享的数据等,指针的值可能会被程序之外的因素修改,此时就需要使用 volatile 来告诉编译器不要对该指针进行优化,确保每次访问都直接从内存中读取最新值。
例如,在中断服务程序中修改一个指向缓冲区(buffer)的指针时,就必须将该指针声明为 volatile,以防止编译器因优化而忽略其变化。
说明: 虽然指针具有特殊的用途——用于访问内存地址,但从变量访问的角度来看,它和其他变量一样具备可变性,因此也可以像普通变量一样使用 volatile 进行修饰。
面试题8:a 和 &a 有什么区别?请写出以下 C 程序的运行结果
#include <stdio.h>int main(void)
{int a[5] = {1, 2, 3, 4, 5};int *ptr = (int *)(&a + 1);printf("%d, %d", *(a + 1), *(ptr - 1));return 0;
}
数组名a和&a的区别
表达式 | 类型 | 含义 |
a | int* | 指向数组首元素 a[0] 的指针 |
&a | int (*)[5] | 指向整个数组 a 的指针 |
虽然它们的地址值相同,但类型不同,因此在进行指针运算时步长也不同:
- a + 1 是以 sizeof(int) 为单位移动(即跳过一个 int)。
- &a + 1 是以 sizeof(int[5]) 为单位移动(即跳过整个数组)。
输出结果:2, 5
面试题9:简述 C/C++ 程序编译时的内存分配情况
从内存分配机制角度,C/C++ 的内存管理还可以分为以下三种方式:
分配方式 | 特点描述 |
静态分配 | 在程序编译阶段完成,如全局变量、静态变量。在整个程序运行过程中有效,速度最快,不易出错。 |
栈上分配 | 函数调用时在栈中为局部变量分配空间,函数返回后自动释放。速度快但容量有限。 |
堆上分配 | 使用 malloc / new 动态申请内存,程序员负责手动释放。灵活性强但容易出错,如内存泄漏、碎片等问题。 |
面试题10:简述strcpy、sprintf与memcpy的区别。
1、操作对象不同
函数 | 源对象类型 | 目标对象类型 | 说明 |
strcpy | 字符串(以 \0 结尾) | 字符串 | 用于字符串之间的拷贝 |
sprintf | 可为任意基本数据类型 | 字符串 | 格式化输出到字符串 |
memcpy | 任意可操作的内存地址 | 任意可操作的内存地址 | 用于任意内存块之间的拷贝 |
2、功能用途不同
- strcpy 专门用于字符串之间的拷贝,遇到 \0 停止拷贝,因此只适用于以 \0 结尾的字符串。
- sprintf 将各种类型的数据格式化后写入字符串中,功能强大但主要用于字符串格式化拼接,不是纯粹的拷贝函数。
- memcpy 用于两个内存块之间的直接拷贝,不依赖 \0,也不关心数据类型,适用于任意类型的数据拷贝。
3、执行效率不同
函数 | 效率评价 |
memcpy | 最高。直接进行内存拷贝,没有格式转换或终止判断 |
strcpy | 中等。需要逐字节判断是否到达 \0 |
sprintf | 最低。涉及格式解析和字符转换,开销较大 |
4、使用注意事项
- strcpy 不检查目标缓冲区大小,容易造成缓冲区溢出,建议使用 strncpy。
- sprintf 同样不检查缓冲区边界,推荐使用更安全的 snprintf。
- memcpy 虽然高效,但如果源和目标内存区域有重叠,应使用 memmove 替代。
面试题11:如何将地址为0x67a9的整型变量赋值为0xaa66?
volatile int *ptr = (volatile int *)0x67a9;
*ptr = 0xaa66;
面试题12:面向对象编程的三大基本特征是什么?
面向对象编程(OOP)的三大核心特征是:
- 封装性(Encapsulation)
- 继承性(Inheritance)
- 多态性(Polymorphism)
这三大特性是构建面向对象系统的基础,它们共同支持代码的模块化、可重用性和灵活性。
特征 | 含义 | 核心作用 |
封装 | 隐藏实现细节,提供统一接口 | 提高安全性、简化使用 |
继承 | 类之间共享属性和方法 | 代码复用、建立类的层级关系 |
多态 | 父类引用指向子类对象,动态绑定 | 实现灵活调用、提升程序扩展性 |
面试题13:C++ 的空类默认包含哪些成员函数?
缺省构造函数:用于创建对象实例时初始化对象。
缺省拷贝构造函数:当对象通过另一个同类对象进行初始化时调用。
缺省析构函数:在对象生命周期结束时自动调用,用于清理资源。
缺省赋值运算符:将一个对象的内容赋值给另一个同类的对象。
缺省取址运算符:返回对象的内存地址。
缺省取址运算符 const:对于常量对象,返回其内存地址。
注意要点:
- 仅在需要时生成:只有当程序中实际使用了这些函数时,编译器才会为其生成相应的实现。
- 覆盖默认行为:如果需要自定义上述任何一种行为,可以通过显式定义相应的成员函数来覆盖默认实现。
- 全面了解默认函数:尽管有些资料可能只提及前四个默认函数,但后两个(取址运算符及其 const 版本)同样是重要的,默认情况下它们也是存在的。
面试题14:请谈谈你对拷贝构造函数和赋值运算符的理解
拷贝构造函数与赋值运算符重载在功能上都用于对象之间的复制操作,但它们在使用场景和实现逻辑上有以下两个主要区别:
(1)拷贝构造函数用于生成一个新的类对象,而赋值运算符则用于已有对象的重新赋值。
(2)由于拷贝构造函数是用于构造新对象的过程,因此在初始化之前无需判断源对象是否与新建对象相同;而赋值运算符必须进行这种判断(即自赋值检查),以避免不必要的错误。此外,在赋值操作中,如果目标对象已经分配了内存资源,则需要先释放原有资源,再进行新的内存分配和数据复制。
注意:当类中包含指针类型的成员变量时,必须显式重写拷贝构造函数和赋值运算符,避免使用编译器默认生成的版本。否则可能会导致浅拷贝问题,引发内存泄漏或重复释放等问题。
面试题15:如何用 C++ 设计一个不能被继承的类?
template <typename T>
class A
{friend T; // 只有 T 类可以访问 A 的私有成员
private:A() {} // 私有构造函数~A() {} // 私有析构函数
};class B : virtual public A<B>
{
public:B() {} // 构造函数~B() {} // 析构函数
};class C : virtual public B
{
public:C() {}~C() {}
};int main(void)
{B b; // 合法:B 可以实例化// C c; // 编译失败:因为 C 继承自 B,而 B 是 A<B> 的子类,构造受限return 0;
}
面试题16:访问基类的私有虚函数。请写出以下 C++ 程序的输出结果
#include <iostream.h>class A
{
public:virtual void g() {cout << "A::g" << endl;}private:virtual void f(){cout << "A::f" << endl;}
};class B : public A
{
public:void g(){cout << "B::g" << endl;}virtual void h(){cout << "B::h" << endl;}
};typedef void (*Fun)(void);int main(void)
{B b;Fun pFun;for (int i = 0; i < 3; i++){pFun = (Fun)*((int*)*(int*)(&b) + i);pFun();}return 0;
}
程序输出结果:
B::g
A::f
B::h
注意:本题主要考察了面试者对虚函数的理解程度。一个对虚函数不了解的人很难正确的做出本题。 在学习面向对象的多态性时一定要深刻理解虚函数表的工作原理。
面试题17:简述类成员函数的重写(Override)、重载(Overload)和隐藏(Hide)的区别。
(1)重写 和 重载 的主要区别如下:
- 作用范围不同: 重写的函数位于两个不同的类中(基类和派生类),而重载的函数始终在同一个类中。
- 参数列表不同: 重写函数与被重写函数的参数列表必须完全相同;而重载函数与被重载函数的参数列表必须不同(包括参数个数、类型或顺序)。
- virtual 关键字要求不同: 基类中的被重写函数必须使用 virtual 关键字进行修饰,才能实现多态;而重载函数无论是否使用 virtual 都不影响其重载特性。
(2)隐藏 与 重写、重载 的区别如下:
- 作用范围不同: 与重写类似,隐藏也发生在基类与派生类之间,而不是像重载那样在同一类中。
- 参数列表不同: 隐藏函数与被隐藏函数的参数列表可以相同,也可以不同,但它们的函数名必须相同。当参数列表不同时,不论基类中的函数是否为虚函数,都会导致基类函数被隐藏,而不是被重写。
说明: 虽然重载和重写都是实现多态性的基础,但它们的技术实现方式完全不同,所达成的目的也有本质区别。其中,重写支持的是运行时多态(动态绑定),而重载实现的是编译时多态(静态绑定)。
面试题18:说下多态实现的原理
当编译器检测到一个类中包含虚函数时,会自动为该类生成一个虚函数表(vtable)。虚函数表中的每一项都是一个指向相应虚函数的指针。
同时,编译器会在该类的实例中隐式插入一个虚函数表指针(vptr),用于指向该类对应的虚函数表。对于大多数编译器(如 VC++),这个 vptr 通常被放置在对象内存布局的最开始位置。
在调用构造函数创建对象时,编译器会在构造过程中自动执行一段隐藏代码,将 vptr 指向当前类的 vtable,从而建立起对象与虚函数表之间的关联。
此外,在构造函数内部,this 指针此时已经指向具体的派生类对象,因此可以通过 vptr 找到正确的 vtable,进而访问到实际应调用的虚函数体。这种机制使得程序可以在运行时根据对象的实际类型来动态绑定函数调用,这就是所谓的动态联编(Dynamic Binding),也是 C++ 实现多态的核心原理。
注意:
- 要明确区分 虚函数、纯虚函数 和 虚拟继承 的概念及其区别;
- 理解虚函数表的工作机制是掌握面向对象多态性的关键;
- 在 C++ 面试中,虚函数与多态是高频考点,务必熟练掌握其实现原理和应用场景。
面试题19:链表和数组有什么区别?
(1)存储形式不同: 数组是一块连续的内存空间,声明时必须指定其固定长度;而链表由一系列不连续的节点组成,每个节点包含数据部分和指向下一个节点的指针,其长度可以根据需要动态增长或缩减。
(2)数据查找效率不同: 数组支持随机访问,可以通过索引直接定位元素,查找效率高;而链表只能从头节点开始逐个遍历,查找效率较低。
(3)插入与删除操作不同: 在链表中进行插入或删除操作只需修改相邻节点的指针,效率较高;而在数组中插入或删除元素通常需要移动大量数据以保持连续性,操作效率较低。
(4)越界问题: 数组存在越界风险,使用不当可能导致程序崩溃或未定义行为;链表不存在越界问题,但需注意空指针异常。
说明:
选择数组还是链表应根据具体应用场景来决定:
- 若应用更注重快速查询,且数据量变化不大,优先考虑使用数组;
- 若应用频繁进行插入和删除操作,数据量不确定,建议使用链表。
数组占用空间紧凑,但长度固定;链表灵活可变,但额外占用空间用于保存指针信息。合理选择数据结构有助于提升程序的效率与稳定性。
面试题20:MySQL索引哪些情况会失效?
- 查询条件包含or,可能导致索引失效
- 如何字段类型是字符串,where时一定用引号括起来,否则索引失效
- like通配符可能导致索引失效。
- 联合索引,查询时的条件列不是联合索引中的第一个列,索引失效。
- 在索引列上使用mysql的内置函数,索引失效。
- 对索引列运算(如,+、-、*、/),索引失效。
- 索引字段上使用(!= 或者 < >,not in)时,可能会导致索引失效。
- 索引字段上使用is null, is not null,可能导致索引失效。
- 左连接查询或者右连接查询查询关联的字段编码格式不一样,可能导致索引失效。
- mysql估计使用全表扫描要比使用索引快,则不使用索引。
面试题21:说说队列和栈的异同。
队列(Queue)和栈(Stack)都属于基础的线性数据结构,但它们在数据的插入与删除操作方式上存在明显差异。
- 操作特性不同: 队列遵循“先进先出”(FIFO, First In First Out)原则,即最先插入的元素最先被移除; 栈则遵循“后进先出”(LIFO, Last In First Out)原则,即最后压入栈的元素最先弹出。
- 应用场景不同: 队列适用于需要按顺序处理数据的场景,如任务调度、消息队列等; 栈常用于函数调用、表达式求值、括号匹配等需要回溯的场景。
- 实现方式类似: 它们都可以通过数组或链表实现,但在插入和删除的位置上有明确限制。
注意:区分“数据结构中的栈”与“程序内存中的栈区”
虽然“栈”这个词在数据结构中表示一种存储模型,但它也常用于描述程序运行时的内存区域,容易引起混淆。
- 栈区(Stack): 是程序运行时的一个内存区域,由编译器自动分配和释放,主要用于存放函数参数、局部变量等。其访问方式类似于数据结构中的栈,具有高效快速的特点。
- 堆区(Heap): 是用于动态内存管理的一块内存区域,通常由程序员手动申请和释放(如使用 malloc / new 和 free / delete)。如果未手动释放,程序结束后可能由操作系统回收。其分配方式类似于链表,灵活但需谨慎管理。
- 两者是完全不同的概念: 数据结构中的“堆”和“栈”描述的是数据组织的方式,而程序内存中的“堆区”和“栈区”指的是内存的分配区域,二者本质上并无直接关联。
面试题22:索引不适合哪些场景?
- 数据量少的不适合加索引
- 更新比较频繁的也不适合加索引
- 区分度低的字段不适合加索引(如性别)
面试题23:编码实现直接插入排序
直接插入排序编程实现如下:
#include<iostream.h>
void main( void )
{ int ARRAY[10] = { 0, 6, 3, 2, 7, 5, 4, 9, 1, 8 }; int i,j; for( i = 0; i < 10; i++) { cout<<ARRAY[i]<<" "; } cout<<endl; for( i = 2; i <= 10; i++ ) //将 ARRAY[2],…,ARRAY[n]依次按序插入 { if(ARRAY[i] < ARRAY[i-1]) //如果 ARRAY[i]大于一切有序的数值, //ARRAY[i]将保持原位不动 { ARRAY[0] = ARRAY[i]; //将 ARRAY[0]看做是哨兵,是 ARRAY[i]的副本 j = i - 1; do{ //从右向左在有序区 ARRAY[1..i-1]中
//查找 ARRAY[i]的插入位置 ARRAY[j+1] = ARRAY[j]; //将数值大于 ARRAY[i]记录后移 j-- ; }while( ARRAY[0] < ARRAY[j] ); ARRAY[j+1]=ARRAY[0]; //ARRAY[i]插入到正确的位置上 } } for( i = 0; i < 10; i++) { cout<<ARRAY[i]<<" "; } cout<<endl;
}
注意:所有为简化边界条件而引入的附加结点(元素)均可称为哨兵。引入哨兵后使得查找循环条件的 时间大约减少了一半,对于记录数较大的文件节约的时间就相当可观。类似于排序这样使用频率非常高 的算法,要尽可能地减少其运行时间。所以不能把上述算法中的哨兵视为雕虫小技。
面试题24:日常工作中你是怎么优化SQL的?
可以从这几个维度回答这个问题:
- 加索引
- 避免返回不必要的数据
- 适当分批量进行
- 优化sql结构
- 分库分表
- 读写分离
面试题25:编码实现冒泡排序
冒泡排序编程实现如下:
#include <stdio.h>
#define LEN 10 //数组长度 void main( void ) { int ARRAY[10] = { 0, 6, 3, 2, 7, 5, 4, 9, 1, 8 }; //待排序数组 printf( "\n" ); for( int a = 0; a < LEN; a++ ) //打印数组内容 { printf( "%d ", ARRAY[a] ); } int i = 0; int j = 0; bool isChange; //设定交换标志
for( i = 1; i < LEN; i++ )
{ //最多做 LEN-1 趟排序 isChange = 0; //本趟排序开始前,交换标志应为假 for( j = LEN-1; j >= i; j-- ) //对当前无序区 ARRAY[i..LEN]自下向上扫描
{
if( ARRAY[j+1] < ARRAY[j] )
{ //交换记录 ARRAY[0] = ARRAY[j+1]; //ARRAY[0]不是哨兵,仅做暂存单元 ARRAY[j+1] = ARRAY[j]; ARRAY[j] = ARRAY[0]; isChange = 1; //发生了交换,故将交换标志置为真 }
}
printf( "\n" );
for( a = 0; a < LEN; a++) //打印本次排序后数组内容 { printf( "%d ", ARRAY[a] );
}
if( !isChange )
{ break;
} //本趟排序未发生交换,提前终止算法
printf( "\n" ); return;
}
面试题26:InnoDB与MyISAM的区别
特性 | InnoDB | MyISAM |
是否支持事务 | ✅ | ❌ |
是否支持外键 | ✅ | ❌ |
是否支持 MVCC | ✅ | ❌ |
全文索引 | ✅(MySQL 5.7+) | ✅ |
锁级别 | 行级锁、表级锁 | 表级锁 |
主键要求 | 必须有主键 | 可无主键 |
崩溃恢复能力 | 强,支持事务恢复 | 较弱 |
存储结构 | 索引组织表,共享/独立表空间 | .frm/.MYD/.MYI 文件存储 |
选择合适的存储引擎应根据具体业务需求进行权衡:
- 如果强调事务安全、并发写入、数据一致性,推荐使用 InnoDB;
- 如果以读为主、对事务要求不高、追求轻量快速,可考虑使用 MyISAM。
面试题27:编程实现堆排序
堆排序编程实现:
void createHeep(int ARRAY[], int sPoint, int Len) //生成大根堆
{while ((2 * sPoint + 1) < Len){int mPoint = 2 * sPoint + 1;if ((2 * sPoint + 2) < Len){if (ARRAY[2 * sPoint + 1] < ARRAY[2 * sPoint + 2]){mPoint = 2 * sPoint + 2;}说明:堆排序,虽然实现复杂,但是非常的实用。另外读者可是自己设计实现小堆排序的算法。虽然和
大堆排序的实现过程相似,但是却可以加深对堆排序的记忆和理解。
28.编程实现基数排序 }if (ARRAY[sPoint] < ARRAY[mPoint]) //堆被破坏,需要重新调整{int tmpData = ARRAY[sPoint]; //交换 sPoint 与 mPoint 的数据ARRAY[sPoint] = ARRAY[mPoint];ARRAY[mPoint] = tmpData;sPoint = mPoint;}else{break; //堆未破坏,不再需要调整}}return;
}
void heepSort(int ARRAY[], int Len) //堆排序
{int i = 0;for (i = (Len / 2 - 1); i >= 0; i--) //将 Hr[0,Lenght-1]建成大根堆{createHeep(ARRAY, i, Len);}for (i = Len - 1; i > 0; i--){int tmpData = ARRAY[0]; //与最后一个记录交换ARRAY[0] = ARRAY[i];ARRAY[i] = tmpData;createHeep(ARRAY, 0, i); //将 H.r[0..i]重新调整为大根堆}return;
}
int main(void)
{int ARRAY[] = { 5, 4, 7, 3, 9, 1, 6, 8, 2 };printf("Before sorted:\n"); //打印排序前数组内容for (int i = 0; i < 9; i++){printf("%d ", ARRAY[i]);}printf("\n");heepSort(ARRAY, 9); //堆排序printf("After sorted:\n"); //打印排序后数组内容for (i = 0; i < 9; i++){printf("%d ", ARRAY[i]);}printf("\n");
}
面试题28:数据库索引的原理及为什么选择B+树?
在探讨数据库索引时,我们首先需要理解其核心目标:提高数据检索速度。为了达到这个目的,数据库系统使用了不同的数据结构来实现索引。这里我们将讨论为何B+树成为大多数关系型数据库(如MySQL)中索引的首选结构,而不是二叉树或平衡二叉树等其他选项。
1. 查询速度与效率稳定性
- 二叉树:理论上,二叉查找树可以在O(log n)时间内完成查找、插入和删除操作。然而,在最坏情况下(例如数据已经排序的情况下),二叉树可能会退化成链表,导致时间复杂度变为O(n),这极大地影响了查询性能。
- 平衡二叉树(如AVL树或红黑树):这些树通过各种机制保持树的高度尽可能小,从而保证操作的时间复杂度为O(log n)。但是,它们每个节点只存储一个键值,这意味着对于大规模数据集,树的高度仍然可能较大,导致磁盘I/O次数增加。
- B树:B树允许每个节点包含多个键值和子节点指针,这样可以减少树的高度,进而减少磁盘访问次数。不过,B树中的所有节点都存储了实际的数据记录,这限制了单个节点能容纳的键值数量,间接增加了树的高度。
- B+树:相比B树,B+树将所有数据记录仅存放在叶子节点上,非叶子节点仅用于索引。这种方式不仅减少了内部节点的大小,允许每个节点存放更多的键值,进一步降低了树的高度,而且所有叶子节点按顺序链接在一起,支持范围查询优化。
2. 存储空间与查找磁盘次数
- 存储空间:由于B+树的非叶子节点不存储实际数据,因此同样的存储空间内,B+树可以拥有更高的分支因子,即每个节点可以有更多的孩子节点。这意味着对于相同数量的数据,B+树通常比其他类型的树更“矮胖”,从而减少了查找过程中所需的磁盘访问次数。
- 查找磁盘次数:数据库中的索引通常是基于磁盘存储的,而磁盘I/O是相对较慢的操作。B+树的设计旨在最小化磁盘I/O,因为它通过最大化每个节点的信息量来减少从根节点到叶子节点路径上的节点数,即减少查找所需读取的磁盘块数。
面试题29:谈谈你对编程规范的理解或认识
编程规范不仅仅是代码格式的统一,更是提升代码质量、增强团队协作效率、保障项目长期维护的重要基础。我认为编程规范的核心目标可以归纳为四个方面:程序的可行性、可读性、可移植性 和 可测试性。
上述四点是编程规范的总体目标,而不是简单的条文背诵。在实际开发中,通常会结合自身编程习惯和团队要求,从以下几个方面落实编程规范:
- 命名清晰:变量、函数、类名要有意义,避免缩写模糊;
- 函数设计简洁:一个函数只做一件事,控制长度不超过一屏;
- 适当注释:关键逻辑加注释,但不过度依赖注释;
- 统一代码风格:使用 IDE 格式化配置、遵循团队编码规范;
- 模块化设计:高内聚、低耦合,便于扩展与测试。
面试题30:简述聚集索引与非聚集索引的区别
主要区别如下:
区别项 | 聚集索引 | 非聚集索引 |
数量限制 | 一个表只能有一个聚集索引 | 一个表可以有多个非聚集索引 |
数据存储顺序 | 索引键值的逻辑顺序决定了数据行的物理存储顺序(即数据按索引排序存储) | 索引的逻辑顺序与数据行的物理存储顺序无关 |
叶节点内容 | 叶节点就是实际的数据页(即索引结构和数据融合在一起) | 叶节点不包含实际数据,仅保存索引列值和指向实际数据行的指针(如行标识 RID 或聚集索引键) |
查询效率 | 查询效率高,特别是范围查询(如 WHERE id BETWEEN 100 AND 200) | 查询效率相对较低,通常需要回表查找数据 |
插入/更新代价 | 插入或更新时可能导致数据页重新排序或分裂,性能影响较大 | 插入或更新对索引维护的开销较小 |
聚集索引决定了数据在磁盘上的物理存储顺序。因此,一旦建立了聚集索引,表中的数据就按照该索引进行组织和排序。也正因为如此,每个表只能拥有一个聚集索引。
非聚集索引是一种独立于数据存储结构的索引形式。它只包含索引字段的值和一个指向对应数据行的指针(RID 或主键),因此可以在一张表上建立多个非聚集索引以满足不同查询需求。
如果将数据库索引类比为书籍目录:
- 聚集索引就像书本内容是按照目录顺序排版的;
- 非聚集索引则像附录的关键词索引,只是告诉你某个词在哪一页,并不改变正文的排列顺序。
面试题31:&&和&、||和|有什么区别
(1)&和|对操作数进行求值运算,&&和||只是判断逻辑关系。
(2)&&和||在在判断左侧操作数就能 确定结果的情况下就不再对右侧操作数求值。
注意:在编程的时候有些时候将&&或||替换成&或|没有出错,但是其逻辑是错误的,可能会导致不可 预想的后果(比如当两个操作数一个是 1 另一个是 2 时。
面试题32:C++ 的引用和 C 语言的指针有什么区别?
(1)初始化要求不同:
引用在声明时必须进行初始化,并且不会单独分配存储空间,它只是某个已有变量的别名;而指针在声明时可以不初始化,在后续赋值时才会指向某个内存地址,并且会占用自身的存储空间。
(2)可变性不同:
引用一旦绑定到一个变量后,就不能再改变,始终代表该变量;而指针可以在程序运行过程中指向不同的对象,具有更高的灵活性。
(3)空值表示不同:
引用不能为“空”,即不存在“空引用”,它必须绑定到一个有效的对象;而指针可以为空(NULL 或 nullptr),表示不指向任何对象,这在很多场景下非常有用,比如判断是否有效等。
注意:引用作为函数参数时需谨慎使用
引用常被用作函数参数,其目的是为了实现对实参的直接修改。然而,这也带来了一个潜在的问题:调用函数时从代码表面看不出该参数是否为引用,容易让人误以为传入的是普通值,从而忽略了函数可能会修改原始变量的风险。
面试题33:请简述什么是脏读、不可重复读和幻读
在数据库并发操作中,由于多个事务交替执行,可能会导致数据一致性问题。其中,脏读、不可重复读 和 幻读 是三种常见的并发异常现象,它们分别描述了不同场景下的数据不一致问题,具体如下:
(1)脏读(Dirty Read): 当一个事务读取到了另一个事务尚未提交的数据时,就可能发生脏读。例如,事务 A 修改了一条记录但还未提交,事务 B 读取了这条修改后的数据,如果事务 A 回滚,则事务 B 读到的就是无效的“脏”数据。
示例:事务 A 更新余额为 500,事务 B 读取后显示为 500,但事务 A 最终回滚,实际余额仍为 1000。
(2)不可重复读(Non-repeatable Read): 在一个事务内多次执行相同的查询,但由于其他事务对同一条数据进行了修改并提交,导致两次查询结果不一致。也就是说,同一行数据在同一个事务中被多次读取,却返回了不同的值。
示例:事务 A 第一次查询某一行得到值为 100,之后事务 B 修改该行并提交,事务 A 再次查询该行得到值为 200。
(3)幻读(Phantom Read): 一个事务在执行范围查询时,另一个事务插入或删除了符合条件的新数据并提交,导致前一个事务再次执行相同范围查询时,结果集发生了变化(多出或少了几行),这种现象称为幻读。
示例:事务 A 查询 WHERE id < 100 得到 5 条记录,事务 B 插入一条 id = 99 的记录并提交,事务 A 再次查询发现变成了 6 条记录。
总结对比表:
现象 | 描述 | 涉及操作 | 典型场景 |
脏读 | 读取到其他事务未提交的数据 | 读已修改数据 | 数据不一致,可能出现错误数据 |
不可重复读 | 同一事务内多次读取同一行,结果不同 | 读已更新数据 | 数据统计或状态判断出错 |
幻读 | 同一事务内多次范围查询,结果集数量变化 | 读新增/删除数据 | 分页查询、范围条件统计受影响 |
面试题34:在高并发情况下,如何做到安全的修改同一行数据?
通常采用两种主流机制:悲观锁 和 乐观锁。
1、使用悲观锁(Pessimistic Lock)
悲观锁的核心思想是:假设并发冲突经常发生,因此在访问数据时就加锁,防止其他线程修改。
实现方式:
- 在查询数据时加上排他锁(Exclusive Lock),例如在 SQL 中使用 SELECT ... FOR UPDATE。
适用场景:
- 写操作频繁;
- 并发冲突概率较高;
- 对数据一致性要求极高。
2、使用乐观锁(Optimistic Lock)
乐观锁的核心思想是:假设并发冲突较少发生,先允许线程读取并修改数据,在提交更新时检查是否有其他线程已经修改过该数据,若有则拒绝更新或重试。
实现方式:
版本号机制(Version Number)
- 在表中增加一个 version 字段;
- 每次更新数据时检查版本号,并将其加一;
适用场景:
- 读多写少;
- 并发冲突较少;
- 希望减少锁竞争以提升性能。
面试题35:简述 typedef 和 #define 的区别
typedef 和 #define 都可以在 C/C++ 中用于定义别名或常量,但它们的本质不同,分别属于不同的处理阶段,具有不同的特性和使用方式。主要区别如下:
(1)用途不同:
- typedef 是用于为已有数据类型创建一个新的别名(alias),主要用于增强代码的可读性和可维护性;
- #define 是预处理指令,通常用于宏定义,包括常量定义、函数宏等,本质上是文本替换。
(2)处理阶段不同:
- typedef 是编译阶段的一部分,它参与语法和类型检查,因此更安全;
- #define 是预编译阶段的宏替换机制,在编译之前进行简单的字符串替换,不进行类型检查,容易引入错误。
(3)作用域控制不同:
- typedef 受作用域限制,可以在函数内部、命名空间中定义局部别名;
- #define 定义的宏没有作用域概念,一旦定义,从定义处开始到文件结束都有效(除非被 #undef 取消)。
(4)对指针的影响不同: 这是两者一个非常容易出错的区别点:
- 使用 typedef 定义指针类型时,能正确地将整个标识符作为指针类型;
- 而使用 #define 宏定义指针类型时,容易因优先级问题导致理解偏差或错误用法。
面试题36:简述关键字const的作用和意义
在 C/C++ 中,const 是一个非常重要的关键字,用于声明常量性(只读性)。它可以修饰变量、指针、函数参数以及成员函数等,表示该标识符所代表的内容在定义后不能被修改。
主要优点:便于类型检查、同宏定义一样可以方便地进行参数 的修改和调整、节省空间,避免不必要的内存分配、可为函数重载提供参考。
面试题37:简述关键字static的作用
使用场景 | 作用描述 |
全局变量前加 static | 限制变量作用域,只在本文件内可见 |
局部变量前加 static | 延长生命周期,保持值直到程序结束,且默认初始化为0 |
函数前加 static | 限制函数作用域,只在本文件内可见 |
类中定义 static 成员 | 所有对象共享该成员 |
类中定义 static 方法 | 只能访问静态成员,无 this 指针 |
面试题38:简述关键字extern的作用
在 C/C++ 中,extern 是一个存储类修饰符,主要用于声明变量或函数是在当前文件之外定义的,即其定义位于其他源文件或库中。它的核心作用是告诉编译器:“这个变量或函数已经在别处定义了,请不要报错,链接时去其他模块中查找”。
特性 | 说明 |
关键字 | extern |
主要用途 | 声明变量或函数在其它模块中定义 |
是否分配内存 | 否,仅声明 |
是否允许多次出现 | 是,可在多个文件中声明 |
是否必须有定义 | 是,否则链接时报错 |
典型应用场景 | 跨文件共享全局变量、调用外部函数 |
面试题39:简述为什么流操作符<<和>>重载时返回引用
在 C++ 中,流操作符 <<(输出)和 >>(输入)经常被连续使用,例如链式调用多个输出或输入操作。为了支持这种链式调用,流操作符的重载函数通常返回一个对流对象的引用。这不仅提高了代码的可读性和简洁性,还确保了流操作的灵活性和效率。
为什么返回引用?
支持链式调用:
- 当我们连续使用流操作符时,如 cout << "Hello, " << "World!" << endl;,每个 << 操作实际上是一个函数调用。
- 如果返回引用,则每次调用后返回的是同一个流对象的引用,这样下一个 << 操作可以直接作用于这个流对象,从而实现链式调用。
保持流对象的状态:
- 返回引用而不是值,意味着不会创建新的流对象副本,保证了流对象的状态一致性。
- 这对于维护流对象内部的状态(如错误状态、缓冲区等)非常重要。
提高性能:
- 返回引用避免了不必要的对象复制,提高了程序运行效率。
操作符类型 | 返回值类型 | 是否支持链式调用 | 适用场景 |
流操作符 << 和 >> | 引用 | 是 | 输入输出流操作 |
赋值操作符 = | 引用 | 是 | 对象赋值 |
算术操作符 +, -, *, / | 对象 | 否 | 数值运算 |
注意:
- 在重载流操作符时,务必返回流对象的引用,否则将无法支持链式调用,导致代码可读性和功能性下降。
- 对于其他操作符,如算术运算符,由于涉及右值和临时对象的创建,应返回新构造的对象而非引用。
面试题40:简述指针常量与常量指针的区别
指针常量指的是一个指针本身是常量,也就是说,该指针一旦初始化指向某个地址后,就不能再改变其指向。
常量指针则指的是一个指向常量的指针,也就是说,不能通过该指针去修改其所指向的对象的值,但指针本身的指向可以更改。
特性 | 指针常量(Constant Pointer) | 常量指针(Pointer to Constant) |
定义形式 | T* const ptr; | const T* ptr; 或 T const* ptr; |
是否能改指向 | ❌ 不可更改 | ✅ 可更改 |
是否能改内容 | ✅ 可更改 | ❌ 不可更改 |
强调重点 | 指针本身不能变 | 指向的内容不能变 |
示例 | int* const ptr = &a; | const int* ptr = &a; |
面试题41:MySQL事务的四大特性
MySQL 中的事务(Transaction)是指作为单个逻辑工作单元执行的一系列操作,这些操作要么全部成功,要么全部失败回滚。为了保证数据的一致性和可靠性,事务必须满足四个基本特性,简称 ACID 特性,分别是:
1. 原子性(Atomicity)
- 事务是一个不可分割的工作单位,事务中的所有操作要么全部完成,要么全部不完成;
- 如果事务中任何一个步骤失败,整个事务都会被回滚到最初状态,就像这个事务从来没有执行过一样;
- 实现机制:通常通过 undo log(回滚日志) 来实现。
✅ 示例:银行转账操作中,A 转账给 B,若在转账过程中出现异常(如断电),系统会自动回滚,确保 A 没扣款,B 也不会多钱。
2. 一致性(Consistency)
- 事务的执行不能破坏数据库的完整性约束和业务逻辑规则;
- 在事务开始之前和结束之后,数据库都处于一致状态;
- 一致性是事务的最终目标,由其他三个特性共同保障。
✅ 示例:如果一个表规定字段 age 必须大于 0,则事务执行后不能让该字段变成负数。
3. 隔离性(Isolation)
- 多个事务并发执行时,彼此之间应该互不干扰,每个事务都感觉不到其他事务的存在;
- 不同的隔离级别可以控制并发访问的程度,避免脏读、不可重复读、幻读等问题;
- 实现机制:通过 锁机制 和 MVCC(多版本并发控制) 实现。
✅ 示例:两个用户同时修改同一行数据,数据库应确保它们的操作不会互相干扰。
4. 持久性(Durability)
- 事务一旦提交,其对数据库的修改就是永久性的,即使系统发生故障也不会丢失;
- 实现机制:主要依赖于 redo log(重做日志) 和 双写缓冲区 等机制。
✅ 示例:支付完成后,即使服务器突然宕机,用户的余额也已经持久化保存。
面试题42:如何避免“野指针”的产生?
“野指针”是指指向无效内存区域的指针。它并未指向一个合法的对象或内存地址,因此对它的操作可能导致程序崩溃或不可预知的行为。为了避免“野指针”的出现,可以从以下几个方面进行预防。
(1)指针变量未初始化的问题
指针变量在声明时如果没有被显式赋值,其内容是随机的,可能指向任意内存地址,从而形成野指针。
解决办法:在定义指针变量时应立即进行初始化,可以将其指向一个有效的变量地址,或者直接赋值为 NULL(C 语言)或 nullptr(C++11 及以上)。
(2)释放内存后未将指针置空的问题
当使用 free()(C 语言)或 delete / delete[](C++)释放指针所指向的内存后,如果未将指针设为 NULL 或 nullptr,该指针就变成了野指针。
解决办法:每次释放完内存后,立即将对应的指针设置为 NULL 或 nullptr,以防止后续误用。
(3)访问超出作用域的内存问题
如果指针指向的是局部变量或临时对象,在变量超出作用域之后,其所占用的内存会被系统回收,此时指针仍然保存着原来的地址,就会变成野指针。
解决办法:不要返回局部变量的地址;在变量的作用域结束前及时释放相关资源,并将指针置为 NULL 或 nullptr。
面试题43:简述常引用的作用
在 C++ 中,常引用(const reference) 是指通过 const 关键字修饰的引用,表示该引用所绑定的对象不能被修改。它的主要作用是在不改变原始对象的前提下,提供对对象的安全访问。
常引用的核心目的是:
- 防止在使用引用的过程中意外修改原始变量的值;
- 提高程序的可读性和安全性;
- 避免不必要的拷贝操作,提升性能。
面试题44:如果一张表有近千万条数据,导致 CRUD 操作变慢,你会如何进行优化?
优化方向 | 技术手段 | 适用场景 |
结构性优化 | 分库分表(水平/垂直)、引入中间件 | 数据量大、并发高 |
查询性能优化 | 索引优化、SQL 优化、覆盖索引 | 查询缓慢、索引缺失 |
存储与运维 | 读写分离、定期维护、缓存 | 读多写少、数据分散 |
架构扩展 | 异步处理、引入搜索引擎 | 实时性不高、复杂查询 |
面对千万级数据量的表,应结合具体业务场景,采用多种优化手段协同工作,既要关注查询性能,也要兼顾系统可扩展性和维护成本。
面试题45:简述strcpy、sprintf与memcpy的区别
特性 | strcpy | sprintf | memcpy |
操作对象 | 字符串 → 字符串 | 多种类型 → 字符串 | 内存块 → 内存块 |
是否支持格式哦u化 | ❌ 不支持 | ✅ 支持 | ❌ 不支持 |
效率 | 中等 | 较低 | 最高 |
安全性 | 易溢出 | 易溢出 | 高(仍需手动控制长度) |
典型应用场景 | 字符串复制 | 数据格式化输出 | 结构体/二进制数据复制 |
面试题46:简述数据库中使用自增主键可能带来的问题
问题类型 | 是否常见 | 原因 | 推荐解决方式 |
分库分表主键冲突 | ✅ 高频 | 多个分片各自维护自增序列 | 改用全局唯一主键方案 |
锁竞争性能瓶颈 | ✅ 中频 | 自增机制加锁保护 | 使用非自增主键或调整自增步长 |
主键用尽溢出 | ❌ 低频 | 数据类型限制 | 使用 BIGINT 或提前扩容 |
安全性/可预测问题 | ✅ 中频 | 主键递增可被猜测 | 使用 UUID 或雪花算法生成不可预测主键 |
建议:
- 单体数据库场景下,自增主键仍是简单高效的首选;
- 但在分布式架构、高并发写入、大数据量等复杂场景中,应优先考虑使用全局唯一主键方案,以提升系统的可扩展性和稳定性。
面试题47:MVCC熟悉吗,它的底层原理?
MVCC是一种用于数据库管理系统中提高并发性能的技术。它通过保存数据对象的多个版本来支持非锁定读取,从而减少事务间的冲突,允许更高的并发度。
MVCC 的实现依赖于以下几个核心概念和技术:
1、隐藏列:
- 每个数据行除了用户定义的列外,还会包含一些隐藏列
2、事务ID:
- 数据库系统为每个新事务分配一个唯一的递增事务ID(Transaction ID),用于标识事务的时间顺序。
3、快照读与当前读:
- 快照读(Snapshot Read): 读取事务开始时的数据快照,不会看到未提交的更改或在此之后发生的更改。
- 当前读(Current Read): 直接读取最新的数据版本,能看到所有已提交的更改。
4、垃圾回收(GC):
- 当某些旧版本的数据不再被任何活跃事务需要时,这些过期的数据版本会被清理掉,以释放存储空间。
面试题48:简述数据库连接池的概念及其必要性
数据库连接池是一种资源管理技术,它通过预先创建并维护一定数量的数据库连接对象,并对外提供获取和释放这些连接的方法,从而减少频繁建立和断开数据库连接所带来的开销。
连接池的工作机制:
- 在初始化时,连接池会预先创建一组数据库连接,并将它们保存在一个池子里。
- 当应用程序需要访问数据库时,不是直接创建新的连接,而是从连接池中借用一个现有的连接。
- 使用完毕后,连接会被归还到池中,而不是真正关闭,以便后续请求可以继续使用。
数据库连接的建立过程
- TCP三次握手: 应用程序通过 TCP 协议与数据库服务器建立网络连接。
- 身份验证: 发送用户名和密码给数据库服务器进行身份验证。
- 执行SQL语句: 验证成功后,应用程序可以向数据库发送 SQL 查询或命令。
- TCP四次挥手: 操作完成后,关闭连接,释放网络资源。
连接池的好处总结
好处 | 描述 |
资源重用 | 减少了频繁创建和销毁连接带来的系统开销 |
更快响应速度 | 快速获取连接,减少等待时间 |
控制并发量 | 限制最大连接数,保护数据库免受过载 |
统一管理 | 防止连接泄漏,简化连接管理 |
面试题49:简述构造函数是否能为虚函数?
在 C++ 中,构造函数不能是虚函数,而析构函数不仅可以是虚函数,而且在某些复杂类层次结构中,通常需要将析构函数声明为虚函数。
构造函数为何不能为虚函数?
1、虚函数表(vtable)机制:
- 虚函数依赖于每个对象内部维护的一个虚函数表(vtable),通过该表实现动态绑定。
- 构造函数的作用是初始化对象的状态,但在对象完全构造之前,vtable 尚未建立,因此无法支持虚函数调用。
2、构造顺序问题:
- 在构造过程中,基类部分先于派生类部分被构造。如果允许构造函数为虚函数,则会导致试图调用一个尚未完全构造的对象的方法,这显然是不合理的。
- 实际上,在构造函数体内调用虚函数时,只会调用到基类中的版本,因为此时派生类部分还未构造完成。
函数类型 | 是否可为虚函数 | 原因 |
构造函数 | ❌ 不可 | 对象未完全构造前,vtable 尚未建立 |
析构函数 | ✅ 可以 | 确保多态删除时,派生类部分也能正确销毁 |
纯虚析构函数 | ✅ 可以 | 必须有定义体,以便在派生类销毁时隐式调用 |
面试题50:谈谈你对面向对象的理解
面向对象是一种程序设计思想,也是一种系统分析与设计方法。它将现实世界中的事物抽象为程序中的“对象”,通过对象之间的交互来完成系统的功能。
1、面向对象的核心思想
面向对象的核心在于从“对象”的角度出发思考问题,而不是单纯地围绕功能或流程展开设计。它强调的是:
- 万物皆对象: 将现实世界中的实体映射为程序中的对象,每个对象都具有属性(数据)和行为(方法)。
- 模块化设计: 将复杂问题分解为多个相对独立的对象,分别进行设计和实现,提高代码的可维护性和扩展性。
- 高内聚、低耦合: 每个对象内部封装自己的状态和行为,对外提供清晰的接口,减少对象间的依赖关系。
2、面向对象的三大基本特性
封装(Encapsulation):
- 将对象的属性和行为包装在一起,并控制对外暴露的程度;
- 提供访问控制机制(如 private、protected、public),增强数据的安全性。
继承(Inheritance):
- 子类可以继承父类的属性和方法,实现代码复用;
- 支持类层次结构的设计,体现“is-a”关系。
多态(Polymorphism):
- 同一个接口可以有不同的实现方式;
- 主要通过虚函数(C++)、接口(Java)或抽象类实现,提升系统的灵活性和扩展性。
3、面向对象与传统面向过程的区别
对比维度 | 面向过程(Procedural) | 面向对象(Object-Oriented) |
设计视角 | 基于功能和流程 | 基于对象和交互 |
数据与行为关系 | 分离 | 绑定在一起 |
扩展性 | 修改原有代码多,扩展困难 | 易于通过继承和多态扩展 |
可维护性 | 结构松散,维护成本高 | 模块清晰,易于维护 |
4、面向对象不仅仅是指编程
虽然我们最常接触的是“面向对象编程(OOP)”,但完整的面向对象技术还包括:
- 面向对象分析(OOA): 分析问题领域,识别出关键的对象及其关系。
- 面向对象设计(OOD): 在分析基础上设计系统的结构,包括类图、交互图等。
- 面向对象建模(OOM): 使用 UML(统一建模语言)等工具对系统进行可视化建模。
由于篇幅限制,本期C/C++高频面试题就写到这儿了
(需要这份C/C++高频面试题1000道pdf文档的同学,文章底部关注后自取)
下期会再更新50道题,先剧透部分题目:
51.const、static作用。
52.c++面向对象三大特征及对他们的理解,引出多态实现原理、动态绑定、菱形继承。
53.虚析构的必要性,引出内存泄漏,虚函数和普通成员函数的储存位置,虚函数表、虚函数表指针
54.malloc、free和new、delete区别,引出malloc申请大内存、malloc申请空间失败怎么办
55.stl熟悉吗,vector、map、list、hashMap,vector底层,map引出红黑树。优先队列用过吗,使用的场景。
无锁队列听说过吗,原理是什么(比较并交换)
56.实现擅长的排序,说出原理(快排、堆排)
57.四种cast,智能指针
58.tcp和udp区别
59.进程和线程区别 60.指针和引用作用以及区别
61.c++11用过哪些特性,auto作为返回值和模板一起怎么用,函数指针能和auto混用吗
62.boost用过哪些类,thread、asio、signal、bind、function
63.单例、工厂模式、代理、适配器、模板,使用场景
64.QT信号槽实现机制,QT内存管理,MFC消息机制
65.进程间通信。会选一个详细问
66.多线程,锁和信号量,互斥和同步
67.动态库和静态库的区别
(需要这份C/C++高频面试题1000道pdf文档的同学,文章底部关注后自取)
相关文章:
C/C++ 高频八股文面试题1000题(一)
原作者:Linux教程,原文地址:C/C 高频八股文面试题1000题(一) 在准备技术岗位的求职过程中,C/C始终是绕不开的核心考察点。无论是互联网大厂的笔试面试,还是嵌入式、后台开发、系统编程等方向的岗位,C/C 都…...
C++ map 和 unordered_map 的区别和联系
C map 和 unordered_map 的区别和联系 map 和 unordered_map 都是 C 标准库中关联容器,用于存储键值对。它们的主要区别在于底层实现和性能特性,联系在于它们都提供了键值对的存储和访问功能。 区别: 特性mapunordered_map底层实现红黑树 …...
Sentinel实现原理
Sentinel 是阿里巴巴开源的分布式系统流量控制组件,主要用于服务保护,涵盖流量控制、熔断降级、系统负载保护等功能。 以下是 Sentinel 的实现原理,使用中文简要说明: 1. 总体架构 Sentinel 采用 轻量级 设计,分为 核…...
python打卡day37
疏锦行 知识点回顾: 1. 过拟合的判断:测试集和训练集同步打印指标 2. 模型的保存和加载 a. 仅保存权重 b. 保存权重和模型 c. 保存全部信息checkpoint,还包含训练状态 3. 早停策略 作业:对信贷数据集训练后保存权重…...
MySQL复杂查询优化实战:从多表关联到子查询的性能突破
文章目录 一、复杂查询性能瓶颈分析与优化框架二、多表关联查询的优化策略与实战1. JOIN顺序优化:基于成本估算的表关联策略2. 复合索引与JOIN条件优化3. 大表JOIN的分片处理 三、子查询优化:从嵌套到JOIN的转换艺术1. 标量子查询转换为JOIN2. EXISTS子查…...
LeetCode 680.验证回文串 II
目录 题目: 题目描述: 题目链接: 思路: 核心思路: 思路详解: 代码: C代码: Java代码: 题目: 题目描述: 题目链接: 680. 验证…...
window显示驱动开发—输出合并器阶段
逻辑管道中的最后一步是通过模具或深度确定可见性,以及写入或混合输出以呈现目标,这可以是多种资源类型之一。 这些操作以及输出资源 (呈现目标) 绑定在输出合并阶段定义。 1. 核心功能与管线定位 输出合并是渲染管线的最终固定功能阶段,负…...
单片机开发日志cv MDK-ARM工具链迁移到MAKE
核心经验: STM32H7 多 RAM 区域,外设相关数据段必须放在 AXI SRAM(RAM)区,不能放在 DTCMRAM,否则外设无法访问,程序表面正常但外设全失效。迁移工程时,务必检查链接脚本的内存分布&a…...
大模型与搜索引擎的技术博弈及未来智能范式演进
基于认知革命与技术替代的全景综述 一、大模型对搜索引擎的替代性分析:技术范式与市场重构 (1)技术原理的代际分野 传统搜索引擎遵循 "爬虫抓取 - 索引构建 - 关键词排序" 的三段式架构,其核心是基于 PageRank 算法的…...
Ajax-入门
Ajax: 全称Asynchronous JavaScript And XML,异步的JavaScript和XML。其作用有如下2点: 与服务器进行数据交换:通过Ajax可以给服务器发送请求,并获取服务器响应的数据。 异步交互:可以在不重新加载整个页面的情况下&a…...
FPGA基础 -- Verilog 共享任务(task)和函数(function)
Verilog 中共享任务(task)和函数(function) 的详细专业培训,适合具有一定 RTL 编程经验的工程师深入掌握。 一、任务(task)与函数(function)的基本区别 特性taskfunctio…...
c++set和pair的使用
set是C中的一种关联容器,具有以下特点: 存储唯一元素(不允许重复) 元素自动排序(默认升序) 基于红黑树实现(平衡二叉搜索树) 插入、删除和查找的时间复杂度为O(log n) 前言 在C…...
数据库中间件ShardingSphere5
一、高性能架构模式 数据库集群,第一种方式“读写分离”,第二种方式“数据库分片”。 1.1 读写分离架构 读写分离原理:将数据库读写操作分散到不同的节点上。 读写分离的基本实现: 主库负责处理事务性的增删改操作,…...
window显示驱动开发—使用状态刷新回调函数
用户模式显示驱动程序可以使用 Direct3D 运行时版本 10 State-Refresh回调函数 来实现无状态驱动程序或构建命令缓冲区前导数据。 Direct3D 运行时在调用 CreateDevice (D3D10 ) 函数时,向D3D10DDIARG_CREATEDEVICE结构的 pUMCallbacks 成员指向的D3D10DDI_CORELAY…...
windows11右击恢复为windows10
文章目录 前言一、问题描述二、解决方案 前言 为了解决win11的右击更多选项的问题 一、问题描述 win11的右键更多选项过于繁琐 二、解决方案 在windows11的终端管理员中输入如下代码: reg add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c…...
基于物联网的智能衣柜系统设计
标题:基于物联网的智能衣柜系统设计 内容:1.摘要 随着物联网技术的飞速发展,智能家居领域迎来了新的变革机遇。本研究的目的在于设计一种基于物联网的智能衣柜系统,以提升用户的衣物管理和使用体验。方法上,通过搭建物联网硬件平台ÿ…...
GM DC Monitor v2.0 卸载教程
以下俩种方法任选一种均可 第一种方法:一键自动卸载 进入到软件安装目录 卸载app 进入到app目录,运行一键卸载脚本:sh uninstall.sh 卸载es 进入到es目录,运行一键卸载脚本:sh uninstall.sh 卸载db 进入到db目录&a…...
C#上位机实现报警语音播报
我们在开发C#上位机时,有时候会需要将报警信息通过语音进行播报,今天跟大家分享一下具体的实现过程。 一、组件安装 首先我们创建好一个Windows窗体项目,然后添加System.Speech库引用。 点击引用,右击添加引用,在程…...
python自助棋牌室管理系统
目录 技术栈介绍具体实现截图系统设计研究方法:设计步骤设计流程核心代码部分展示研究方法详细视频演示试验方案论文大纲源码获取/详细视频演示 技术栈介绍 Django-SpringBoot-php-Node.js-flask 本课题的研究方法和研究步骤基本合理,难度适中…...
榕壹云婚恋相亲系统:ThinkPHP+UniApp打造高效婚配平台
引言 在数字化浪潮下,婚恋相亲行业正加速向线上迁移。榕壹云公司基于市场需求与技术积累,开发一款功能完备、技术开源的婚恋相亲小程序系统,为单身人士提供高效、安全的婚恋平台。本文将围绕系统背景、客户定位、核心技术、功能模块及优势场景展开详细解析,助力开发者与技…...
每日leetcode
2890. 重塑数据:融合 - 力扣(LeetCode) 题目 DataFrame report --------------------- | Column Name | Type | --------------------- | product | object | | quarter_1 | int | | quarter_2 | int | | quarter_3 | i…...
深入理解XGBoost(何龙 著)学习笔记(五)
深入理解XGBoost(何龙 著)学习笔记(五) 本文接上一篇,内容为线性回归,介绍三部分,首先介绍了"模型评估”,然后分别提供了线性回归的模型代码:scikit-learn的Linear…...
SelectDB 在 AWS Graviton ARM 架构下相比 x86 实现 36% 性价比提升
在海量数据分析中,追求高性价比已成为各大企业的主流趋势。ARM 架构凭借其高能效和低成本的特点,逐渐在数据中心崛起,成为理想的高性价比选择。基于 ARM 架构的 AWS Graviton 系列处理器,正是这一趋势的典型代表。Graviton 处理器…...
机器学习流量识别(pytorch+NSL-KDD+多分类建模)
本文主要实现以下功能,会提供完整的可运行的代码以及解释为什么这么设计。文章不会收费,若被限制查看,请私信我。 使用 NSL-KDD 数据集的CSV文件进行流量攻击检测,使用机器学习算法实现流量攻击检测,使用pytorch框架…...
三种经典算法无人机三维路径规划对比(SMA、HHO、GWO三种算法),Matlab代码实现
代码功能 该MATLAB代码用于对比三种元启发式优化算法(SMA、HHO、GWO三种算法, SMA黏菌算法、HHO哈里斯鹰优化算法、GWO灰狼优化算法) 在特定优化问题上的性能,运行环境MATLABR2020b或更高 : 初始化问题模型ÿ…...
FTTR+软路由网络拓扑方案
文章目录 网络拓扑软路由配置FTTR光猫路由器TPLink路由器配置WAN设置LAN设置 参考 网络拓扑 软路由配置 配置静态IP地址:192.168.1.100设置网关指向主路由的IP 设置自定义DNS服务器 开启DHCP 这一步很关键,可以让连上wifi的所有设备自动趴强。 FTTR光猫…...
服务器获取外网IP,并发送到钉钉
服务器获取外网IP,并发送到钉钉 import time import hmac import hashlib import base64 import urllib.parse import requests# 请填入你的钉钉机器人配置 access_token XXXX secret XXXX# 获取公网 IP def get_public_ip():try:response requests.get("…...
解决uni-app发布微信小程序主包大小限制为<2M的问题
一 问题说明 我想用uniapp开发多端应用,引入了uview组件库来美化样式,可发布为微信小程序却提示我代码质量不过关,主包代码量太大了: 二 问题分析 2.1 原生微信小程序开发代码质量限制: 1.主包代码大小不得大于2M&…...
魅族“换血”出牌:手机基本盘站不稳,想靠AI和汽车“改命”
撰稿|何威 来源|贝多财经 被吉利收购后,魅族逐渐转向在AI领域躬身耕作。 自2024年2月以“All in AI”正式宣告转型、喊出不再推出传统智能手机的豪言开始,这家曾以设计见长的手机厂商,将下半场押注在AI终端、AR眼镜与智能座舱系统上&#…...
原点安全入选 Gartner®“数据安全平台”中国市场指南代表厂商
2025年1月7日,全球权威咨询与分析机构 Gartner 发布《中国数据安全平台市场指南》(China Context: ‘Market Guide for Data Security Platforms’),北京原点数安科技有限公司(简称“原点安全”,英文名称&q…...
uni-app-配合iOS App项目开发apple watch app
假设你已经用uni-app开发好了一个iOS端的app,现在想要开发一个配套的apple watch app。改怎么去开发呢?是不是一头雾水,这篇文章就会介绍一些apple watch app开发的知识以及如何在uni-app开发的iOS app基础上去开发配套的watch app。 一、ap…...
如何理解Java反射机制
反射机制原理 反射是Java在运行时动态获取类信息、操作类属性和方法的能力。核心原理是JVM在类加载时创建Class对象,该对象包含类的完整结构信息。 关键类: Class:类的元数据入口 Field:类的成员变量 Method:类的方…...
SM3算法C语言实现(无第三方库,带测试)
一、SM3算法介绍 SM3算法是中国国家密码管理局(OSCCA)于2010年发布的商用密码散列函数标准,属于我国自主设计的密码算法体系之一 ,标准文档下载地址为:SM3密码杂凑算法 。SM3算法输出长度为256位(32字节&a…...
King’s LIMS 系统引领汽车检测实验室数字化转型
随着汽车保有量的持续攀升和车龄的增长,消费者对汽车的需求已悄然转变,从最初对外观和性能的追求,逐渐深化为对安全性、可靠性、耐久性、性能与舒适性以及智能化功能的全方位关注。这无疑让汽车检测行业在保障车辆质量、满足市场需求方面肩负…...
CppCon 2017 学习:Mocking Frameworks Considered
当然可以,下面是对 Fowler 的 Whiskey-Store 示例。 Fowler 的 Whiskey-Store 示例(坏设计) 贴出的类图是 Martin Fowler 在《重构》书中使用的一个教学用反面案例(故意设计得不合理),用来说明如何通过重…...
通过事件过滤器拦截QRadioButton点击事件
通过事件过滤器拦截QRadioButton点击事件 一、事件过滤器完整实现 1. 核心代码扩展(含注释) bool MainWindow::eventFilter(QObject* obj, QEvent* ev) {// 拦截所有QRadioButton的鼠标事件(包括点击、释放、双击)if (ev->ty…...
领码 SPARK 融合平台赋能工程建设行业物资管理革新——数智赋能,重塑中国模式新范式
摘要 工程建设行业正加速迈向数字化与精益化转型,物资管理成为项目成败的关键瓶颈。本文深入解析中国工程企业“项目部-物资部-企业项目管理部”三级协同的独特物资管理体系,聚焦集中采购与零星采购的统筹难题。基于领码 SPARK 融合平台,提出…...
“地标界爱马仕”再启:世酒中菜联袂陈汇堂共筑新会陈皮顶奢产业
“地标界爱马仕”再启战略新篇:世酒中菜联袂陈汇堂,共筑新会陈皮顶奢产业生态 ——中世国际与陈汇堂股权合作签约仪式在国际地理标志服务基地举行 江门市新会区,2025年6月20日——被誉为“地标界爱马仕”的全球顶奢品牌运营商世酒中菜 &…...
.Net Framework 4/C# 数据访问技术(ADO.NET)
一、数据库基础 (一) 数据库简介 数据库是按照数据结构来组织、存储和管理数据的仓库,是存储在一起的相关数据的集合。 (二) SQL 语言简介 SQL 是一种数据库查询和程序设计语言,用于存取数据以及查询,更新和管理关系型数据库系统。在编写 SQL 语句时,SQL 语句各关键字要以…...
北京京东,看看难度
最近由于三大外卖平台“打仗”,优惠券多到数不过来,一日三餐每个平台各点一单哈哈哈,正好最近组织内部还有朋友在北京的京东面试过,分享一下她的面经(Java岗): 1. Kafka消息不丢失问题…...
RPGMZ游戏引擎 如何手动控制文字显示速度
直接上代码 const _Window_Base_prototype_initialize Window_Base.prototype.initialize;Window_Base.prototype.initialize function(rect) {_Window_Base_prototype_initialize.call(this, rect);this.文字速度缓冲 0;}; this.文字速度缓冲 0; 进行缓冲 Window_Base…...
linux线程同步
互斥锁 同步与互斥概述** 现代操作系统基本都是多任务操作系统,即同时有大量可调度实体在运行。在多任务操作系统中,同时运行的多个任务可能: 都需要访问/使用同一种资源 多个任务之间有依赖关系,某个任务的运行依赖于另一个任…...
大内存对电脑性能有哪些提升
在科技飞速发展的今天,电脑已经成为我们生活和工作中不可或缺的伙伴。无论是日常办公、追剧娱乐,还是进行复杂的游戏和专业设计,电脑的性能都至关重要。而在影响电脑性能的众多因素中,内存大小常常被人们忽视。 多任务处理更流畅…...
什么是“微博养铁粉”以及如何增加微博铁粉
发了个发微博养铁工具_微博养铁粉的定义 微博养铁粉是指粉丝通过与博主的互动,成为博主的铁粉。铁粉是微博推出的一种反映粉丝与博主之间亲密度的互动产品。成为铁粉后,粉丝的评论权重增加,更容易上前排,点赞和评论的效果也会更好…...
华为和H3C服务器配置远控管理地址
1、华为RH2288_V3服务器 1.1、启动服务器按DEL按键进入服务器bios 1.2、选择Advanced菜单中的 IPMI iBMC Configuration配置项回车进入。 1.3、IPMI iBMC Configuration配置界面中选择IBMC Configuration配置项回车进入。 1.4、IBMC Configuration 配置项中配置IPV4 Configura…...
Git 查询与切换分支的完整指南
Git 查询与切换分支的完整指南 1. 查询分支列表 查看本地分支 git branch当前分支会以绿色显示并带有 * 标记添加 -v 或 -vv 查看更详细的信息(最后一次提交和跟踪关系) git branch -v # 或者 git branch -vv查看所有分支(包括远程分支&a…...
Spring 中的依赖注入(DI)详解
📌 摘要 在现代 Java 开发中,依赖注入(Dependency Injection, DI) 是 Spring 框架最核心的功能之一。它通过解耦对象之间的依赖关系,提高了代码的可维护性、可测试性和可扩展性。 本文将全面讲解 Spring 中依赖注入的…...
Bytebase 3.7.1 - 数据库变更功能全免费!
🔔 重大变更 所有数据库变更相关功能现已在社区版中完全免费开放!详情请查看我们的最新定价。 🎄 改进 文档网站全面升级,改进导航、搜索功能,以及与 AI 集成自助回答问题。SQL 编辑器现在会高亮光标所在的语句。SQ…...
深度学习笔记27-LSTM实现糖尿病探索与预测(Pytorch)
🍨 本文为🔗365天深度学习训练营中的学习记录博客🍖 原作者:K同学啊 一、前期准备 1.数据导入 import torch.nn as nn import torch.nn.functional as F import torchvision,torch import numpy as np import pandas as pd impo…...
3DS中文游戏全集下载 任天堂3DS简介3DS第一方独占游戏推荐
任天堂3DS 的详细介绍,涵盖其硬件特性、核心功能、游戏阵容及历史地位: 3DS游戏全集下载 https://pan.quark.cn/s/dd40e47387e7 https://sink-698.pages.dev/3ds CIA CCA 等格式可用于3DS模拟器和3DS实体机 3DS 是什么? 全称:Nin…...