C++面试高频(二)

1.知道动态链接与静态链接吗?两者有什么区别

动态链接和静态链接的区别:

动态链接:

  • 在程序运行时进行链接,加载共享库文件。
  • 节省空间,可多个程序共享库文件。
  • 灵活性高,可以动态加载不同版本的库文件。
  • 维护方便,只需更新库文件本身。

静态链接:

  • 在编译时进行链接,将库函数复制到可执行文件中。
  • 独立的可执行文件,不依赖外部库文件。
  • 可执行文件较大,可能会有冗余代码。
  • 维护复杂,更新库函数需重新编译和分发可执行文件。

使用场景和优缺点:

  • 动态链接适合节省资源、可升级和灵活性要求高的场景。但在运行时有一定开销。
  • 静态链接适合独立部署和简化依赖关系的场景。但可执行文件较大且维护复杂。

2.C++编译时和C有什么不同,在c++中怎么用c?

C++与C在编译时的主要区别有以下几点:

  1. 由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般只包括函数名
  2. 语法和功能:C++相比C具有更多的语法和功能。C++引入了面向对象编程的概念,包括类、继承、多态等。此外,C++还提供了更多的库和工具,如标准模板库(STL)和异常处理机制等。
  3. 兼容性:C++是C的超集,这意味着C的源代码可以直接在C++中编译和运行。C++编译器会自动识别和处理C的语法,因此可以使用C代码编写的功能和库。

在C++中使用C代码有多种方式,其中常见的几种方式包括:

使用extern "C"进行函数声明:在C++中,使用extern "C"修饰C代码的函数声明,以告诉编译器使用C的名称重载规则。

extern "C" {
    // C函数声明
    int add(int a, int b);
}

在C++中包含C的头文件:在C++源文件中直接包含C的头文件,即#include "my_c_code.h",然后直接使用其中声明的C函数和数据结构。

#include "my_c_code.h"

int main() {
    int result = add(3, 4);  // 调用C函数
    return 0;
}

使用#ifdef __cplusplus进行条件编译:在C的头文件中使用条件编译,根据__cplusplus宏定义来区分C和C++环境,在C++环境下使用extern "C"修饰C函数声明。

#ifdef __cplusplus
extern "C" {
#endif

// C函数声明
int add(int a, int b);

#ifdef __cplusplus
}
#endif

3.为什么要少使用宏?C++有什么解决方案?

在C++中,推荐尽量避免过多使用宏的原因有以下几点:

  1. 可读性差:宏通常使用简单的文本替换机制,在代码中展开为复杂的表达式或语句,导致代码可读性降低。
  2. 潜在的副作用:宏的使用可能导致潜在的副作用,比如多次求值、修改变量等,这可能导致意外行为和错误。
  3. 缺乏类型检查:宏不进行类型检查,因此在使用宏时需要自行确保类型匹配,否则可能导致运行时错误。

为了解决这些问题,C++提供了一些替代方案来减少宏的使用:

  1. 使用const和constexpr:C++中的const和constexpr关键字可以用于定义常量,避免了宏定义常量的麻烦,并且提供了类型安全和编译期计算的优势。
  2. 使用内联函数:C++的内联函数可以取代宏,以提高代码的可读性和类型安全性。内联函数在编译时展开,避免了宏带来的副作用和类型不匹配的问题。
  3. 使用模板:模板是C++的强大特性之一,可以实现类型安全的泛型编程。通过模板,可以避免使用宏进行代码的泛化。

4.请你说说内联函数,为什么使用内联函数?需要注意什么?

内联函数是指在函数声明前加上inline关键字的函数,它的作用是告诉编译器在调用函数的地方直接将函数体插入,而不是通过函数调用的方式执行。使用内联函数可以提高代码的执行效率,减少函数调用的开销。 使用内联函数的主要目的是减少函数调用的开销,因为函数的调用会涉及栈帧的创建和销毁、参数传递等操作,而将函数体直接插入调用点则无需进行这些操作。

需要注意的点:

  • 内联函数适用于函数体简单、调用频繁的情况。如果函数体较大或调用频率较低,使用内联函数可能会导致代码膨胀,产生更多的代码复制,甚至可能导致性能下降。
  • 内联函数的声明通常放在头文件中,因此需要注意内联函数的定义和声明应该一致,遵循内联函数的定义规则,在同一个编译单元中只能有一个定义。
  • 虚函数不能使用内联函数,因为虚函数的调用是通过虚表进行的,无法在编译时确定调用的具体函数。
  • 5.简述C++从代码到可执行二进制文件的过程

  • 预处理(Preprocessing):预处理器(如cpp)会处理源代码文件,展开宏定义、处理条件编译指令等,并生成一个被称为"翻译单元"(translation unit)的中间文件。
  • 编译(Compilation):编译器(如gcc、clang等)将预处理产生的翻译单元转化为汇编代码(Assembly Code)。这个阶段将源代码的高级语言表示转化为底层的汇编指令。
  • 汇编(Assembly):汇编器(如as)将汇编代码转化为机器码的目标文件(Object File)。目标文件中包含了可执行程序的机器指令,但还没有进行最终的链接。
  • 链接(Linking):链接器(如ld)将目标文件与所需的库文件进行链接,解析符号引用,生成最终的可执行二进制文件。这个阶段还包括地址分配、符号解析、重定位等操作,确保不同目标文件之间的引用关系正确。
  • 6.继承和虚继承

    继承是面向对象编程中的一个重要概念,它允许一个类(派生类或子类)继承另一个类(基类或父类)的属性和方法。通过继承,派生类可以重用基类的代码,并可以在此基础上进行扩展和修改。

    继承可以通过以下方式进行:

    公有继承(public inheritance):使用public关键字来指定基类与派生类之间的继承关系。公有继承表示派生类可以访问基类的公有成员。例如:

    class Base {
    public:
        int x;
    };
    
    class Derived : public Base {
    public:
        int y;
    };
    在这个例子中,Derived类公有继承了Base类,因此它可以访问Base类中的公有成员变量x。
    
    

    保护继承(protected inheritance):使用protected关键字来指定基类与派生类之间的继承关系。保护继承表示派生类可以访问基类的公有和保护成员。例如:

    class Base {
    protected:
        int x;
    };
    
    class Derived : protected Base {
    public:
        int y;
    };
    在这个例子中,Derived类保护继承了Base类,因此它可以访问Base类中的公有和保护成员变量x。
    

    私有继承(private inheritance):使用private关键字来指定基类与派生类之间的继承关系。私有继承表示派生类可以访问基类的公有和保护成员。例如:

    class Base {
    private:
        int x;
    };
    
    class Derived :

    剩余60%内容,订阅专栏后可继续查看/也可单篇购买

    c++/嵌入式面经专栏 文章被收录于专栏

    本人2022年毕业于山东大学,目前就职国内某芯片厂。打算把之前校招时做的笔记通过专栏发出来,本专栏适合于C/C++、嵌入式方向就业的同学,本篇面经总结数千篇面经的知识集合,实时更新全网最新的嵌入式/C++最新内容,囊括了C语言、C++、操作系统、计算机网络、嵌入式、算法与数据结构、数据库等一系列知识点,在我看来这些是求职者在面试中必须掌握的知识点。最后呢祝各位能找到自己合适的工作。

    全部评论
    打卡
    1
    送花
    回复
    分享
    发布于 02-25 10:53 四川
    刚面试完就来了,前排
    点赞
    送花
    回复
    分享
    发布于 2023-10-08 16:57 上海
    秋招专场
    校招火热招聘中
    官网直投
    打卡。。。
    点赞
    送花
    回复
    分享
    发布于 01-06 08:28 江苏

    相关推荐

    17 131 评论
    分享
    牛客网
    牛客企业服务