一、C++ 基础

1.1 语言特性与区别

  1. C++ 与 C 的主要区别是什么?C++ 为何被称为 “带类的 C”?

    • 主要区别:C++ 引入了面向对象编程(OOP)特性(类、继承、多态等),而 C 是过程式编程语言;C++ 支持函数重载、模板、异常处理等高级特性,C 不支持。
    • 为何称为"带类的 C":早期 C++ 是在 C 语言基础上添加类和对象机制扩展而来,保留了 C 的大部分语法和功能,因此被称为"带类的 C"。
  2. C++ 的基本数据类型有哪些?它们的大小(32 位 / 64 位系统)分别是多少?

    • 基本数据类型:整数类型(char、short、int、long、long long)、浮点类型(float、double、long double)、布尔类型(bool)、空类型(void)。
    • 大小(32位/64位系统)
      • char:1字节
      • short:2字节
      • int:4字节
      • long:4字节(32位)/ 8字节(64位)
      • long long:8字节
      • float:4字节
      • double:8字节
      • bool:1字节
  3. 指针和引用的区别是什么?何时使用指针,何时使用引用?

    • 区别

      • 指针可以为空(nullptr),引用必须初始化且不能为null;
      • 指针可以重新指向其他对象,引用一旦绑定不能更改;
      • 指针需要解引用(*)访问对象,引用直接访问;
      • 指针有自己的内存空间,引用不占用额外内存(编译器处理为指针)。
    • 使用场景

      • 当需要表示"无对象"时使用指针(如返回可能失败的查找结果);
      • 当需要动态修改指向的对象时使用指针;
      • 其他情况优先使用引用(更安全、简洁)。
    • 代码示例

      int a = 10;
      int* p = &a;  // 指针
      int& r = a;   // 引用
      *p = 20;      // 解引用修改a的值
      r = 30;       // 直接修改a的值
      p = nullptr;  // 指针可以为空
      // r = nullptr;  // 错误:引用不能为null
      
  4. 结构体 struct 和共同体 union(联合)的区别是什么?

    • struct:各成员占用独立内存空间,总大小为各成员大小之和(考虑内存对齐)。

    • union:所有成员共享同一块内存空间,总大小为最大成员的大小。同一时间只能有一个成员有效。

    • 代码示例

      struct S { int a; char b; };  // 大小至少为 4+1=5字节(实际可能更大,取决于对齐)
      union U { int a; char b; };   // 大小为4字节(int的大小)
      
  5. struct 和 class 的区别?

    • 默认访问权限:struct默认public,class默认private。
    • 默认继承方式:struct默认public继承,class默认private继承。
    • 模板参数:class可用于定义模板参数(如template),struct不行(C++11后无区别)。
  6. C++ 是不是类型安全的?为什么?

    • 不是完全类型安全。原因:
      • 存在强制类型转换(如reinterpret_cast可以任意转换指针类型);
      • 指针运算可能导致越界访问;
      • 空指针解引用;
      • 未初始化的变量;
      • 数组与指针的隐式转换。

1.2 关键字与修饰符

  1. const 关键字的用法(修饰变量、函数参数、函数返回值、成员函数)?

    • 修饰变量:表示变量的值不能被修改。

      const int MAX = 100;  // 常量
      
    • 修饰函数参数:防止函数修改实参的值。

      void print(const std::string& message);  // 引用传递,不修改原字符串
      
    • 修饰函数返回值:表示返回值不能被修改(通常用于返回指针或引用时)。

      const char* getName() const;  // 返回常量指针
      
    • 修饰成员函数:表示该函数不会修改类的成员变量。

      class Test { 
      public:int getValue() const;  // 不会修改成员变量
      };
      
  2. volatile 关键字的作用?它与 const 能否同时修饰一个变量?

    • 作用:告诉编译器不要对该变量进行优化,每次都从内存中读取值。通常用于多线程环境中共享的变量或硬件寄存器映射的变量。

    • 与const共存:可以同时修饰一个变量,表示变量的值不能被程序修改,但可能被其他因素(如硬件、其他线程)修改。

      volatile const int* p = &someRegister;  // 指向只读寄存器的指针
      
  3. typedef 和 using(C++11)的区别?如何用它们定义函数指针?

    • 区别

      • typedef是C语言的关键字,using是C++11引入的别名声明;
      • using可以定义模板别名,typedef不能;
      • using的语法更清晰,尤其是在复杂类型别名时。
    • 定义函数指针

      // 使用typedef
      typedef int (*FuncPtr)(int, int);// 使用using(C++11)
      using FuncPtr = int (*)(int, int);// 使用示例
      int add(int a, int b) { return a + b; }
      FuncPtr f = add;
      
  4. typedef 和 #define 的区别是什么?

    • 类型检查:typedef会进行类型检查,#define只是简单的文本替换;
    • 作用域:typedef有作用域限制,#define全局有效;
    • 复杂性:typedef适合定义复杂类型(如函数指针),#define更适合简单的文本替换;
    • 调试:typedef在调试时更友好,可以显示类型信息。
  5. extern “C” 的作用是什么?

    • 作用:指定使用C语言的函数命名规则和调用约定,以便C++代码能够调用C语言编写的函数。
    • 使用场景:在C++项目中调用C语言库,或者让C项目调用C++编写的接口。
// 在C++代码中声明C函数
extern "C" {void cFunction();
}// 在C++头文件中同时支持C和C++编译
#ifdef __cplusplus
extern "C" {
#endifvoid commonFunction();
#ifdef __cplusplus
}
#endif
  1. static 关键字的作用有哪些(函数体内、模块内、类中)?

    • 函数体内:变量在函数调用之间保持值,只初始化一次。

      void increment() {static int count = 0;  // 只初始化一次count++;  // 每次调用都增加
      }
      
    • 模块内:限制变量或函数的作用域为本模块(.cpp文件),不能被其他模块访问。

      static void helper() { /* 只在当前文件可见 */ }
      
    • 类中:静态成员变量属于类而不是对象,静态成员函数不依赖于对象实例。

      class MyClass {
      public:static int count;  // 静态成员变量声明static void printCount() {  // 静态成员函数std::cout << count << std::endl;}
      };
      int MyClass::count = 0;  // 静态成员变量定义
      
  2. auto(C++11 前仅用于自动变量)和 decltype 的区别?

    • auto:根据初始化表达式自动推导变量类型,需要初始化。

      auto x = 10;  // x被推导为int类型
      auto s = "hello";  // s被推导为const char*
      
    • decltype:根据表达式推导类型,不需要初始化。

      int a = 5;
      decltype(a) b;  // b被推导为int类型
      decltype(a + 1.0) c;  // c被推导为double类型
      
  3. nullptr 与 NULL 的区别?为何推荐使用 nullptr?

    • NULL:通常是一个宏定义,在C++中被定义为0或 (void*)0,可能导致类型歧义。

    • nullptr:C++11引入的关键字,表示空指针常量,类型为nullptr_t。

    • 推荐使用nullptr的原因:

      • 避免整数和指针类型混淆;
      • 在函数重载时提供明确的类型信息;
      • 提高代码的可读性和安全性。
      void f(int);
      void f(int*);
      f(NULL);  // 可能调用f(int)而不是f(int*)
      f(nullptr);  // 明确调用f(int*)
      

1.3 内存管理

  1. C++ 的内存分区(栈、堆、全局 / 静态存储区、常量存储区、代码区)各自的特点?

    • 栈(Stack)
      • 由编译器自动分配释放;
      • 存储函数参数、局部变量等;
      • 空间小,访问速度快;
      • 内存分配是连续的。
    • 堆(Heap)
      • 由程序员动态分配和释放;
      • 空间较大,访问速度相对较慢;
      • 内存分配不一定连续,可能产生碎片。
    • 全局/静态存储区
      • 存储全局变量和静态变量;
      • 程序运行期间一直存在;
      • 程序结束时由系统释放。
    • 常量存储区
      • 存储常量(如字符串常量、const常量);
      • 内容不允许修改。
    • 代码区
      • 存储程序的二进制指令;
      • 通常是只读的。
  2. 堆和栈的区别(申请方式、大小限制、生命周期、碎片问题等)?

    • 申请方式:栈由编译器自动分配;堆由程序员调用malloc/free或new/delete分配。
    • 大小限制:栈的大小通常较小(几MB);堆的大小受限于系统虚拟内存(几GB)。
    • 生命周期:栈中的数据在函数调用结束后自动销毁;堆中的数据需要手动释放,否则会造成内存泄漏。
    • 碎片问题:频繁分配/释放堆内存会产生碎片;栈不会产生碎片。
    • 分配效率:栈的分配效率高;堆的分配效率相对较低。
  3. 什么是内存泄漏?如何检测和避免内存泄漏?

    • 内存泄漏:程序中动态分配的内存没有被正确释放,导致这部分内存无法被重用。
    • 检测方法
      • 使用内存检测工具(如Valgrind、Visual Leak Detector);
      • 在代码中添加内存跟踪日志;
      • 使用智能指针自动管理内存。
    • 避免方法
      • 及时释放动态分配的内存;
      • 使用RAII原则(Resource Acquisition Is Initialization);
      • 优先使用智能指针(如std::unique_ptr、std::shared_ptr);
      • 避免复杂的内存管理逻辑。
  4. C 和 C++ 动态管理内存的方法有何不同(malloc/free vs new/delete)?

    • malloc/free:C语言的函数,只负责分配/释放内存空间,不调用构造函数/析构函数。

      int* p = (int*)malloc(sizeof(int));
      free(p);
      
    • new/delete:C++的运算符,不仅分配/释放内存,还会调用构造函数/析构函数。

      int* p = new int(10);  // 分配内存并初始化
      delete p;  // 调用析构函数并释放内存// 数组版本
      int* arr = new int[5];
      delete[] arr;  // 必须使用delete[]释放数组
      
    • 其他区别

      • new/delete是运算符,malloc/free是函数;
      • new自动计算所需内存大小,malloc需要手动计算;
      • new分配失败时抛出异常,malloc返回nullptr;
      • new/delete可以重载,malloc/free不能。
  5. new 和 delete 是如何实现的?与 malloc 和 free 有何异同?

    • new的实现
      1. 调用operator new分配内存;
      2. 调用构造函数初始化对象。
    • delete的实现
      1. 调用析构函数清理对象;
      2. 调用operator delete释放内存。
    • 与malloc/free的异同
      • 相同点:都是用于动态内存管理。
      • 不同点
        • new/delete是运算符,malloc/free是函数;
        • new/delete会调用构造函数/析构函数,malloc/free不会;
        • new自动计算内存大小,malloc需要手动指定;
        • new失败时抛出异常,malloc返回nullptr;
        • new/delete可以重载,malloc/free不能。
  6. delete 和 delete [] 的区别是什么?

    • delete:用于释放单个对象的内存,先调用该对象的析构函数,然后释放内存。
    • delete []:用于释放数组对象的内存,先调用数组中每个对象的析构函数,然后释放内存。
    • 混用的后果:用delete释放数组会导致只调用第一个对象的析构函数,造成内存泄漏;用delete []释放单个对象会导致多次调用析构函数,可能导致程序崩溃。
  7. 什么是野指针?其成因有哪些?

    • 野指针:指向已释放内存或未分配内存的指针,无法确定其指向的内容。
    • 成因
      • 指针未初始化;
      • 指针指向的内存已被释放,但指针未置空;
      • 指针越界访问。
    • 避免方法
      • 指针初始化时置为nullptr;
      • 释放内存后将指针置为nullptr;
      • 使用智能指针代替裸指针。
  8. 栈溢出的原因是什么?有哪些解决方法?

    • 原因
      • 函数调用层次过深(如递归调用没有终止条件);
      • 局部变量占用过多栈空间(如大型数组);
      • 栈空间设置过小。
    • 解决方法
      • 优化递归算法,避免过深的调用层次;
      • 使用堆内存代替栈内存存储大型数据;
      • 调整栈空间大小(特定编译器/环境下)。
  9. C++ 的内存管理中,自由存储区与堆的区别是什么?

    • 自由存储区:由C++的new/delete运算符分配和释放的内存区域,是C++概念。
    • :由C语言的malloc/free函数分配和释放的内存区域,是操作系统概念。
    • 关系:通常情况下,自由存储区和堆是同一块内存区域,但严格来说,自由存储区是基于堆实现的。
    • 区别
      • 分配/释放方式不同:自由存储区使用new/delete,堆使用malloc/free;
      • 内存管理机制不同:new/delete会调用构造函数/析构函数,malloc/free不会;
      • new/delete可以重载,改变自由存储区的分配策略。

1.4 函数与预处理

  1. 函数重载的原理是什么?为何返回值不同不能构成重载?

    • 原理:编译器根据函数的参数类型、数量、顺序生成不同的函数名(名称修饰),使得同名但参数列表不同的函数在编译后实际上有不同的标识符。

    • 返回值不同不能构成重载:因为在函数调用时,编译器无法仅根据返回值类型来确定要调用哪个函数。函数调用的语法中,返回值是可选的,不影响函数的选择。

      void print(int);
      void print(double);  // 重载,参数类型不同
      // int print(int);  // 错误:仅返回值不同,不能构成重载
      
  2. 函数默认参数的规则(从右向左设置,调用时不能跳过前面的参数)?

    • 从右向左设置:默认参数必须从最右边的参数开始设置,不能跳过中间的参数。

      void func(int a, int b = 10, int c = 20);  // 正确
      // void func(int a = 10, int b, int c = 20);  // 错误:不能跳过b
      
    • 调用时不能跳过前面的参数:在调用有默认参数的函数时,必须从左到右提供参数,不能跳过前面的参数而直接使用后面的默认参数。

      func(5);  // 等同于func(5, 10, 20)
      func(5, 15);  // 等同于func(5, 15, 20)
      // func(, 15);  // 错误:不能跳过前面的参数
      
  3. 内联函数(inline)的作用?与宏定义的区别?为何内联函数不宜过长?

    • 作用:减少函数调用的开销,提高程序运行效率。编译器会尝试将内联函数的代码插入到调用点,而不是进行函数调用。

    • 与宏定义的区别

      • 内联函数由编译器处理,进行类型检查;宏由预处理器处理,仅进行文本替换。
      • 内联函数支持调试;宏不支持调试。
      • 内联函数更安全,不会有宏替换可能带来的副作用;宏可能因优先级问题导致错误。
      // 内联函数
      inline int max(int a, int b) { return a > b ? a : b; }// 宏定义
      #define MAX(a, b) ((a) > (b) ? (a) : (b))
      
    • 内联函数不宜过长的原因:

      • 如果内联函数过长,编译器可能会忽略inline关键字,将其当作普通函数处理;
      • 过长的内联函数会导致代码膨胀,增加可执行文件的大小。
  4. 预处理指令(#include、#define、#ifdef 等)的作用?#include <> 和 #include “” 的区别?

    • 常用预处理指令
      • #include:包含头文件;
      • #define:定义宏;
      • #undef:取消宏定义;
      • #ifdef/#ifndef:条件编译(如果宏已定义/未定义);
      • #else/#elif:条件编译的分支;
      • #endif:结束条件编译块;
      • #pragma:特定编译器的指令。
    • #include <> 和 #include “” 的区别
      • #include <>:用于包含标准库头文件,编译器会先在标准库目录中查找;
      • #include "":用于包含用户自定义头文件,编译器会先在当前目录中查找,然后再到标准库目录中查找。
  5. 定义和声明的区别是什么?

    • 声明(Declaration):告诉编译器某个名字的存在及其类型,但不分配内存或初始化。

      extern int g_var;  // 变量声明
      int func(int a);   // 函数声明
      
    • 定义(Definition):声明的同时分配内存或提供函数体。一个变量或函数可以有多个声明,但只能有一个定义。

      int g_var = 10;  // 变量定义
      int func(int a) { return a * 2; }  // 函数定义
      
  6. 引用作为函数参数以及返回值的好处是什么?有哪些限制?

    • 作为函数参数的好处

      • 避免拷贝大对象,提高效率;
      • 可以修改实参的值(如果不是const引用);
      • 比指针更安全,不需要检查空指针。
    • 作为函数返回值的好处

      • 避免返回值的拷贝,提高效率;
      • 可以直接修改返回的对象(链式调用)。
    • 限制

      • 引用必须绑定到有效的对象,不能返回局部变量的引用;
      • 返回引用时需要确保引用的对象在函数调用结束后仍然存在。
      // 正确:返回类成员的引用
      class MyClass {
      private:int value;
      public:int& getValue() { return value; }
      };// 错误:返回局部变量的引用
      int& badFunction() {int x = 10;return x;  // x在函数结束后被销毁
      }
      
  7. C++ 文件编译与执行的四个阶段是什么?

    • 预处理(Preprocessing)
      • 处理预处理指令(如#include、#define);
      • 删除注释;
      • 宏展开;
      • 生成.i文件。
    • 编译(Compilation)
      • 将预处理后的代码转换为汇编语言;
      • 进行语法检查、语义分析、优化等;
      • 生成.s文件。
    • 汇编(Assembly)
      • 将汇编代码转换为机器码(二进制);
      • 生成.obj文件(Windows)或.o文件(Unix/Linux)。
    • 链接(Linking)
      • 将多个目标文件和库文件链接在一起;
      • 解决符号引用;
      • 生成可执行文件(.exe文件或ELF文件)。
  8. 头文件中 #ifndef/define/endif 与 #pragma once 的区别是什么?

    • #ifndef/define/endif(条件包含):

      • 标准C++语法,所有编译器都支持;
      • 可以嵌套使用,实现更复杂的条件包含;
      • 可能存在宏名冲突的问题。
      #ifndef HEADER_FILE_NAME_H
      #define HEADER_FILE_NAME_H// 头文件内容#endif  // HEADER_FILE_NAME_H
      
    • #pragma once

      • 非标准但被大多数编译器支持的语法;
      • 更简洁,不容易出错;
      • 依赖于编译器对文件的识别,可能在某些情况下(如硬链接)导致重复包含。
      #pragma once// 头文件内容
      
    • 选择建议:两种方式都可以使用,通常推荐使用#pragma once,因为它更简洁且不容易出错,但如果需要兼容不支持#pragma once的编译器,或者需要更复杂的条件包含,可以使用#ifndef/define/endif

1.5 类型转换与其他

  1. C++ 的四种强制转换(static_cast、dynamic_cast、const_cast、reinterpret_cast)分别是什么?各自的适用场景?

    • static_cast

      • 适用场景:用于基本类型转换、上行转换(派生类指针/引用转为基类指针/引用)、转换编译器能明确识别的类型。
      • 特点:编译时进行检查,不进行运行时类型检查。
      int i = 10;
      double d = static_cast<double>(i);  // 基本类型转换Base* base = static_cast<Base*>(derived);  // 上行转换(安全)
      
    • dynamic_cast

      • 适用场景:主要用于下行转换(基类指针/引用转为派生类指针/引用),必须用于包含虚函数的类。
      • 特点:运行时进行类型检查,如果转换失败返回nullptr(指针)或抛出异常(引用)。
      Base* base = new Derived();
      Derived* derived = dynamic_cast<Derived*>(base);  // 下行转换,需要运行时检查
      
    • const_cast

      • 适用场景:用于移除变量的const或volatile限定符。
      • 注意:如果原对象本身不是const的,使用const_cast修改是合法的;如果原对象是const的,修改其内容是未定义行为。
      const int* p = &value;
      int* q = const_cast<int*>(p);  // 移除const限定符
      
    • reinterpret_cast

      • 适用场景:用于不同类型指针之间的转换、指针与整数之间的转换,是最不安全的转换方式。
      • 特点:仅重新解释指针的二进制表示,不进行任何类型检查或转换。
      int* p = reinterpret_cast<int*>(0x12345678);  // 指针与整数转换
      
  2. 左值和右值的区别?左值引用与右值引用的区别是什么?

    • 左值(lvalue):表达式结束后依然存在的持久对象,可以出现在赋值语句的左侧。
    • 右值(rvalue):表达式结束后就不再存在的临时对象,只能出现在赋值语句的右侧。
    int a = 10;  // a是左值,10是右值
    int& lr = a;  // 左值引用绑定到左值
    // int& lr2 = 10;  // 错误:左值引用不能绑定到右值
    const int& lr3 = 10;  // 常量左值引用可以绑定到右值// C++11引入的右值引用
    int&& rr = 10;  // 右值引用绑定到右值
    // int&& rr2 = a;  // 错误:右值引用不能直接绑定到左值
    int&& rr3 = std::move(a);  // 使用std::move将左值转换为右值
    
    • 左值引用(&)
      • 可以绑定到左值或const右值;
      • 通常用于函数参数传递,避免拷贝。
    • 右值引用(&&)
      • C++11引入,用于实现移动语义和完美转发;
      • 只能绑定到右值或通过std::move转换的左值;
      • 主要用于移动构造函数、移动赋值运算符和完美转发。
  3. 指针数组和数组指针的区别是什么?

    • 指针数组:一个数组,其元素是指针。

      int* arr[5];  // 包含5个int*指针的数组
      
    • 数组指针:一个指针,指向一个数组。

      int (*ptr)[5];  // 指向包含5个int元素的数组的指针
      int arr[5];
      ptr = &arr;  // 正确:ptr指向整个数组
      
    • 记忆方法:看括号的位置,*和变量名在括号内的是数组指针,否则是指针数组。

  4. sizeof 和 strlen 的区别是什么?

    • sizeof

      • 运算符,不是函数;
      • 编译时计算;
      • 计算变量、类型或表达式所占的字节数;
      • 对于数组名,计算整个数组的大小;
      • 对于指针,计算指针本身的大小(通常为4或8字节)。
    • strlen

      • 函数,定义在头文件中;
      • 运行时计算;
      • 计算字符串的长度,不包括结束符’\0’;
      • 只适用于以’\0’结尾的字符数组(C风格字符串)。
      char str[] = "hello";
      sizeof(str);  // 6字节(包含'\0')
      strlen(str);  // 5字节(不包含'\0')char* p = str;
      sizeof(p);  // 4或8字节(指针的大小)
      strlen(p);  // 5字节
      
  5. 关于 sizeof 的详细小结(包括不同类型、数组、结构体等)?

    • 基本类型:sizeof(char) = 1字节,sizeof(short) = 2字节,sizeof(int) = 4字节,sizeof(long) = 4或8字节,sizeof(long long) = 8字节,sizeof(float) = 4字节,sizeof(double) = 8字节,sizeof(bool) = 1字节。

    • 指针类型:在32位系统上,sizeof(任意指针) = 4字节;在64位系统上,sizeof(任意指针) = 8字节。

    • 数组:sizeof(数组名) = 数组元素个数 * sizeof(元素类型),但当数组名作为函数参数传递时,会退化为指针,sizeof(指针) = 指针本身的大小。

      int arr[10];
      sizeof(arr);  // 10 * sizeof(int) = 40字节(假设int为4字节)void func(int a[]) {sizeof(a);  // 4或8字节(指针的大小)
      }
      
    • 结构体:sizeof(结构体)考虑内存对齐,通常大于或等于各成员大小之和。

      struct S {char c;    // 1字节int i;     // 4字节double d;  // 8字节
      };
      // 在大多数64位系统上,sizeof(S) = 16字节(内存对齐)
      
    • 空类:C++中空类的大小为1字节(为了区分不同的对象实例)。

      class Empty { };
      sizeof(Empty);  // 1字节
      
  6. 结构体为什么需要内存对齐?内存对齐的原因是什么?

    • 内存对齐:编译器为了提高访问效率,会对结构体成员进行对齐,使每个成员的起始地址是其大小的整数倍。
    • 内存对齐的原因
      • 性能考虑:处理器访问对齐的内存比未对齐的内存更快;
      • 硬件限制:某些处理器架构不支持非对齐的内存访问,或者访问非对齐内存会导致性能下降;
      • 兼容性:不同编译器、不同平台可能有不同的对齐要求,适当的对齐可以保证数据结构的兼容性。
    • 对齐规则
      • 每个成员的起始地址必须是其自身大小的整数倍;
      • 整个结构体的大小必须是其最大成员大小的整数倍(或编译器指定的最大对齐值的整数倍)。
  7. 引用是否占用内存空间?为什么?

    • 从实现角度:引用通常在编译器内部实现为指针,因此会占用内存空间(在32位系统上为4字节,在64位系统上为8字节)。
    • 从语言标准角度:C++标准没有明确规定引用是否占用内存空间,只是规定引用必须绑定到一个对象,并且不能重新绑定。
    • 为何会有这样的设计:引用的设计目的是提供一个便捷、安全的别名机制,而不是作为一种新的指针类型。因此,标准并不关心其具体实现细节,只关心其行为。
  8. 全局变量和局部变量的区别是什么?是怎么实现的?操作系统和编译器是怎么知道的?

    • 全局变量
      • 定义在函数外部,作用域为整个程序;
      • 存储在全局/静态存储区,程序启动时分配内存,程序结束时释放;
      • 默认初始化为0或空指针。
    • 局部变量
      • 定义在函数内部,作用域为函数内部或代码块内部;
      • 存储在栈区,函数调用时分配内存,函数返回时释放;
      • 不默认初始化,其值是未定义的。
    • 实现方式
      • 编译器在编译时为全局变量和局部变量分配不同的内存区域;
      • 全局变量的地址在编译时或链接时确定;
      • 局部变量的地址在运行时动态确定(基于栈指针)。
    • 操作系统和编译器如何知道
      • 编译器在编译过程中会记录变量的作用域和存储类型;
      • 链接器会处理全局变量的引用;
      • 操作系统在加载程序时,根据可执行文件中的段信息(如.data段、.bss段)来分配和初始化全局变量的内存空间。
  9. main 函数执行之前,还会执行什么代码?

    • 全局变量的构造函数:程序启动时,会先初始化全局变量和静态变量,并调用它们的构造函数;

    • C++ 运行时库初始化:初始化C++标准库,如内存分配器、异常处理机制等;

    • main函数的参数设置:解析命令行参数,设置argc和argv;

    • 其他初始化:如静态对象的初始化、线程局部存储的初始化等。

    • 示例

      class MyClass {
      public:MyClass() { std::cout << "MyClass constructor called" << std::endl; }
      };MyClass globalObj;  // 全局对象,其构造函数在main之前调用int main() {std::cout << "main function called" << std::endl;return 0;
      }
      // 输出顺序:
      // MyClass constructor called
      // main function called
      

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/93776.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/93776.shtml
英文地址,请注明出处:http://en.pswp.cn/web/93776.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Tomcat里catalina.sh详解

在 Tomcat 中&#xff0c;catalina.sh&#xff08;Linux/macOS&#xff09;或 catalina.bat&#xff08;Windows&#xff09;是 核心的启动和关闭脚本&#xff0c;用于控制 Tomcat 服务器的运行。它是 Tomcat 的“主控脚本”&#xff0c;负责设置环境变量、启动/关闭 JVM 进程&…

STM32之MCU和GPIO

一、单片机MCU 1.1 单片机和嵌入式 嵌入式系统 以计算机为核心&#xff0c;tips&#xff1a;计算机【处理单元&#xff0c;内存 硬盘】 可以控制的外部设备&#xff0c;传感器&#xff0c;电机&#xff0c;继电器 嵌入式开发 数据源--> 处理器(CPU MCU MPU) --> 执行器 …

22_基于深度学习的桃子成熟度检测系统(yolo11、yolov8、yolov5+UI界面+Python项目源码+模型+标注好的数据集)

目录 项目介绍&#x1f3af; 功能展示&#x1f31f; 一、环境安装&#x1f386; 环境配置说明&#x1f4d8; 安装指南说明&#x1f3a5; 环境安装教学视频 &#x1f31f; 二、数据集介绍&#x1f31f; 三、系统环境&#xff08;框架/依赖库&#xff09;说明&#x1f9f1; 系统环…

数据结构:二叉树oj练习

在讲今天的题目之前&#xff0c;我们还需要讲一下二叉树的以下特点&#xff1a; 对任意一颗二叉树&#xff0c;如果度为0的节点个数是n0&#xff0c;度为2的节点个数是n2&#xff0c;则有n0n21. 证明&#xff1a;二叉树总的节点个数是n&#xff0c;那么有nn0n1n2 二叉树的度为…

RabbitMQ高级特性——TTL、死信队列、延迟队列、事务、消息分发

目录 一、TTL 1.1设置消息的TTL 1.2设置队列的TTL 1.3两者之间的区别 二、死信队列 2.1死信的概念 2.2死信产生的条件&#xff1a; 2.3死信队列的实现 死信队列的工作原理 2.4常⻅⾯试题 三、延迟队列 3.1概念 3.2应用场景 3.3RabbitMQ 实现延迟队列的核心原理 1…

神经网络设计中关于BN归一化(Normalization)的讨论

在神经网络的结构中&#xff0c;我们常常可以看见归一化&#xff08;Normalization&#xff09;如BN的出现&#xff0c;无论是模型的backbone或者是neck的设计都与它有着重大的关系。 因此引发了我对它的思考&#xff0c;接下来我将从 是什么&#xff08;知识领域&#xff0c;诞…

MacOS 安全机制与“文件已损坏”排查完整指南

1. 背景说明macOS 为了保护系统安全&#xff0c;内置了多个安全机制&#xff1a;机制作用是否影响第三方 AppSIP (System Integrity Protection)保护系统关键文件/目录不被篡改高风险 App/驱动可能受限Gatekeeper限制未签名/未认证 App 运行阻止“未知开发者” App文件隔离属性…

package.json文件中的devDependencies和dependencies对象有什么区别?

前端项目的package.json文件中&#xff0c;dependencies和devDependencies对象都用于指定项目所依赖的软件包&#xff0c;但它们在项目的开发和生产环境中的使用有所不同。1.dependencies&#xff1a;dependencies是指定项目在生产环境中运行所需要的依赖项。这些依赖项通常包括…

【最新版】CRMEB Pro版v3.4系统源码全开源+PC端+uniapp前端+搭建教程

一.系统介绍 crmebPro版 v3.4正式发布&#xff0c;智能任务推送、动态标签管理、商城AI生产力&#xff0c;焕然一新&#xff0c;不负期待&#xff01;页面DIY设计功能全面升级&#xff0c;组件更丰富&#xff0c;样式设计更全面&#xff1b;移动端商家管理&#xff0c;让商城管…

AI 浪潮下 IT 从业者的职业展望:替代之惑与转型之道

一、引言1.1 科技变革的浪潮&#xff1a;AI 崛起与 IT 行业震荡在当今科技飞速发展的时代&#xff0c;人工智能&#xff08;AI&#xff09;无疑是最具影响力的变革力量之一。从实验室的前沿研究到广泛的商业应用&#xff0c;AI 以惊人的速度渗透到各个领域&#xff0c;彻底改变…

DSP音频算法移植优化工程师实战

以下以音频FIR滤波器算法为例&#xff0c;完整演示从MATLAB原型 → Python验证 → TI DSP C语言移植优化的全流程&#xff0c;包含关键代码和优化技巧&#xff1a;关键优化技术解析&#xff1a; 内存访问优化使用#pragma DATA_ALIGN确保64位对齐&#xff08;满足LDDW指令要求&a…

Spark 运行流程核心组件(三)任务执行

一、启动模式 1、standalone资源申请&#xff1a;Driver向Master申请Executor资源Executor启动&#xff1a;Master调度Worker启动Executor注册通信&#xff1a;Executor直接向Driver注册 2、YARNDriver向YARN ResourceManager(RM)申请AM容器RM分配NodeManager(NM)启动AM&#x…

rabbitmq发送的延迟消息时间过长就立即消费了

RabbitMQ延迟消息在设置过长时间后被立即消费的问题&#xff0c;通常与以下原因有关&#xff1a; TTL限制问题 RabbitMQ对消息TTL(Time To Live)有32位整数限制(0-4294967295毫秒)&#xff0c;约49.7天。超过该值的延迟时间会导致消息立即被消费解决方案&#xff1a;确保设置的…

kafka的pull的依据

1. 每次 pull() 是否必须在提交上一批消息的 offset 之后&#xff1f;绝对不需要&#xff01; 提交 offset 和调用 poll() (拉取消息) 是两个完全独立的行为。消费者可以连续调用 poll() 多次&#xff0c;期间完全不提交任何 offset。 这是 Kafka 消费者的正常工作模式。提交 o…

学习嵌入式的第二十一天——数据结构——链表

单向链表特点&#xff1a;存储的内存空间不连续 。为了弥补顺序存储存劣势。优势 插入&#xff0c;删除 O(1) 动态存储 &#xff0c;在程序运行期间决定大小。劣势&#xff1a; 不能随机访问 O(N) 节点-> 数据域指针域 顺序表(数组) 只有数据域链表的操作代码&#xff1…

Rust Web 全栈开发(十三):发布

Rust Web 全栈开发&#xff08;十三&#xff09;&#xff1a;发布Rust Web 全栈开发&#xff08;十三&#xff09;&#xff1a;发布发布 teacher_service发布 svr测试 teacher_service 和 svr发布 wasm-client测试 wasm-clientRust Web 全栈开发&#xff08;十三&#xff09;&a…

Zephyr 中的 bt_le_per_adv_set_data 函数的介绍和应用方法

目录 概述 1 函数接口介绍 1.1 函数原型 1.2 功能详解 2 使用方法 2.1 创建流程 2.1.1 创建扩展广播实例 2.1.2 设置周期性广播数据 2.1.3 配置周期性广播参数 2.1.4 启动广播 2.2 主流程函数 2.3 关键配置 (prj.conf) 3 高级用法 3.1 大数据分片传输 3.2 动态数…

Ansible 角色管理指南

Ansible 角色管理指南 实验环境设置 以下命令用于准备实验环境&#xff0c;创建一个工作目录并配置基本的Ansible设置&#xff1a; # 创建web工作目录并进入 [azurewhiskycontroller ~]$ mkdir web && cd web# 创建Ansible配置文件 [azurewhiskycontroller web]$ cat &…

【补充】数据库中有关系统编码和校验规则的简述

一、字符集和校验规则&#xfeff;1.创建数据库案例数据库创建方法&#xff1a;使用CREATE DATABASE语句创建数据库字符集指定方式&#xff1a;通过CHARACTER SETutf8指定数据库编码格式默认配置说明&#xff1a;未指定字符集时默认使用utf8和utf8_general_ci配置文件位置&…

计算机网络 HTTP1.1、HTTP2、HTTP3 的核心对比及性能分析

以下是 HTTP/1.1、HTTP/2、HTTP/3 的核心对比及性能分析&#xff0c;重点关注 HTTP/3 的性能优势&#xff1a;&#x1f4ca; HTTP 协议演进对比表特性HTTP/1.1 (1997)HTTP/2 (2015)HTTP/3 (2022)传输层协议TCPTCPQUIC (基于 UDP)连接建立TCP 三次握手 TLS 握手 (高延迟)同 HTT…