文档教程知识库【免费下载链接】InterviewGuide「InterviewGuide」是阿秀从校园-职场多年计算机自学过程的记录以及学弟学妹们计算机校招秋招经验总结文章的汇总包括但不限于C/C 、Golang、JavaScript、Vue、操作系统、数据结构、计算机网络、MySQL、Redis等学习总结坚持学习持续成长项目地址https://gitcode.com/forthespada/InterviewGuide点击查看免费下载本文基于 InterviewGuide 仓库「校招八股文」C 基础语法系列中的 01-01-06-basic第 101120 题整理扩写覆盖程序入口与内存布局、volatile、空类默认成员、string 与 char 指针互转、指针/引用传参取舍、静态绑定与动态绑定、对象计数设计、成员初始化列表、strcpy/sprintf/memcpy/strncpy 差异、Debug/Release 等 20 个高频 C 面试问题。读完本文你可以按题号快速回顾每个知识点的标准回答思路与代码级依据并结合系列中内存篇、C11 篇、多态篇的配套文章形成完整的 C 基础知识体系从容应对校招与社招的 C 基础语法轮。本篇 20 题速览题号主题一句话考点101main(int argc, char *argv[])内存结构命令行参数的存放方式102volatile 关键字阻止编译器优化、内存重读103空类默认函数构造/拷贝构造/析构/赋值104C 标准库构成标准函数库 面向对象类库105const char*与string互转六种转换关系的写法106指针参数 vs 引用参数按对象类型选择传参方式107静态绑定与动态绑定静态类型/动态类型决定绑定时机108计算单个类对象个数static 计数 构造/拷贝/赋值/析构109快速定位错误位置错误列表双击、模板错误看最靠后引用110成员初始化列表使用场景与“按声明顺序初始化”111引用/值传参与返回值好处与三条引用返回值限制112strcpy/sprintf/memcpy 区别操作对象、效率、功能三维度113引用作函数参数的好处无副本、免解引用、可读性好114数组与指针的区别连续内存、sizeof、退化为首地址115阻止类被实例化抽象类 / private 构造函数116禁止生成拷贝构造函数重写、private 只声明不定义、基类方案117Debug 与 Release 区别调试信息、优化、.pdb 文件118main 返回值int 返回给操作系统表示退出状态119比较大小的函数模板隐式类型转换的坑120strcpy 与 strncpy边界检查与缓冲区溢出一、程序入口与内存模型101、程序执行int main(int argc, char *argv[])时的内存结构你了解吗这是面试中考察命令行参数机制的经典题回答要点是argc命令行参数的个数argv字符串数组每个元素是char*指向一个以\0结尾的参数串依次存放在 argv 指向的指针数组中第一个参数argv[0]约定为程序自身的名称可执行文件名其余才是用户真正传入的参数。从 C 语言标准看main的两个等价原型是int main(void)与int main(int argc, char *argv[])本文档第 118 题进一步强调main的返回值必须是int以便把退出状态传递给程序激活者操作系统。也就是说进程启动时运行环境CRT 启动代码会把命令行参数解析好后装进栈上区域再调用main程序结束后main的int返回值即成为进程退出码。想深入了解进程启动时各段内存代码区、全局/静态存储区、堆、栈的划分可继续读系列的 01-02-01-memory 中“C 的内存分区”一节。102、volatile 关键字的作用volatile是一种类型修饰符用它声明的变量表示可能被编译器未知的因素更改例如操作系统、硬件或其他线程。遇到volatile声明的变量编译器对访问该变量的代码不再进行优化从而提供对特殊地址的稳定访问声明语法int volatile vInt;每次读取volatile变量的值时系统总是重新从它所在的内存位置读取数据即使前面的指令刚刚读取过读取到的数据也立刻被保存回去不会被缓存在寄存器中。原文档总结了volatile最典型的三个使用场景中断服务程序中修改的、供其它程序检测的变量需要加volatile多任务环境下各任务间共享的标志应该加volatile存储器映射的硬件寄存器通常也要加volatile说明因为每次对它的读写都可能具有不同意义。需要与const区分开const约束的是“程序员不能改”volatile约束的是“编译器不能假设它不变”。在单线程纯软件代码中volatile并不提供线程同步语义跨线程共享变量应使用原子类型或内存序等并发工具可结合 01-03-01-C11 中 C11 新特性扩展回答。二、类的默认成员与对象生命周期103、如果有一个空类它会默认添加哪些函数对任何类只要用户没有显式定义编译器都会按规则隐式生成成员函数。一个空类Empty至少拥有四个由编译器生成的特殊成员函数1) Empty(); // 缺省构造函数 2) Empty(const Empty); // 拷贝构造函数 3) ~Empty(); // 析构函数 4) Empty operator(const Empty); // 赋值运算符补充两个高频延伸点面试常被追问空类对象的sizeof通常为 1因为每个对象需要独立的地址但当空类作为基类时占用的大小为 0这一点在 01-02-01-memory 的“类的对象存储空间”一节有明确记载若类中定义了虚函数编译器还会为对象额外生成一个指向虚函数表vtable的指针虚表机制的细节见 01-05-01-other 的多态原理部分。108、如何设计一个类计算仅单个子类的对象个数原文档勘误说明微信好友“铁锤哥哥”勘误——“计算子类个数” - 计算仅单个子类的对象个数2021.06.28。原文给出的六步设计非常适合作为“static 成员 特殊成员函数协作”的标准答法为类设计一个static静态变量count作为计数器类定义结束后初始化count在构造函数中对count进行1设计拷贝构造函数拷贝时同样count 1拷贝产生了新对象必须计数设计赋值运算符在赋值函数中按原文逻辑对count做相应调整注意自赋值与目标对象原本占用的计数工程实现中需要谨慎处理避免重复计数在析构函数中对count进行-1。class Widget { public: Widget(); // count 1; Widget(const Widget); // count 1; Widget operator(const Widget); // 按需维护 count ~Widget(); // count - 1; static int count(); // 返回当前存活对象数 private: static int s_count; // 所有对象共享的计数器 };这个题的本质是考察“对象生命周期事件构造/拷贝构造/赋值/析构与静态数据成员”的组合运用与 RAII 思想一脉相承——01-04-01-STL 中讲的“资源获取即初始化”同样是依赖构造/析构成对出现的机制。三、C 标准库体系104、C 中标准库是什么原文档把 C 标准库拆成两部分这个分层回答法值得直接继承第一部分标准函数库—— 由通用的、独立的、不属于任何类的函数组成函数库继承自 C 语言按功能又可细分为输入/输出 I/O字符串和字符处理数学时间、日期和本地化动态分配宽字符函数等。第二部分面向对象类库—— 类及其相关函数的集合包括标准的 C I/O 类iostream系列String类数值类STL 容器类、STL 算法、STL 函数对象、STL 迭代器、STL 分配器本地化库异常处理类杂项支持库。面试中能把“STL 五要素容器/算法/函数对象/迭代器/分配器”与标准库结构对应起来说明理解到位。STL 容器与迭代器的深入内容见 01-04-02-STL。四、指针、引用与传参策略105、const char*与string之间的关系是什么原文档先给出关系定位string是 C 标准库封装字符串操作的类实际操作过程中可以用const char*给string类初始化。然后给出六组完整转换关系建议全部掌握a) string 转 const char* string s abc; const char* c_s s.c_str(); b) const char* 转 string直接赋值即可 const char* c_s abc; string s(c_s); c) string 转 char* string s abc; char* c; const int len s.length(); c new char[len 1]; strcpy(c, s.c_str()); d) char* 转 string char* c abc; string s(c); e) const char* 转 char* const char* cpc abc; char* pc new char[strlen(cpc) 1]; strcpy(pc, cpc); f) char* 转 const char*直接赋值即可 char* pc abc; const char* cpc pc;两个易错点需要强调s.c_str()返回的const char*指向string内部缓冲其生命周期与string对象绑定string销毁后指针即失效string转char*c 组必须手动拷贝并new出独立内存因为 C 风格的字符串接口要求可写缓冲对应的delete[]也不能遗漏。106、什么情况用指针当参数什么时候用引用为什么原文档给出的完整决策树分“只读使用”与“需要修改”两类建议按对象类型机械套用对于使用引用值而不做修改的函数数据对象很小内置类型或小型结构按值传递数据对象是数组使用指针并且指针应声明为指向const的指针数据对象是较大的结构使用const指针或const引用节省拷贝的时间和空间数据对象是类对象使用const引用传递类对象参数的标准方式是按引用传递。对于修改函数中数据的函数数据是内置数据类型使用指针数据对象是结构使用引用或指针数据是类对象使用引用。原文还特别纠正了一个流传甚广的错误说法——“数组只能使用指针传递”并给出了反例templatetypename T, int N void func(T (a)[N]) { a[0] 2; } int main() { int a[] { 1, 2, 3 }; func(a); cout a[0] endl; return 0; }T (a)[N]是“数组的引用”借助模板参数推导出了元素类型与数组长度证明定长数组同样可以按引用传递。这类“能指出八股文中的不严谨之处”正是面试加分项。113、将引用作为函数参数有哪些好处与第 106 题形成呼应这里从“引用 vs 普通值传递 vs 指针”三个角度对比传递引用与传递指针效果等价形参成为实参变量或对象的别名被调函数中对形参的操作就是对主调函数中目标对象的操作不产生实参副本使用引用传参内存中不会产生实参的副本直接对实参操作而按普通变量传参时需要给形参分配存储单元形参是实参的副本如果传递的是对象还会调用拷贝构造函数。因此参数较大时引用的效率和空间开销都优于值传递相比指针更易用更安全指针传参虽效果相同但被调函数中同样要给形参分配存储单元且需要反复使用*指针变量名的形式容易出错、可读性差而主调函数调用点还必须显式取变量地址。引用则语法清晰、无需取址。111、函数参数与返回值使用引用传递的好处与限制引用传参的好处与 113 题合并记忆函数内部可以对该参数进行修改提高函数调用和运行效率——省去传值和生成副本的时间与空间消耗。原文对值传递机制的解释很直白形参的作用域只在函数体内部实参与形参是两个不同的东西函数调用时通过“形参 实参”给形参赋值产生实参副本函数内部对参数的修改只作用于副本函数结束形参生命终结实参不受任何影响。用引用作为返回值最大的好处是在内存中不产生被返回值的副本。但有如下三条限制面试高频追问不能返回局部变量的引用——函数返回后局部变量即被销毁不能返回函数内部new分配内存的引用——虽然不存在被动销毁问题但如果返回的引用只作为临时变量出现、没有被赋给实际变量那么new的空间就无法释放造成memory leak可以返回类成员的引用但最好是const——若其他对象获得了该成员的非 const 引用对它的单纯赋值可能破坏业务规则的完整性。114、数组和指针的区别原文五条结论按“内存布局 → sizeof → 编译器实现 → 函数传参 → 下标访问”层层递进数组在内存中连续存放开辟一块连续内存空间sizeof(数组名)得到数组所占字节数数组元素个数 sizeof(数组名) / sizeof(数组元素类型)sizeof对指针无效sizeof(p)p 为指针得到的是指针变量本身的字节数而不是 p 所指内存的容量从源码结构看编译器为了简化数组支持实际上是用指针实现对数组的访问即把数组元素引用转换为“指针 偏移量”的寻址向函数传参时如果实参是数组接受的形参是对应的指针——传递的是数组首地址而不是整个数组这既“省事”也意味着函数内无法通过sizeof恢复数组长度这也是 106 题中数组传参默认用指针/指针化引用的深层原因下标访问形式相同原地址 下标偏移但数组名对应的首元素地址是固定的指针指向的地址可以改变。五、绑定机制与类型转换107、你知道静态绑定和动态绑定吗讲讲原文四条定义是标准骨架对象的静态类型对象在声明时采用的类型编译期确定对象的动态类型目前所指对象的类型运行期决定。动态类型可以更改而静态类型无法更改静态绑定绑定的是对象的静态类型某特性如函数调用依赖于对象的静态类型发生在编译期动态绑定绑定的是对象的动态类型某特性依赖于对象的动态类型发生在运行期。面试时的展开技巧给出一个“静态类型是 Base、动态类型是 Derived”的指针/引用场景指出只有public 继承 基类虚函数才会走动态绑定虚表分派其余情况内联、重载、普通成员函数都是静态绑定。多态与虚表的代码级分析见 01-05-01-other。116、如何禁止程序自动生成拷贝构造函数原文三层递进方案手动重写为了阻止编译器默认生成拷贝构造函数和拷贝赋值函数需要手动重写这两个函数某些情况下为避免它们被调用需将二者设置为privateprivate 且只声明不定义类内成员函数和friend函数仍可调用private函数如果该 private 函数只声明不定义一旦被调用就会产生连接错误链接期 undefined reference从而在编译链路上“禁绝”拷贝路径基类方案定义一个 base 类在 base 类中将拷贝构造函数和拷贝赋值函数设置为private。派生类中编译器将不会自动生成这两个函数且由于 base 类中它们是私有的派生类相关操作也被阻止。class CopyGuard { public: CopyGuard() {} ~CopyGuard() {} private: CopyGuard(const CopyGuard); // 只声明不定义 CopyGuard operator(const CopyGuard); };方案 3 的机制可以这样向面试官解释派生类的隐式拷贝构造/赋值需要调用基类的对应函数而基类版本是私有的因此派生类自身的隐式生成版本会因访问控制不可用编译器便不再为派生类自动生成任何尝试拷贝派生类对象的行为都会直接编译失败。115、如何阻止一个类被实例化有哪些方法原文两种标准方法将类定义为抽象基类至少含一个纯虚函数 0或将构造函数声明为private不允许类外部创建对象只能在类内部创建对象典型代表是单例模式构造私有化 类内getInstance()提供唯一入口。六、字符串函数与缓冲区安全112、说一说 strcpy、sprintf 与 memcpy 三个函数的不同之处原文从三个维度对比这个“三维对比”结构本身就是答题模板1) 操作对象不同strcpy的两个操作对象均为字符串sprintf的源对象可以是多种数据类型目的操作对象是字符串memcpy的两个对象是两个任意可操作的内存地址不限数据类型。2) 执行效率不同memcpy 最高strcpy 次之sprintf 效率最低sprintf要做格式化解析开销最大。3) 实现功能不同strcpy字符串变量间的拷贝sprintf其它数据类型到字符串的格式化转化memcpy内存块间的二进制拷贝。120、strcpy 函数和 strncpy 函数的区别哪个更安全函数原型char* strcpy(char* strDest, const char* strSrc) char* strncpy(char* dest, const char* src, size_t n)行为差异strcpy如果参数dest所指的内存空间不够大可能造成缓冲溢出buffer overflow编写程序时要特别留意可用strncpy取代strncpy复制源字符串的前n个字符src与dest所指的内存区域不能重叠且dest必须有足够空间放置n个字符。原文给出的三条strncpy边界规则目标长度 / 指定长度 n / 源长度三者关系目标长 指定长 n 源长将源串全部拷贝到目标自动加上\0指定长 n 源长只按n拷贝不追加\0这是strncpy最容易被忽略的坑需手动补\0指定长 n 目标长运行时错误越界。综合两条结论strncpy相对更安全——它引入了长度参数n把拷贝上界显式化避免了strcpy完全依赖源串终止符、无法感知目标容量的致命弱点但“安全”有前提必须保证dest容量不小于n并注意补终止符。七、编译产物与工程实践117、Debug 和 Release 的区别是什么原文三条要点调试版本Debug包含调试信息体积比 Release 大很多并且不做任何优化优化会使调试复杂化因为源代码与生成指令之间的关系变得更复杂便于程序员调试。Debug 模式下生成两个文件.exe或.dll以及一个.pdb文件记录断点等调试信息发布版本Release不对源代码进行调试编译时对应用程序速度进行优化使程序在代码大小和运行速度上都是最优的调试信息可放在单独的 PDB 文件中生成。Release 模式下只生成一个.exe或.dll文件本质认知Debug 和 Release 并没有本质界限它们只是一组编译选项的集合编译器只是按预定选项行动。甚至可以修改这些选项得到“优化过的调试版本”或“带跟踪语句的发布版本”。119、模板会写吗写一个比较大小的模板函数原文示例#includeiostream using namespace std; templatetypename type1, typename type2// 函数模板 type1 Max(type1 a, type2 b) { return a b ? a : b; } void main() { coutMax Max(5.5,a)endl; }注意示例中的两个历史写法细节模板参数名type1、type2与内置类型关键字type命名风格接近现代代码应写成typename T1, typename T2void main()是非标准写法标准 C 要求int main()。原文还特别指出该模板存在一个隐晦的 bug微信好友“江河”指出2021.06.28 采纳a、b 只有在能进行隐式类型转换时才能比较否则a b这一步会直接报错。例如Max(5.5, a)依赖的是 char 向 float/double 的隐式转换若传入两个没有共同比较语义的类型如自定义类之间未定义operator编译期即失败。此时往往需要对运算符进行重载代码量瞬间上升——这其实是在引出“比较概念应抽象为Compare(a, b)函数模板或 C11 的std::enable_if/auto推导约束”这一更现代的解法与 01-03-01-C11 中auto、decltype类型推导新特性正好衔接。118、main 函数的返回值有什么值得考究之处吗原文两条要点main()函数作为程序运行入口点返回值类型必须是int这样返回值才能传递给程序激活者如操作系统表示程序正常退出main(int args, char **argv)参数的处理一般会调用getopt()函数处理但实践中这只是部分场景不会经常用到。109、怎么快速定位错误出现的地方原文两条实操经验偏 IDEVisual Studio 系工程习惯简单错误直接双击错误列表里的错误项或点击“生成输出”窗口中错误信息里带行号的地方编辑窗口即可定位到错误位置复杂的模板错误最好使用生成输出窗口。多数情况下触发错误的位置是最靠后的引用位置如果这样仍无法确定就需要先把自己代码中的引用位置找出来然后逐个分析。模板实例化报错时“错误信息指向最外层调用点”是所有主流编译器MSVC/GCC/Clang的共同现象把“看最靠后的引用”作为模板错误的定位口诀非常实用。八、成员初始化列表110、成员初始化列表什么时候用到它的调用过程是什么四个典型使用场景原文当初始化一个引用成员变量时引用必须初始化不能在函数体内“赋值”初始化一个const成员变量时同样只能初始化不能赋值当调用基类的构造函数、而该构造函数拥有一组参数时当调用成员类的构造函数、而它拥有一组参数时。调用过程的关键规则编译器会逐一处理初始化列表以适当顺序在构造函数体内安插初始化操作且这一切发生在任何显式用户代码之前。列表中的项目顺序是由类中的成员声明顺序决定的而不是初始化列表中的排列顺序——即使你在列表里调整书写顺序实际初始化顺序也不变。这一规则直接决定了“成员之间不要互相依赖初始化”否则会踩出未定义行为。九、本篇知识点串联与系列延伸回顾本篇 20 题可以归纳为四条主线方便按线索串记对象模型线103/108/115/116/110空类默认四函数 → static 计数 → 阻止实例化/拷贝 → 成员初始化列表核心是特殊成员函数的生成规则与生命周期事件传参传递线106/113/111/114/101数组指针退化、引用别名机制、返回值三条限制、argv参数布局核心是“谁在栈上、谁在堆上、传过去的是什么”字符串安全线105/112/120string/char*六种互转、三个拷贝函数三维对比、strncpy边界规则核心是终止符与容量边界语言机制线102/107/117/118/119/109/104volatile 反优化、静态/动态绑定、Debug/Release 编译选项、main返回语义、模板类型转换坑、错误定位与标准库结构。按仓库的系列编号继续延伸前面三篇基础语法01-01-01-basic1-20 题、01-01-02-basic21-40 题、01-01-03-basic41-60 题、01-01-04-basic61-80 题、01-01-05-basic81-100 题紧接本篇的下一批121-140 题01-01-07-basic专题配套内存与内存池 01-02-01-memory、C11 新特性 01-03-01-C11、STL 01-04-02-STL、多态与虚表 01-05-01-other。建议的复习方式先按“对象模型 → 传参传递 → 字符串安全 → 语言机制”四线各挑 2-3 题做自测口述再回到对应代码示例空类四函数、数组引用模板、Max函数模板、strncpy三条边界规则手写一遍最后用本篇的速览表对照查漏即可把这一批 20 题从“读过”推进到“答得出、写得出”。赞分享文档教程知识库【免费下载链接】InterviewGuide「InterviewGuide」是阿秀从校园-职场多年计算机自学过程的记录以及学弟学妹们计算机校招秋招经验总结文章的汇总包括但不限于C/C 、Golang、JavaScript、Vue、操作系统、数据结构、计算机网络、MySQL、Redis等学习总结坚持学习持续成长项目地址https://gitcode.com/forthespada/InterviewGuide点击查看免费下载相关推荐InterviewGuide C基础语法面试要点精讲61-80内存泄漏、类型转换、函数调用栈与动态绑定机制InterviewGuide C基础语法面试要点精讲61 80内存泄漏、类型转换、函数调用栈与动态绑定机制 本文基于 InterviewGuide 仓文档教程知识库InterviewGuide C基础语法面试题精讲81-100组合与继承、函数指针、内存对齐、模板头文件、static 与异常处理InterviewGuide C基础语法面试题精讲81 100组合与继承、函数指针、内存对齐、模板头文件、static 与异常处理 本篇是 Inter文档教程知识库校招八股文精选C 基础语法高频面试题 21-40InterviewGuide 实战解析校招八股文精选C 基础语法高频面试题 21 40InterviewGuide 实战解析 本文是 InterviewGuide「阿秀的学习笔记」校招八股文档教程知识库上一篇在 Artillery 中复用 TypeScript 编写的 Playwright 测试代码下一篇agent-skills项目结构详解轻松理解技能组织与工作流设计创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考