c2c网站建设费用,台州百度搜索优化,网站流量分析,河北网站建设中心高性能计算面经 C八股文真景一面凉经自我介绍#xff0c;介绍一下你做过的加速的模块(叠噪#xff0c;噪声跟原图有什么关系#xff1f;)OpenGL和OpenCL有什么区别#xff1f;**1. 核心用途****2. 编程模型****3. 硬件抽象****4. API设计****5. 典型应用场景****6. 互操作性… 高性能计算面经 C八股文真景一面凉经自我介绍介绍一下你做过的加速的模块(叠噪噪声跟原图有什么关系)OpenGL和OpenCL有什么区别**1. 核心用途****2. 编程模型****3. 硬件抽象****4. API设计****5. 典型应用场景****6. 互操作性****总结表****选择依据** 为什么要用OpenCLOpenGL和OpenCL同时走会有GPU抢占问题怎么解决为什么要内存拷贝OpenCL为什么对功耗有优化你们是怎么做优化的(性能拆解和分析)性能本地跑很快部署到手机很慢怎么办跟别的算法一起跑性能有影响怎么办(并行调度)OpenCL的img和buf有什么区别**1. 数据结构和存储方式****2. 访问方式****3. 性能特点****4. 典型应用场景****5. 创建方式****6. 内核代码中的访问****7. 关键区别总结****何时选择****互操作示例** 说一下内存对齐为什么要做字节对齐你们做加速有多少人怎么划分的哪些模块是你独立完整实现的Neon是如何做的稳定性和效果问题是怎么排查的如果只能靠压测且低概率出现的问题怎么解决为什么想离职期望是多少反问部门做服务端AI加速有4-5个人 影石一面什么是8bit量化量化是怎么实现的**1. 什么是8bit量化****2. 量化的核心步骤****具体实现方法** **3. 量化的类型****4. 实现示例****5. 量化的优势与挑战****总结** 量化之后出现精度损失怎么办1. **校准过程优化**2. **量化策略调整**3. **模型结构调整**4. **后处理与微调**5. **工具与位宽选择**6. **高级技术融合**7. **验证与监控**实施示例PyTorch QAT关键点总结 C和C有什么区别1. **编程范式**2. **核心特性**3. **内存管理**4. **标准库**5. **兼容性**6. **应用场景**7. **语法细节差异**8. **性能**总结 C如何调用C函数反过来呢**1. C调用C函数****关键步骤**使用 extern C 声明C函数避免C的名称修饰。**示例** **2. C调用C函数****关键步骤**在C代码中用 extern C 导出C兼容的函数接口并在C中声明。**示例** **注意事项****总结** C中如何new和delete一个数组delete没有加[]会怎么样**1. 如何正确分配和释放数组****分配数组****释放数组** **2. 错误使用 delete 而非 delete[] 的后果****(1) 内存泄漏或资源泄漏****(2) 堆内存损坏****(3) 行为不可预测** **3. 对基本类型数组的影响****4. 总结与最佳实践****建议** **示例验证** C的多态是怎么实现的**1. 虚函数表vtable****2. 虚函数指针vptr****3. 动态绑定过程****4. 关键特性与注意事项****5. 总结** 类中有虚函数析构函数应该注意什么什么情况下会触发虚函数**一、虚函数与析构函数的注意事项****1. 虚析构函数必须存在****2. 构造函数和析构函数中避免调用虚函数** **二、虚函数的触发条件****1. 通过指针或引用调用虚函数****2. 虚函数未被隐藏或覆盖****3. 虚函数未被final禁止重写** **三、总结****关键点****示例验证** 指针和引用的区别**1. 定义与本质****示例代码** **2. 操作与功能****示例代码** **3. 参数传递与语义****示例代码** **4. 内存管理****示例代码** **5. 底层实现****总结选择指针还是引用****核心原则** 什么是深拷贝和浅拷贝**1. 浅拷贝Shallow Copy****示例问题** **2. 深拷贝Deep Copy****正确使用** **3. 关键区别总结****4. 何时需要深拷贝****5. 其他语言的实现****6. 总结** C中opencv的mat是怎么深拷贝和浅拷贝的说一下快排二分查找快速排序Quick Sort二分查找Binary Search对比总结 char a -1在计算机内存中是怎么存储的**1. 符号性决定存储形式****2. 存储过程****情况1有符号charsigned char****情况2无符号charunsigned char** **3. 验证代码****4. 总结****5. 扩展补码的优势** C实现sharedPtr手写C的string类对cache的理解如何提高缓存命中率数据结构中顺序存储和链式存储的优缺点 大疆车载面经自我介绍介绍简历项目围绕项目问涉及的知识点问了很多DSP的内容(项目相关)回顾你做的项目你觉得有哪些地方可以改进有哪些挑战难忘的经历说说你对大疆车载的了解反问优化手段有哪些模型推理加速是怎么整的量化部署过程gpu加速有什么好处原理是什么cuda相关多线程如何使用痛点是什么如何做同步同步的手段有哪些区别是什么分别用在什么场景内存优化有哪些手段dma相关的cache cpu 内存之间的联系 高性能面试问题 C八股文
真景一面凉经
自我介绍介绍一下你做过的加速的模块(叠噪噪声跟原图有什么关系)
OpenGL和OpenCL有什么区别
OpenGLOpen Graphics Library和OpenCLOpen Computing Language是两种不同的开放标准由Khronos Group维护但它们的应用场景和设计目标有显著区别。以下是两者的主要区别 1. 核心用途 OpenGL 专注于图形渲染用于在GPU上高效渲染2D/3D图形。它提供了一套API允许开发者利用硬件加速绘制复杂的图形场景如游戏、CAD、可视化等。 OpenCL 专注于通用并行计算允许开发者利用GPU、CPU或其他计算设备如FPGA进行高性能计算。它适用于科学计算、物理模拟、图像处理、机器学习等需要大规模并行计算的场景。 2. 编程模型 OpenGL 基于图形管线Graphics Pipeline包含顶点处理、光栅化、片段处理等固定或可编程阶段。使用着色器语言GLSL编写顶点着色器、片段着色器等程序。以图形对象如纹理、缓冲区、帧缓存为中心操作围绕绘制图形展开。 OpenCL 基于数据并行或任务并行模型强调将计算任务分解为多个并行执行的工作项Work Items。使用C语言扩展OpenCL C编写内核Kernel函数直接在计算设备上运行。以数据缓冲区和计算任务为中心操作围绕数据处理展开。 3. 硬件抽象 OpenGL 主要面向GPU的图形功能如光栅化、纹理映射、深度测试。设计目标是最大化图形渲染性能对图形硬件的细节进行抽象。 OpenCL 面向异构计算支持CPU、GPU、DSP等多种设备。提供对硬件资源的更底层控制如内存模型、工作组划分适合优化计算密集型任务。 4. API设计 OpenGL 状态机State Machine驱动通过设置全局状态如颜色、纹理控制渲染流程。强调图形管线的配置如绑定着色器、设置顶点数据。 OpenCL 面向任务的API需要显式管理计算设备、上下文、命令队列等。更接近通用编程模型支持异步任务提交和内存传输。 5. 典型应用场景 OpenGL 游戏开发、3D建模软件如Blender、虚拟现实VR、图形界面渲染。例如通过OpenGL绘制游戏中的角色和场景。 OpenCL 科学计算如流体模拟、图像处理如滤镜加速、密码学、深度学习推理加速。例如用OpenCL加速Photoshop中的图像处理算法。 6. 互操作性
两者可以结合使用 OpenCL处理计算任务如物理模拟、图像生成结果通过OpenGL渲染。通过共享内存对象如OpenCL缓冲区与OpenGL纹理互操作减少数据传输开销。 总结表
特性OpenGLOpenCL目标图形渲染通用并行计算硬件主要GPU图形单元支持多设备GPU/CPU等编程模型图形管线 着色器数据/任务并行 内核函数主要API状态机驱动glBind*显式任务管理clEnqueue*适用领域游戏、可视化、UI科学计算、图像处理、AI 选择依据
需要渲染图形如3D模型、特效 → OpenGL 或现代替代品Vulkan、DirectX。需要加速数值计算如矩阵运算、模拟 → OpenCL 或类似框架CUDA、SYCL。混合场景 → 结合两者如用OpenCL生成数据OpenGL渲染。
为什么要用OpenCL
OpenGL和OpenCL同时走会有GPU抢占问题怎么解决为什么要内存拷贝
OpenCL为什么对功耗有优化
你们是怎么做优化的(性能拆解和分析)
性能本地跑很快部署到手机很慢怎么办跟别的算法一起跑性能有影响怎么办(并行调度)
OpenCL的img和buf有什么区别
在OpenCL中image对象图像内存对象和buffer对象缓冲区内存对象是两种不同的内存类型设计目标和适用场景有明显区别。以下是它们的核心差异 1. 数据结构和存储方式
特性Buffer (cl_mem)Image (cl_mem)数据结构一维线性内存存储任意数据多维结构化数据2D/3D类似纹理数据格式无格式要求原始字节流需明确指定像素格式如CL_RGBA, CL_FLOAT等存储优化普通全局内存可能存储在GPU的纹理内存中硬件缓存优化 2. 访问方式
特性BufferImage读写接口直接通过指针__global float*必须通过采样器Sampler和坐标如read_imagef坐标访问线性索引buffer[offset]多维坐标如(x, y, z)自动滤波不支持支持如双线性插值、边界处理数据类型转换无自动转换自动转换为规范化值如[0,1] 3. 性能特点
特性BufferImage缓存效率依赖访问模式适合随机访问利用空间局部性适合相邻像素访问硬件加速无特殊优化可能使用纹理硬件单元高速缓存、滤波内存带宽普通全局内存带宽可能更高纹理内存的合并访问优化 4. 典型应用场景
场景BufferImage适合任务通用数据数组、结构体等图像/纹理处理滤波、插值等示例矩阵运算、物理模拟、数值计算图像卷积、光线追踪采样、体积渲染 5. 创建方式
// Buffer的创建一维数据
cl_mem buffer clCreateBuffer(context, CL_MEM_READ_WRITE, size_in_bytes, NULL, err
);// Image的创建需指定格式和维度
cl_image_format format;
format.image_channel_order CL_RGBA; // 通道顺序
format.image_channel_data_type CL_FLOAT; // 数据类型cl_mem image clCreateImage2D(context, CL_MEM_READ_ONLY, format, width, height, 0, // row_pitch0表示连续存储NULL, err
);6. 内核代码中的访问
// Buffer的访问直接指针操作
__kernel void buffer_kernel(__global float* buffer) {int idx get_global_id(0);buffer[idx] ...;
}// Image的访问需采样器和坐标
__constant sampler_t sampler CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP | CLK_FILTER_NEAREST;__kernel void image_kernel(__read_only image2d_t input, __write_only image2d_t output) {int2 coord (int2)(get_global_id(0), get_global_id(1));float4 pixel read_imagef(input, sampler, coord); // 读取像素write_imagef(output, coord, pixel * 2.0f); // 写入像素
}7. 关键区别总结
维度BufferImage数据布局一维线性多维结构化硬件支持普通内存纹理内存硬件滤波适用场景通用计算图像/空间数据访问灵活性高直接指针低需采样器坐标性能优化依赖算法空间局部性缓存优化 何时选择 用Buffer 需要处理任意结构的原始数据如数值数组、自定义结构体或需要直接内存操作如原子操作。 用Image 处理图像/纹理数据且需要利用硬件加速的滤波如双线性插值、规范化坐标如[0,1]范围或空间局部性优化如卷积核计算。 互操作示例
两者可以结合使用
用Buffer存储中间计算结果再转换为Image进行渲染。从Image读取数据后用Buffer进行非图像类计算如统计像素值。
说一下内存对齐为什么要做字节对齐
你们做加速有多少人怎么划分的哪些模块是你独立完整实现的
Neon是如何做的
稳定性和效果问题是怎么排查的
如果只能靠压测且低概率出现的问题怎么解决
为什么想离职期望是多少
反问部门做服务端AI加速有4-5个人
影石一面
网上的参考
什么是8bit量化量化是怎么实现的
8bit量化是一种通过降低数据精度来减少存储和计算开销的技术常用于优化深度学习模型。以下是详细的解释和实现步骤
1. 什么是8bit量化
核心概念将高精度如32位浮点数数据映射到8位整数范围通常为0-255或无符号整数或-128-127有符号整数以减少内存占用和加速计算。应用场景主要用于模型推理阶段的优化适用于移动端、嵌入式设备等资源受限环境。 2. 量化的核心步骤
量化通过以下公式实现 q round ( x S Z ) 和 x ′ S ⋅ ( q − Z ) q \text{round}\left(\frac{x}{S} Z\right) \quad \text{和} \quad x S \cdot (q - Z) qround(SxZ)和x′S⋅(q−Z) 其中
(x)原始浮点数值。(q)量化后的8位整数。(S)缩放因子浮点范围与整数范围的比值。(Z)零点将浮点零点对齐到整数的偏移量。
具体实现方法 确定浮点范围 非对称量化统计张量中的最小值 x min x_{\text{min}} xmin和最大值 x max x_{\text{max}} xmax。对称量化取绝对值最大的值 x max max ( ∣ x min ∣ , ∣ x max ∣ ) x_{\text{max}} \max(|x_{\text{min}}|, |x_{\text{max}}|) xmaxmax(∣xmin∣,∣xmax∣)范围变为 [ − x max , x max ] [-x_{\text{max}}, x_{\text{max}}] [−xmax,xmax]。 计算缩放因子(S) 非对称量化 S x max − x min 2 8 − 1 ( 如用无符号8位整数 ) S \frac{x_{\text{max}} - x_{\text{min}}}{2^8 - 1} \quad (\text{如用无符号8位整数}) S28−1xmax−xmin(如用无符号8位整数)对称量化 S x max 2 7 − 1 ( 如用有符号8位整数范围-128 127 ) S \frac{x_{\text{max}}}{2^{7} - 1} \quad (\text{如用有符号8位整数范围-128~127}) S27−1xmax(如用有符号8位整数范围-128 127) 计算零点(Z)仅非对称量化需要 Z round ( 0 − x min S ) Z \text{round}\left(0 - \frac{x_{\text{min}}}{S}\right) Zround(0−Sxmin) 用于对齐浮点数0与整数零点例如处理负数。 量化与反量化 量化将浮点数转换为整数 q clip ( round ( x S Z ) , 0 , 255 ) q \text{clip}\left(\text{round}\left(\frac{x}{S} Z\right), 0, 255\right) qclip(round(SxZ),0,255)反量化恢复近似浮点值 x ′ S ⋅ ( q − Z ) x S \cdot (q - Z) x′S⋅(q−Z) 3. 量化的类型
训练后量化Post-Training Quantization, PTQ 直接对训练好的模型进行校准统计各层输入/输出的范围无需重新训练。速度快但可能损失精度。 量化感知训练Quantization-Aware Training, QAT 在训练时模拟量化过程让模型适应低精度误差。精度更高但需要额外训练时间。 4. 实现示例
以非对称量化为例
输入浮点范围[-3.0, 5.0]计算缩放因子 S 5.0 − ( − 3.0 ) 255 ≈ 0.03137 S \frac{5.0 - (-3.0)}{255} \approx 0.03137 S2555.0−(−3.0)≈0.03137计算零点 Z round ( 0 − − 3.0 0.03137 ) ≈ 96 Z \text{round}\left(0 - \frac{-3.0}{0.03137}\right) \approx 96 Zround(0−0.03137−3.0)≈96量化值 q round ( − 3.0 0.03137 96 ) 0 ( 下限 ) q \text{round}\left(\frac{-3.0}{0.03137} 96\right) 0 \quad (\text{下限}) qround(0.03137−3.096)0(下限) q round ( 5.0 0.03137 96 ) 255 ( 上限 ) q \text{round}\left(\frac{5.0}{0.03137} 96\right) 255 \quad (\text{上限}) qround(0.031375.096)255(上限) 5. 量化的优势与挑战
优势 模型大小减少约75%32位→8位。内存带宽需求降低推理速度提升支持整数运算硬件。 挑战 精度损失需通过校准或QAT缓解。某些层如残差连接对量化敏感需特殊处理。 总结
8bit量化通过缩放因子和零点将浮点数映射到8位整数显著压缩模型并加速推理。其实现需权衡精度与效率常用方法包括训练后量化和量化感知训练。实际应用中TensorFlow Lite、PyTorch等框架已提供自动化工具简化流程。
量化之后出现精度损失怎么办
模型量化后出现精度损失是常见问题但可通过多种方法缓解。以下是分步解决方案
1. 校准过程优化
使用代表性数据确保校准数据集覆盖真实场景的数据分布避免偏差。动态范围调整采用百分位数校准如99.99%而非最大值避免异常值影响范围估计。逐通道量化Per-Channel对卷积层按通道单独量化减少权重分布差异带来的误差。
2. 量化策略调整
混合精度量化对敏感层如首尾层、注意力机制保留FP16其他层量化到INT8。对称与非对称量化尝试激活层用非对称量化处理ReLU后的非负值权重用对称量化。量化感知训练QAT在训练中模拟量化噪声微调模型参数以适应低精度。
3. 模型结构调整
插入归一化层在激活函数前添加BatchNorm或LayerNorm稳定激活值分布。避免敏感操作减少通道间差异大的结构如深度可分离卷积或用量化友好型结构如MobileNet。
4. 后处理与微调
量化后微调使用少量数据对量化模型微调调整参数补偿量化误差。逐层误差分析对比量化前后各层输出针对高误差层进行优化如提升其量化位宽。
5. 工具与位宽选择
更换量化工具尝试TensorRT、TFLite等不同框架利用其优化特性。调整位宽对关键层尝试INT16或在支持的情况下使用混合位宽如8/4位混合。
6. 高级技术融合
知识蒸馏用原模型指导量化模型训练提升小模型精度。自适应量化动态调整不同输入的量化参数平衡精度与速度。
7. 验证与监控
分阶段量化逐步量化模型部分层监控精度变化定位敏感模块。鲁棒性测试在边缘案例Corner Cases上验证量化模型表现针对性增强。
实施示例PyTorch QAT
import torch
from torch.quantization import QuantStub, DeQuantStub, prepare_qat, convertclass QATModel(torch.nn.Module):def __init__(self, original_model):super().__init__()self.quant QuantStub()self.model original_modelself.dequant DeQuantStub()def forward(self, x):x self.quant(x)x self.model(x)x self.dequant(x)return x# 加载预训练模型
model ... # 原始模型
qat_model QATModel(model)# 配置量化参数
qat_model.qconfig torch.quantization.get_default_qat_qconfig(fbgemm)# 准备并训练
prepare_qat(qat_model, inplaceTrue)
# 微调训练循环模拟量化噪声
train(qat_model, train_loader, epochs5)# 转换为量化模型
quantized_model convert(qat_model.eval())关键点总结
校准数据质量和量化策略是影响精度的核心因素。QAT通常比后训练量化PTQ效果更好但需训练资源。模型本身对量化的友好性如结构设计决定上限必要时需结构调整。
通过上述方法逐步调试通常可将精度损失控制在1%以内甚至无损量化。若仍不达标需权衡性能与精度考虑硬件支持的极限位宽或异构计算方案。
C和C有什么区别
C和C是两种广泛使用的编程语言虽然C最初是作为C的扩展开发的但两者在设计和用途上有显著区别。以下是它们的主要差异 1. 编程范式
C过程式编程语言强调函数和步骤的分解通过函数模块化解决问题。C多范式语言支持面向对象编程类、继承、多态、泛型编程模板、过程式编程甚至函数式编程。 2. 核心特性 C 无类和对象的概念。不支持函数重载同名函数和运算符重载。没有引用仅用指针无异常处理机制。结构体struct不能包含函数。 C 面向对象特性类class、继承、多态虚函数、封装。函数重载允许同名函数根据参数不同实现不同功能。运算符重载例如自定义操作符的行为。引用类型通过int ref a直接操作变量。模板支持泛型编程如vectorT。异常处理try/catch/throw机制。命名空间避免命名冲突namespace。 3. 内存管理
C手动管理内存仅通过malloc/calloc和free操作堆内存。C引入new和delete运算符支持构造/析构函数自动管理资源如RAII技术。 4. 标准库
C标准库较小提供基础功能如stdio.h, stdlib.h, 字符串处理等。C包含C的大部分库并扩展了面向对象和泛型的库 STL标准模板库容器vector, map、算法sort, find、迭代器。输入输出流cout/cin代替printf/scanf。 5. 兼容性
C兼容C大多数C代码可在C中编译需注意少数语法差异如强制类型转换。C不兼容CC无法直接使用C的类、引用等特性。 6. 应用场景
C适合底层开发操作系统、嵌入式系统、驱动程序追求极致性能和硬件控制。C适合大型软件、游戏引擎、GUI应用、高频交易等兼顾性能与抽象需求。 7. 语法细节差异
特性CC空指针NULLnullptr更安全默认返回值函数不写返回值默认int必须显式声明返回值类型结构体声明定义变量需加struct关键字可直接使用类型名如MyStruct s;常量定义const变量可能不是真正的常量const为真正常量支持常量表达式 8. 性能
两者性能接近但C的某些高级特性如虚函数、RTTI可能带来微小开销可通过谨慎编码规避。C更贴近硬件适合对性能极度敏感的场景。 总结
C简单、高效适合底层和资源受限环境。C功能丰富适合复杂项目通过抽象提高代码可维护性同时保持高性能。
选择时需根据项目需求若需要面向对象或高级抽象选C若追求极简和直接硬件控制选C。许多开发者会同时掌握两者以应对不同场景。
C如何调用C函数反过来呢
在C和C之间相互调用函数时由于两种语言的编译机制和符号命名规则不同需要特别注意名称修饰Name Mangling和链接规范Linkage Specification。以下是具体实现方法 1. C调用C函数
关键步骤使用 extern C 声明C函数避免C的名称修饰。
示例 C代码example.c #include stdio.hvoid c_function() {printf(This is a C function.\n);
}C头文件example.h声明时添加 extern C仅在C中生效 #ifdef __cplusplus
extern C { // 告诉C编译器以下函数按C的规则编译和链接
#endifvoid c_function();#ifdef __cplusplus
}
#endifC代码main.cpp #include example.hint main() {c_function(); // 正确调用C函数return 0;
}编译命令 gcc -c example.c -o example.o # 编译C代码
g main.cpp example.o -o main # 编译C并链接C的目标文件2. C调用C函数
关键步骤在C代码中用 extern C 导出C兼容的函数接口并在C中声明。
示例 C代码cpp_code.cpp #include iostream// 导出C兼容的函数禁止名称修饰
extern C void cpp_function() {std::cout This is a C function called from C.\n;
}C头文件cpp_header.h #ifdef __cplusplus
extern C { // C需要extern CC编译器会忽略这段代码
#endifvoid cpp_function(); // 在C中声明为普通函数#ifdef __cplusplus
}
#endifC代码main.c #include cpp_header.hint main() {cpp_function(); // 调用C函数return 0;
}编译命令 g -c cpp_code.cpp -o cpp_code.o # 编译C代码
gcc main.c cpp_code.o -o main -lstdc # 编译C并链接C的目标文件需C标准库注意事项 类型兼容性 被调用的函数参数和返回值必须是C兼容的类型如基本类型、指针、struct等。避免传递C特有类型如类、模板、引用等。 全局函数限制 C无法直接调用C的类成员函数或操作对象需通过封装全局函数间接实现extern C void wrapper_for_cpp_class_method() {MyClass obj;obj.method();
}名称修饰问题 C函数若未用 extern C其符号名会被编译器修饰如 _Z12func_namev导致C代码无法找到该函数。 编译器和链接器 确保C和C代码分别用对应编译器编译如 gcc 和 g。链接时可能需要指定C标准库如 -lstdc。 总结
场景关键方法示例C调用C函数在C头文件中用 extern C 包裹声明extern C { void c_func(); }C调用C函数在C代码中用 extern C 导出函数extern C void cpp_func() { ... }
通过合理使用 extern C 和类型兼容性约束可以无缝实现C与C的互操作。
C中如何new和delete一个数组delete没有加[]会怎么样
在C中动态分配和释放数组需要严格遵循new[]和delete[]的配对使用规则。以下是具体说明及错误使用的后果 1. 如何正确分配和释放数组
分配数组
使用 new[] 运算符为数组动态分配内存并初始化元素若为类类型调用构造函数
// 分配一个包含10个int的数组
int* intArray new int[10]; // 分配一个包含5个对象的数组假设MyClass有默认构造函数
MyClass* objArray new MyClass[5]; 释放数组
必须使用 delete[] 释放数组内存若为类类型调用每个元素的析构函数
delete[] intArray; // 正确释放int数组
delete[] objArray; // 正确释放对象数组2. 错误使用 delete 而非 delete[] 的后果
如果误用 delete 释放数组会导致未定义行为Undefined Behavior, UB具体表现可能包括
(1) 内存泄漏或资源泄漏
类类型数组delete 只会调用第一个元素的析构函数其余元素的析构函数不会被调用。 若类持有动态资源如堆内存、文件句柄这些资源将泄漏。class ResourceHolder {
public:ResourceHolder() { data new int[100]; } // 分配资源~ResourceHolder() { delete[] data; } // 应在析构函数释放资源
private:int* data;
};ResourceHolder* array new ResourceHolder[3];
delete array; // 错误只有第一个元素调用析构函数剩余2个资源泄漏(2) 堆内存损坏
new[] 和 delete[] 的实现通常会在分配的内存块头部记录数组长度。 若用 delete 释放数组内存管理器可能无法正确识别分配块的大小导致堆结构破坏引发程序崩溃或难以调试的错误。
(3) 行为不可预测
未定义行为的具体表现取决于编译器和运行时环境可能包括 程序直接崩溃。内存泄漏但程序看似正常运行。后续内存操作出现诡异错误。 3. 对基本类型数组的影响
即使数组元素是基本类型如int、float仍需使用 delete[]
int* arr new int[10];
delete arr; // 错误应为 delete[] arr;虽然基本类型没有析构函数但 new[] 可能仍会记录数组长度信息。 使用 delete 会导致内存管理器错误解析分配块可能引发堆损坏。 4. 总结与最佳实践
操作正确用法错误用法后果分配数组new T[n]--释放数组delete[] ptrdelete ptr未定义行为内存泄漏、崩溃等分配单个对象new T--释放单个对象delete ptrdelete[] ptr未定义行为堆损坏
建议
避免手动管理数组优先使用 std::vector 或 std::unique_ptrT[] 等容器/智能指针。// 使用智能指针自动管理数组
std::unique_ptrint[] safeArray(new int[10]);严格配对若必须手动管理确保 new[] 和 delete[] 严格成对出现。 示例验证
#include iostreamclass Test {
public:Test() { std::cout Constructor\n; }~Test() { std::cout Destructor\n; }
};int main() {Test* arr new Test[3];delete[] arr; // 正确输出3次Destructor// delete arr; // 错误仅输出1次Destructor其余泄漏return 0;
}输出正确使用 delete[]
Constructor
Constructor
Constructor
Destructor
Destructor
Destructor输出错误使用 delete
Constructor
Constructor
Constructor
Destructor // 只有第一个元素被销毁遵循规则可避免资源管理错误确保程序健壮性。
C的多态是怎么实现的
C的多态性主要通过虚函数Virtual Functions和虚函数表vtable机制来实现其核心在于动态绑定Dynamic Binding允许在运行时确定调用的具体函数。以下是详细的实现机制 1. 虚函数表vtable
定义每个包含虚函数的类或从包含虚函数的类派生的类都有一个虚函数表。这是一个隐式的静态数组存储该类所有虚函数的地址。结构 表中条目按虚函数的声明顺序排列。若子类重写虚函数表中对应条目替换为子类函数的地址未重写则保留父类函数地址。 多重继承若类继承多个含虚函数的基类会为每个基类维护一个独立的虚函数表。 2. 虚函数指针vptr
隐藏成员每个对象实例内部包含一个指向其所属类虚函数表的指针vptr通常位于对象内存布局的起始位置。初始化 在对象构造时由构造函数隐式初始化vptr使其指向当前类的虚函数表。构造顺序基类→派生类故vptr在构造过程中可能被多次修改最终指向实际类型的虚函数表。 析构过程析构时反向调整vptr确保销毁阶段仍能正确调用当前类的虚函数。 3. 动态绑定过程
调用步骤 通过对象指针/引用找到vptr。通过vptr定位到虚函数表。根据函数在表中的偏移量获取实际函数地址。执行该地址对应的函数。 示例class Animal {
public:virtual void speak() { cout Animal sound endl; }
};class Dog : public Animal {
public:void speak() override { cout Woof! endl; }
};Animal* animal new Dog();
animal-speak(); // 输出 Woof!animal-speak()运行时通过Dog对象的vptr找到Dog的虚函数表调用Dog::speak()。 4. 关键特性与注意事项
虚析构函数若通过基类指针删除派生类对象基类析构函数必须为虚函数以确保正确调用派生类析构函数避免资源泄漏。性能开销 空间每个对象需存储vptr通常4/8字节。时间虚函数调用需两次间接寻址访问vptr→查表→跳转略慢于非虚函数。 覆盖控制 overrideC11显式标记重写编译器检查签名一致性。final禁止进一步重写或继承。 5. 总结
C多态通过虚函数表和虚函数指针实现动态绑定
编译时生成虚函数表记录虚函数地址。运行时通过对象的vptr查找虚函数表确定实际调用的函数。
这种机制在保持高效性的同时提供了灵活的运行时多态支持是C面向对象设计的核心基石之一。
类中有虚函数析构函数应该注意什么什么情况下会触发虚函数
在C中当类包含虚函数时析构函数的设计和虚函数的触发条件是保证多态行为正确性的关键。以下是详细解答 一、虚函数与析构函数的注意事项
1. 虚析构函数必须存在 问题背景 若通过基类指针删除一个派生类对象而基类析构函数未声明为虚函数会导致派生类的析构函数未被调用引发资源泄漏如内存、文件句柄未释放。 正确做法 class Base {
public:virtual ~Base() {} // 虚析构函数virtual void foo() 0;
};class Derived : public Base {
public:~Derived() { /* 释放派生类资源 */ }void foo() override { /* 实现 */ }
};Base* obj new Derived();
delete obj; // 正确调用 Derived::~Derived() 和 Base::~Base()基类析构函数必须为虚函数确保通过基类指针删除对象时派生类析构逻辑被执行。
2. 构造函数和析构函数中避免调用虚函数 问题 在构造函数和析构函数中调用虚函数时动态绑定失效实际调用的是当前类的版本而非派生类重写的版本。 构造函数执行时派生类尚未初始化虚函数表vtable指向当前类的虚函数。析构函数执行时派生类已部分销毁虚函数表可能已恢复为基类版本。 示例 class Base {
public:Base() { callFoo(); } // 危险调用 Base::foo()virtual void foo() { cout Base::foo endl; }void callFoo() { foo(); }
};class Derived : public Base {
public:void foo() override { cout Derived::foo endl; }
};Derived d; // 输出 Base::foo而非 Derived::foo二、虚函数的触发条件
虚函数的动态绑定多态仅在以下场景生效
1. 通过指针或引用调用虚函数
动态绑定触发条件Base* ptr new Derived();
ptr-foo(); // 调用 Derived::foo()Base ref *ptr;
ref.foo(); // 调用 Derived::foo()若直接通过对象实例调用虚函数静态绑定生效无多态行为Derived d;
d.foo(); // 调用 Derived::foo()
Base b d;
b.foo(); // 调用 Base::foo()对象切片无多态2. 虚函数未被隐藏或覆盖
函数签名必须一致 派生类的虚函数需与基类虚函数名称、参数、返回类型完全一致协变返回类型除外。 使用 override 关键字C11可让编译器检查重写是否合法class Derived : public Base {
public:void foo() override { /* ... */ } // 显式标记重写
};3. 虚函数未被final禁止重写
若基类虚函数被声明为 final派生类无法重写class Base {
public:virtual void foo() final {} // 禁止派生类重写
};三、总结
关键点 虚析构函数 基类的析构函数必须为虚函数确保多态删除对象时资源正确释放。若类可能被继承且需通过基类指针操作对象析构函数应声明为虚函数。 虚函数触发条件 通过基类指针或引用调用虚函数时触发动态绑定。构造函数和析构函数中调用虚函数可能导致非预期行为。 设计建议 若类含虚函数析构函数应始终为虚函数除非明确禁止继承。使用 override 和 final 增强代码可读性和安全性。
示例验证
#include iostream
using namespace std;class Animal {
public:virtual ~Animal() { cout ~Animal() endl; }virtual void speak() { cout Animal sound endl; }
};class Dog : public Animal {
public:~Dog() { cout ~Dog() endl; }void speak() override { cout Woof! endl; }
};int main() {Animal* animal new Dog();animal-speak(); // 输出 Woof!delete animal; // 正确调用 ~Dog() 和 ~Animal()return 0;
}输出
Woof!
~Dog()
~Animal()通过合理使用虚析构函数和虚函数可以确保C多态行为的正确性和资源管理的安全性。
指针和引用的区别
在C中指针Pointer和引用Reference都是用于间接访问数据的工具但它们在语义、用法和底层实现上存在显著差异。以下是它们的核心区别及适用场景 1. 定义与本质
特性指针引用本质存储变量地址的变量独立实体变量的别名与原变量共享内存地址初始化要求可以不初始化但建议初始化为nullptr必须初始化且绑定后不可更改指向对象空值Null可以指向空地址nullptr不能为空必须绑定有效对象
示例代码
int a 10;// 指针
int* p a; // 合法
int* p2; // 合法但未初始化危险
p2 nullptr; // 合法// 引用
int r a; // 合法
int r2; // 编译错误引用必须初始化2. 操作与功能
特性指针引用重新赋值可修改指向其他对象一旦绑定无法更改始终指向初始对象内存操作支持指针算术如p、p 1不支持算术运算仅是别名多级间接访问支持多级指针如int** pp不支持多级引用但可通过typedef间接实现解引用方式需显式使用*如*p 20直接使用原变量名如r 20
示例代码
int arr[3] {1, 2, 3};// 指针算术
int* p arr;
p; // 指向arr[1]
*p 20; // arr[1] 20// 引用无法重新绑定
int x 5, y 10;
int ref x;
ref y; // 实际是x yref仍绑定x而非y3. 参数传递与语义
场景指针引用函数参数显式传递地址需检查空值隐式传递别名语法更简洁无需空值检查函数重载void func(int*)和void func(int)视为不同函数返回值可以返回指针如动态分配的对象可以返回引用如避免对象拷贝
示例代码
// 指针参数
void swap_ptr(int* a, int* b) {if (a b) { // 需检查空指针int tmp *a;*a *b;*b tmp;}
}// 引用参数更安全
void swap_ref(int a, int b) {int tmp a;a b;b tmp;
}4. 内存管理
特性指针引用动态内存可指向堆内存需手动new/delete通常绑定栈内存生命周期由作用域管理资源所有权可能涉及资源所有权转移需谨慎管理不拥有资源仅作为别名
示例代码
// 指针管理动态内存
int* p new int(10);
delete p; // 必须手动释放// 引用绑定栈对象
int x 10;
int r x; // 无需释放5. 底层实现
指针直接对应内存地址操作透明如mov指令操作地址值。引用通常由编译器实现为“自动解引用的指针”但在语法层面隐藏了地址操作。 总结选择指针还是引用
场景推荐选择原因可选参数允许空值指针如nullptr表示无数据引用无法表示空值函数内需重新绑定对象指针引用一旦绑定不可修改避免拷贝大对象引用传递别名而非副本高效实现多态指针或引用均可虚函数通过引用/指针触发多态资源管理如动态内存指针配合RAII或智能指针引用不拥有资源
核心原则
优先使用引用在参数传递、返回值优化等场景中引用更安全、简洁。必须使用指针当需要处理动态内存、可选参数或多级间接访问时。
什么是深拷贝和浅拷贝
在编程中深拷贝Deep Copy和浅拷贝Shallow Copy是两种不同的对象拷贝方式主要区别在于对资源所有权和内存管理的处理方式。以下是它们的核心区别及实际应用场景 1. 浅拷贝Shallow Copy 定义 仅复制对象的成员值包括指针的值而不复制指针指向的实际数据。 拷贝后的对象与原对象的指针成员指向同一块内存。 特点 速度快仅复制指针地址不涉及内存分配和数据复制。潜在风险多个对象共享同一资源可能导致悬空指针、双重释放等问题。 C中的默认行为 class ShallowExample {int* data;
public:// 默认拷贝构造函数和赋值运算符执行浅拷贝ShallowExample(const ShallowExample other) : data(other.data) {}
};示例问题
ShallowExample obj1;
ShallowExample obj2 obj1; // 浅拷贝
// obj1和obj2的data指向同一内存
delete obj1.data; // 释放内存
obj2.data[0] 10; // 危险obj2.data已是悬空指针2. 深拷贝Deep Copy 定义 不仅复制对象的成员值还会为指针成员分配新内存并复制原指针指向的所有数据。 拷贝后的对象与原对象完全独立资源互不影响。 特点 安全性高资源独立避免共享冲突。速度较慢需要分配内存并复制数据。 实现方式 class DeepExample {int* data;size_t size;
public:// 自定义深拷贝构造函数DeepExample(const DeepExample other) {size other.size;data new int[size]; // 分配新内存memcpy(data, other.data, size * sizeof(int)); // 复制数据}// 深拷贝赋值运算符DeepExample operator(const DeepExample other) {if (this ! other) { // 防止自赋值delete[] data; // 释放旧资源size other.size;data new int[size]; // 分配新内存memcpy(data, other.data, size * sizeof(int));}return *this;}~DeepExample() { delete[] data; }
};正确使用
DeepExample obj1;
DeepExample obj2 obj1; // 深拷贝obj2.data是独立内存块
delete obj1.data; // 不影响obj2.data
obj2.data[0] 10; // 安全3. 关键区别总结
特性浅拷贝深拷贝资源所有权共享资源多个对象指向同一内存独立资源每个对象拥有自己的内存副本内存操作不分配新内存仅复制指针值分配新内存并复制数据性能快慢取决于数据大小安全性低需手动管理共享资源高资源隔离避免冲突适用场景只读共享数据、性能敏感且无资源所有权的场景需独立管理资源的场景如动态数组、字符串等 4. 何时需要深拷贝
对象管理资源时 若类包含指针成员且该指针指向动态分配的内存如数组、文件句柄、网络连接等必须实现深拷贝。遵循“三法则” 在C中如果类需要自定义析构函数通常也需要自定义拷贝构造函数和拷贝赋值运算符即深拷贝逻辑。 5. 其他语言的实现
Java/Python 默认的赋值和拷贝如clone()或copy.copy()通常是浅拷贝。深拷贝需手动实现如Java实现Cloneable接口Python使用copy.deepcopy()。 JavaScript 对象展开符{...obj}或Object.assign()是浅拷贝。深拷贝需递归复制或使用JSON.parse(JSON.stringify(obj))局限性无法处理函数、循环引用。 6. 总结
浅拷贝适合轻量级、无需资源隔离的场景但需谨慎管理共享资源。深拷贝确保资源独立性是管理动态内存或独占资源的必要手段。在C中深拷贝需手动实现在高级语言中如Python可依赖内置方法但仍需理解底层逻辑以避免陷阱。
C中opencv的mat是怎么深拷贝和浅拷贝的
说一下快排二分查找
快速排序Quick Sort
算法思想 快速排序采用分治策略通过选择一个基准元素将数组分成两部分使左边元素均小于基准右边元素均大于基准然后递归地对子数组排序。
步骤详解 选择基准Pivot 通常可选第一个元素、最后一个元素、中间元素或随机元素作为基准。优化方法如三数取中法选首、中、尾的中位数可减少最坏情况概率。 分区Partition 重新排列数组使小于基准的元素位于左侧大于基准的位于右侧。最终基准元素的位置即为分区点。 双指针法i 左边界 - 1
pivot 右边界元素
for j from 左边界 to 右边界-1:if arr[j] pivot:i 1swap arr[i] 和 arr[j]
swap arr[i1] 和 arr[右边界]
return i1递归排序 对分区后的左右子数组重复上述步骤直到子数组长度为1。
时间复杂度
平均(O(n \log n))最坏已排序数组固定基准(O(n^2))优化后随机基准接近平均情况
代码示例
#include iostream
#include vector
using namespace std;// 分区函数将数组分为左右两部分返回基准元素的正确索引
int partition(vectorint arr, int low, int high) {int pivot arr[high]; // 选择最后一个元素作为基准int i low - 1; // i标记比基准小的元素的右边界// 遍历数组将小于等于基准的元素交换到左侧for (int j low; j high; j) {if (arr[j] pivot) {i;swap(arr[i], arr[j]);}}// 将基准元素放到正确的位置i1swap(arr[i 1], arr[high]);return i 1;
}// 快速排序递归函数
void quickSort(vectorint arr, int low, int high) {if (low high) {int pi partition(arr, low, high); // 获取基准位置quickSort(arr, low, pi - 1); // 递归排序左半部分quickSort(arr, pi 1, high); // 递归排序右半部分}
}// 测试快速排序
void testQuickSort() {vectorint arr {10, 7, 8, 9, 1, 5};cout 原始数组: ;for (int num : arr) cout num ;cout endl;quickSort(arr, 0, arr.size() - 1);cout 排序后数组: ;for (int num : arr) cout num ;cout endl;
}二分查找Binary Search
算法思想 在有序数组中通过不断将搜索区间对半分快速定位目标值的位置。
步骤详解 初始化边界 左边界 left 0右边界 right len(arr) - 1。 循环查找 计算中间索引 mid left (right - left) // 2避免整数溢出。若 arr[mid] target返回 mid。若 arr[mid] target调整左边界 left mid 1。否则调整右边界 right mid - 1。 终止条件 当 left right 时表示目标不存在返回 -1。
时间复杂度
(O(\log n))每次将搜索范围缩小一半。
代码示例
#include iostream
#include vector
using namespace std;// 二分查找函数返回目标索引未找到返回-1
int binarySearch(const vectorint arr, int target) {int left 0;int right arr.size() - 1;while (left right) {int mid left (right - left) / 2; // 避免整数溢出if (arr[mid] target) {return mid;} else if (arr[mid] target) {left mid 1; // 目标在右半部分} else {right mid - 1; // 目标在左半部分}}return -1;
}// 测试二分查找
void testBinarySearch() {vectorint arr {1, 3, 5, 7, 9};int target 5;cout 有序数组: ;for (int num : arr) cout num ;cout endl;int index binarySearch(arr, target);if (index ! -1) {cout 目标 target 的索引为: index endl;} else {cout 目标 target 未找到 endl;}
}对比总结
算法应用场景时间复杂度核心操作关键点快速排序大规模数据排序平均 (O(n \log n))分区与递归基准选择、原地排序二分查找有序数据查询(O(\log n))折半比较边界处理、有序前提
注意事项
快速排序需注意基准选择优化以避免最坏情况。二分查找必须确保输入数组有序且正确处理中间值计算防止溢出。
char a -1在计算机内存中是怎么存储的
在C/C中char a -1; 在内存中的存储方式如下
1. 符号性决定存储形式 char的符号性 char的默认符号性由编译器决定 有符号signed char范围是 -128 到 1278位补码。无符号unsigned char范围是 0 到 2558位纯二进制。 大多数编译器如GCC/x86默认 char 为 有符号但某些平台如ARM可能默认为无符号。 需通过编译选项或显式声明signed char/unsigned char明确符号性。 2. 存储过程
情况1有符号charsigned char
补码表示 -1 的补码形式为 11111111二进制即 0xFF十六进制。 补码规则绝对值取反后加1。1的二进制00000001
取反11111110
加111111111 → 即-1的补码内存存储 a 的内存占用1字节8位直接写入 0xFF。
情况2无符号charunsigned char
溢出处理 若编译器默认 char 为无符号-1 会被隐式转换为 模运算结果-1 mod 256 255 → 二进制 11111111 (0xFF)内存存储 同样存入 0xFF但程序逻辑上将其视为 255。 3. 验证代码
#include stdio.hint main() {char a -1;unsigned char *p (unsigned char*)a;printf(内存值十六进制: 0x%02X\n, *p); // 输出 0xFFreturn 0;
}无论 char 的默认符号性如何输出均为 0xFF证明内存中存储的是 11111111。 4. 总结
关键点说明内存存储值固定为 11111111二进制或 0xFF十六进制符号性影响有符号char解释为 -1无符号char解释为 255编译器差异依赖默认符号性建议显式声明 signed char 或 unsigned char 避免歧义补码机制负数的存储通过补码实现保证运算一致性 5. 扩展补码的优势
统一加减法补码允许使用同一套电路处理加减法无需区分符号位。零的唯一性补码中 0 仅有 00000000 一种表示无 0 和 -0 歧义。范围对称性8位有符号整数范围为 -128 到 127充分利用所有位组合。
通过上述分析char a -1; 的内存存储始终为 0xFF其符号性由编译器决定值的解释方式。
C实现sharedPtr手写C的string类
对cache的理解如何提高缓存命中率
数据结构中顺序存储和链式存储的优缺点
大疆车载面经
网上的参考
自我介绍介绍简历项目
围绕项目问涉及的知识点
问了很多DSP的内容(项目相关)
回顾你做的项目你觉得有哪些地方可以改进有哪些挑战难忘的经历
说说你对大疆车载的了解反问
优化手段有哪些
模型推理加速是怎么整的
量化部署过程
gpu加速有什么好处原理是什么
cuda相关
多线程如何使用痛点是什么
如何做同步同步的手段有哪些区别是什么分别用在什么场景
内存优化有哪些手段dma相关的
cache cpu 内存之间的联系
高性能面试问题
C static的作用修饰成员变量成员函数。static全局变量和普通变量的区别。 怎么只在堆上创建构造函数 右值引用 锁 lambda表达式 move forward 编译的过程 动态库和静态库的区别 new 和 malloc的区别 new的底层实现 拷贝构造函数是传值还是引用为什么要传引用。 线程安全的单例模式 智能指针shared_ptr是不是线程安全的 vector的扩容机制为什么要2倍扩容 C内存模型 为什么构造函数不能是虚函数为什么析构函数是虚函数 线程间共享内存什么时候用到条件变量什么时候用到锁 有什么区别 死锁条件如何避免lock_gard unquie_lock 区别 C怎么调用c语言封装的函数 多态实现原理虚函数表存的位置注意别忘了模板多态模板的偏特化 进程和线程的区别 大端小端 怎么判断两种方法
高性能相关 opencl的运行流程 GPU架构 GPU全局内存和局部内存区别 怎么更好利用局部内存 Cache原理 如何提高cache命中率 通常优化的思路 写opencl为什么要减少分支掩码 opencl 写kernel的主要参数有哪些 计算密集型和访存密集型的区别 可分离卷积在GPU上为什么慢为什么是访存密集型 算子融合 convBN 融合的公式为什么可以融合 推理框架中卷积的实现有哪些 时间局部性和空间局部性 产生bank confict的原因和解决方法 TVM opencl 实现矩阵乘法 向量求和