<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>c++笔记 on MU</title><link>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/</link><description>Recent content in c++笔记 on MU</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Tue, 17 Sep 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/index.xml" rel="self" type="application/rss+xml"/><item><title>include 的细节</title><link>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/include/</link><pubDate>Tue, 17 Sep 2024 00:00:00 +0000</pubDate><guid>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/include/</guid><description>#include &amp;ldquo;headername&amp;rdquo; 和 #include &amp;lt;headername&amp;gt; 的区别 #include &amp;quot;headername&amp;quot; 和 #include &amp;lt;headername&amp;gt; 的主要区别在于头文件的搜索路 径。虽然在包含标准库文件（如 iostream）时，两者的行为通常没有区别，但在实际开发 中，两者有不同的语义，尤其是在包含自定义头文件时。
1.#include &amp;ldquo;headername&amp;rdquo; 含义：首先在当前目录（即源文件所在的目录）中搜索头文件。如果没有找到该头文件， 编译器会在标准的系统路径中继续查找。
用法：这种方式常用于包含用户自定义的头文件（非标准库头文件）。
搜索路径：
当前源文件所在目录。 如果在当前目录找不到头文件，编译器会在标准的包含路径（如 /usr/include/ 或 C:\Program Files...）中继续查找 2.#include &amp;lt;headername&amp;gt; 含义：编译器会直接在系统的标准路径中搜索头文件，而不检查当前目录。
用法：这种方式通常用于包含标准库头文件，例如 iostream、vector 等。
搜索路径：直接在系统的标准头文件路径中查找，不查找当前目录。
区别 主要区别是搜索路径： #include &amp;quot;headername&amp;quot;：先查找当前目录，然后再查找系统标准目录。 #include &amp;lt;headername&amp;gt;：只查找系统标准目录，不查找当前目录。 使用场景： #include &amp;quot;headername&amp;quot;：主要用于用户自定义的头文件，开发者可以将头文件放在 当前项目目录中。 #include &amp;lt;headername&amp;gt;：主要用于标准库头文件或系统级头文件，编译器会从系统 的标准路径中查找。 对于标准库头文件（如 #include &amp;lt;iostream&amp;gt;），两者的区别在很多编译器中不大，因 为即使你使用 #include &amp;quot;iostream&amp;quot;，编译器也会在标准路径中找到标准库头文件。但 是，规范上推荐使用 #include &amp;lt;iostream&amp;gt; 来包含标准库头文件，因为它更明确且可 以避免潜在的冲突。</description></item><item><title>pragma 指令</title><link>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/pragma/</link><pubDate>Tue, 17 Sep 2024 00:00:00 +0000</pubDate><guid>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/pragma/</guid><description>描述 在 C++ 中，#pragma 是一种编译器指令，用于向编译器提供特定的编译指示。它并不是 C++ 标准的一部分，而是由不同编译器实现的，因此它的作用和支持的指令可能会因编译器 的不同而有所差异。
#pragma 是一种编译器指令，用于执行特定的编译器行为，常用于内存对齐、警告控制、优 化等任务。由于它不是 C++ 标准的一部分，因此其使用和功能通常依赖于具体的编译器。 在跨平台开发时需要特别注意，尽量避免编译器特定的 #pragma 指令，以提高代码的可移 植性
1. #pragma once 用于防止头文件被多次包含。 它告诉编译器，在编译过程中只包含一次该文件的内容。相比传统的 include guard (#ifndef、#define、#endif)，它更简洁。 示例 #pragma once // 头文件内容 等同于 #ifndef HEADER_FILE_NAME #define HEADER_FILE_NAME // 头文件内容 #endif 2. #pragma pack 用于修改结构体或类的字节对齐方式。通常编译器会根据平台的要求为结构体字段进行内 存对齐，但通过 #pragma pack，你可以改变默认的对齐方式 下面的示例会让 MyStruct 使用 1 字节对齐，避免结构体字段之间的填充字节。
示例 #pragma pack(1) // 设置为1字节对齐 struct MyStruct { char a; int b; }; #pragma pack() // 恢复默认对齐 3. #pragma warning 用于控制编译过程中是否显示某些警告，通常是在禁用特定警告或启用更多警告信息时使 用。 示例 #pragma warning(push) // 保存当前警告状态 #pragma warning(disable:4996) // 禁用某个警告（如过时的函数使用） // 这里可以包含会触发该警告的代码，但警告会被忽略 #pragma warning(pop) // 恢复之前的警告状态 4.</description></item><item><title>C 动态分配二维数组的内存</title><link>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/dy_allocate_mem_2darray/</link><pubDate>Sun, 21 Jul 2024 00:00:00 +0000</pubDate><guid>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/dy_allocate_mem_2darray/</guid><description>指针数组 使用指针数组, 每个指针指向一维数组的起始位置
#include &amp;lt;stdio.h&amp;gt; #include &amp;lt;stdlib.h&amp;gt; int main() { int rows = 3; int cols = 4; // 分配行指针数组 double **array = (double **)malloc(rows * sizeof(double *)); if (array == NULL) { printf(&amp;#34;Memory allocation failed\n&amp;#34;); return 1; } // 为每一行分配内存 for (int i = 0; i &amp;lt; rows; i++) { array[i] = (double *)malloc(cols * sizeof(double)); if (array[i] == NULL) { printf(&amp;#34;Memory allocation failed\n&amp;#34;); // 释放已分配的内存 for (int j = 0; j &amp;lt; i; j++) { free(array[j]); } free(array); return 1; } } // 初始化并打印二维数组 for (int i = 0; i &amp;lt; rows; i++) { for (int j = 0; j &amp;lt; cols; j++) { array[i][j] = i * cols + j; printf(&amp;#34;%f &amp;#34;, array[i][j]); } printf(&amp;#34;\n&amp;#34;); } // 释放内存 for (int i = 0; i &amp;lt; rows; i++) { free(array[i]); } free(array); return 0; } 连续内存块 分配一个连续的内存块，并使用指针数组管理。</description></item><item><title>在 C 函数中建议 move data to heap 时</title><link>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/move_stack_to_heap/</link><pubDate>Sat, 20 Jul 2024 00:00:00 +0000</pubDate><guid>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/move_stack_to_heap/</guid><description>malloc malloc（memory allocation）函数用于分配指定字节数的内存，并返回指向这块内存的指针。内存未初始化。
#include &amp;lt;stdlib.h&amp;gt; int main() { // 分配内存用于存储10个整数 int *array = (int *)malloc(10 * sizeof(int)); if (array == NULL) { // 处理内存分配失败的情况 return 1; } // 使用分配的内存 for (int i = 0; i &amp;lt; 10; i++) { array[i] = i; } // 释放内存 free(array); return 0; } calloc calloc(contiguous allocation) 函数用于分配指定数量的元素, 每个元素大小为指定字节数, 并将分配的内存初始化为 zero;
#include &amp;lt;stdlib.h&amp;gt; int main() { // 分配内存用于存储 10 个整数, 并初始化为 zero int *array = (int*)calloc(10, sizeof(int)); if(array == NULL) { // 处理内存分配失败的情况 return 1; } // 使用分配的内存 for(int i = 0; i &amp;lt; 10; i++&amp;gt;) { // array[i] 已初始化为 0 } // 释放内存 free(array); return 0; } realloc realloc (re-allocation) 函数用于调整之前分配的内存块的大小, 如果新的内存大于旧的内存, 新的内存部分未初始化</description></item><item><title>比较 boost 和 stl</title><link>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/stlboost/</link><pubDate>Sun, 02 Jun 2024 00:00:00 +0000</pubDate><guid>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/stlboost/</guid><description>Boost 和 STL（Standard Template Library）是两个非常重要的 C++ 库，它们各自提供了 丰富的功能和工具，但在用途、设计哲学和使用场景上有一些显著的区别。下面是对 Boost 和 STL 的详细比较：
STL（Standard Template Library） 标准库的一部分：STL 是 C++ 标准库的一部分，由 ISO/IEC 标准化组织定义。所 有符合标准的 C++ 编译器都必须提供 STL。 核心组件：STL 主要包含容器、算法和迭代器三个核心组件。 容器：如 vector、list、map、set 等，用于存储和管理数据。 算法：如 sort、find、copy 等，用于对容器中的数据进行操作。 迭代器：用于遍历容器中的元素，提供了统一的接口。 轻量级：STL 设计简洁，专注于基本的数据结构和算法，适合大多数常见的编程任 务。 性能优化：STL 的实现经过高度优化，性能通常非常好。 使用场景 通用数据结构和算法：适用于大多数常见的数据管理和操作需求。 学习和教学：STL 是学习 C++ 标准库和模板编程的好起点。 Boost 第三方库：Boost 是一个第三方库集合，由 C++ 社区开发和维护，不是标准库的一 部分。 功能丰富：Boost 提供了比 STL 更多的功能，涵盖了网络编程、线程管理、正则表 达式、智能指针、日期和时间处理等多个领域。 模块化：Boost 包含许多独立的模块，每个模块解决特定的问题。用户可以根据需 要选择和使用这些模块。 高级特性：Boost 提供了许多高级特性，如元编程、函数对象、信号槽机制等，适 合复杂的编程任务。 社区支持：Boost 有一个活跃的社区，提供了丰富的文档、示例和讨论论坛。 使用场景 复杂应用开发：适用于需要高级功能和复杂数据结构的项目。 高性能计算：许多 Boost 模块针对高性能计算进行了优化。 跨平台开发：Boost 提供了许多跨平台的解决方案，适合多平台项目。 对比总结 功能对比 STL：主要提供基本的数据结构和算法，适合大多数常见的编程任务。 Boost：提供更丰富的功能和高级特性，适合复杂的编程需求。 性能对比 STL：经过高度优化，性能通常非常好。 Boost：某些模块也经过优化，但整体性能取决于具体的模块和实现。 易用性对比 STL：设计简洁，易于学习和使用。 Boost：功能丰富，但可能需要更多的时间来学习和掌握。 社区和支持 STL：作为标准库的一部分，有广泛的文档和教程支持。 Boost：有活跃的社区，提供了丰富的资源和支持。 结论 STL 是 C++ 标准库的一部分，提供了基本的数据结构和算法，适合大多数常见的编 程任务。 Boost 是一个功能丰富的第三方库，提供了许多高级特性，适合复杂的编程需求。</description></item><item><title>move函数的实现原理</title><link>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E8%AF%AD%E8%A8%80%E7%89%B9%E6%80%A7%E7%9B%B8%E5%85%B3/move%E5%87%BD%E6%95%B0%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/</link><pubDate>Wed, 09 Aug 2023 00:00:00 +0000</pubDate><guid>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E8%AF%AD%E8%A8%80%E7%89%B9%E6%80%A7%E7%9B%B8%E5%85%B3/move%E5%87%BD%E6%95%B0%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/</guid><description/></item><item><title>C++ 是如何实现面向对象编程的</title><link>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/%E4%BB%80%E4%B9%88%E6%98%AF%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/</link><pubDate>Thu, 27 Oct 2022 00:00:00 +0000</pubDate><guid>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/%E4%BB%80%E4%B9%88%E6%98%AF%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/</guid><description>面向对象的三个特性 封装:将对象的状态（数据成员）和行为（成员函数）捆绑在一起，并控制对它们的访问。封装的主要目的是隐藏类的内部实现细节，并仅暴露必要的接口供外部使用。这样可以保护数据，防止外部代码意外或恶意地改变对象的状态。 数据成员可以被声明为 private、protected 或 public。 成员函数通常也是 public 的，以便用户可以调用这些函数来操作对象。 使用 private 和 protected 修饰符可以限制对数据成员的直接访问。 继承:允许创建新的类（称为派生类或子类）从现有的类（称为基类或父类）继承属性和方法。这样可以复用代码，同时也可以扩展或修改基类的行为。 派生类可以继承基类的所有非私有成员。 派生类可以添加新的成员变量和方法，或者重写基类的方法。 继承支持层次化的分类结构，便于建模现实世界的关系 多态: 允许使用一个接口来表示多种类型的特性。在 C++ 中，多态通常是通过 虚函数 （virtual function）来实现的。当一个函数被声明为虚函数时，可以在派生类中重写该函数，并且可以通过基类的指针或引用调用派生类中的重写版本。 支持运行时多态性（runtime polymorphism），即可以在运行时确定调用哪个版本的函数。 通常与虚函数和接口有关 虚函数 和 纯虚函数 虚函数的实现机制 C++ 中多态的实现原理 多态是通过虚函数实现的，虚函数的地址保存在虚函数表中，虚函数表的地址保存在含有虚函数的类的实例对象的内存空间中。
重载 | 重写 | 隐藏 函数重载: 同一可访问区 内被声明几个具有 不同 参数列（参数的类型、个数、顺序）的同名函数，根据参数列表确定调用哪个函数，重载不关心函数返回类型。 函数重写(覆盖): 派生类中存在 重新定义 的函数。函数名、参数列表、返回值类型都必须同基类中被重写的函数一致，只有函数体不同。派生类调用时会调用派生类的重写函数，不会调用被重写函数。重写的基类中被重写的函数 必须 有 virtual 修饰。 函数隐藏: 在面向对象编程中，特别是在 C++ 中，当一个派生类（derived class）中定义了一个与基类（base class）中相同名称的成员函数时，如果没有特别标记为 virtual，那么派生类中的成员函数就会“隐藏”掉基类中的同名成员函数。这意味着当你通过派生类的对象调用这个函数时，调用的是派生类中的版本，而不是基类中的版本。 class Base { public: void fun(int tmp, bool tag ) { cout &amp;lt;&amp;lt; &amp;#34;Base::fun(int tmp, bool tmp1)&amp;#34; &amp;lt;&amp;lt; endl; } virtual void vir_fun(int* array) { cout &amp;lt;&amp;lt; &amp;#34;Base::vir_fun(int* array)&amp;#34; &amp;lt;&amp;lt; endl; } } class Derived: public Base { public: void fun(int tmp); // 隐藏基类中的同名函数 void fun(float tmp); // 重载 参数类型不同（相对于上一个函数） void fun(int tmp, float tmp1); // 重载 参数个数不同（相对于上一个函数） void fun(float tmp, int tmp1); // 重载 参数顺序不同（相对于上一个函数） int fun(int tmp); // error: &amp;#39;int A::fun(int)&amp;#39; cannot be overloaded 错误：注意重载不关心函数返回类型 virtual void vir_fun(int* array) { cout &amp;lt;&amp;lt; &amp;#34;Derived::vir_fun(int* array)&amp;#34;&amp;lt;&amp;lt; endl; } }; int main() { Derived ex; ex.</description></item><item><title>sizeof 和 strlen 的区别</title><link>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E5%85%B3%E9%94%AE%E5%AD%97%E5%92%8C%E5%85%B3%E9%94%AE%E5%BA%93%E5%87%BD%E6%95%B0.md/sizeofstrlen/</link><pubDate>Sat, 15 Oct 2022 00:00:00 +0000</pubDate><guid>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E5%85%B3%E9%94%AE%E5%AD%97%E5%92%8C%E5%85%B3%E9%94%AE%E5%BA%93%E5%87%BD%E6%95%B0.md/sizeofstrlen/</guid><description/></item><item><title>编译与链接</title><link>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E7%BC%96%E8%AF%91%E5%92%8C%E5%86%85%E5%AD%98%E7%9B%B8%E5%85%B3/%E7%BC%96%E8%AF%91%E4%B8%8E%E9%93%BE%E6%8E%A5/</link><pubDate>Sat, 15 Oct 2022 00:00:00 +0000</pubDate><guid>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E7%BC%96%E8%AF%91%E5%92%8C%E5%86%85%E5%AD%98%E7%9B%B8%E5%85%B3/%E7%BC%96%E8%AF%91%E4%B8%8E%E9%93%BE%E6%8E%A5/</guid><description>什么是编译 C++ 是一种静态类型的编程语言，CPU 无法直接执行文件中的各行命令, 这意味着在程序执行之前需要通过 编译过程 将其源代码转换为机器码. 编译器会读取程序的源文件 .cpp, 将其翻译为可执行文件 ELF, ELF 文件可以经过操作系统进行加载执行
编译的过程 常见的编译过程分为四个过程: 编译预处理, 编译, 汇编, 链接
编译预处理：在预编译过程中主要处理源代码中的预处理指令，比如引入头文件#include，去除注释，处理所有的条件编译指令#ifdef, #ifndef, #else, #elif, #endif，宏的替换#define，添加行号，保留所有的编译器指令； 编译：针对预处理后的文件进行词法分析、语法分析、语义分析、符号汇总、汇编代码生成，并针对程序的结构或者特定的 CPU 平台进行优化，其中涉及的过程较为复杂。简单来说编译的过程即为将 .cpp 源文件翻译成 .s 的汇编代码； 汇编：将汇编代码 .s 翻译成机器指令 .o 文件，一个 .cpp 文件只会生成一个 .o 文件； 链接：汇编程序生成的目标文件即为 .o 文件，单独的 .o 文件可能无法执行。因为一个程序可能由多个源文件组成，此时就存在多个 .o 文件。文件 A 中的函数引用了另一个文件 B 中定义的符号或者调用了某个库文件中的函数，这就需要 链接处理。那链接的目的就是将这些文件对应的目标文件连接成一个整体，从而生成一个可被操作系统加载执行的 ELF 程序文件。链接的过程较为复杂，如有兴趣可以参考：「Computer Systems: A Programmer&amp;rsquo;s Perspective」。 什么是链接 链接的种类 静态链接 动态链接</description></item><item><title>大端和小端</title><link>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E7%BC%96%E8%AF%91%E5%92%8C%E5%86%85%E5%AD%98%E7%9B%B8%E5%85%B3/%E5%A4%A7%E7%AB%AF%E5%92%8C%E5%B0%8F%E7%AB%AF/</link><pubDate>Sat, 15 Oct 2022 00:00:00 +0000</pubDate><guid>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E7%BC%96%E8%AF%91%E5%92%8C%E5%86%85%E5%AD%98%E7%9B%B8%E5%85%B3/%E5%A4%A7%E7%AB%AF%E5%92%8C%E5%B0%8F%E7%AB%AF/</guid><description>什么是字节序(Endian-ness) 两种字节序的形式 小端(Little-Endian) 大段(Bit-Endian) 字节序的转换</description></item><item><title>内存泄漏</title><link>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E7%BC%96%E8%AF%91%E5%92%8C%E5%86%85%E5%AD%98%E7%9B%B8%E5%85%B3/%E5%86%85%E5%AD%98%E7%9B%B8%E5%85%B3/%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F/</link><pubDate>Sat, 15 Oct 2022 00:00:00 +0000</pubDate><guid>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E7%BC%96%E8%AF%91%E5%92%8C%E5%86%85%E5%AD%98%E7%9B%B8%E5%85%B3/%E5%86%85%E5%AD%98%E7%9B%B8%E5%85%B3/%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F/</guid><description>什么是内存泄漏 程序块在[堆]中申请的[动态内存], 在程序块使用完成后没有得到[及时释放], 从而导致了堆中可使用的内存越来越少的现象
内存泄漏是逻辑上的概念, 指在程序运行期间, 总内存的可复用率变低的问题 内存泄漏主要指在[堆]上分配的变量所占用的内存, 因为[栈]中分配的变量, 会随着函数退出时自动回收. 而堆是动态分配的 不仅要考虑 malloc, calloc, realloc, new 等分配的内存是否得到了释放, 也要考虑使用系统资源或者在[堆]中创建的对象 内存泄漏的检测与预防的实践 通常会利用各种[内存检测工具]来检测内存泄漏, 其原理也是统计和分析内存的申请和释放的相关日志
以下通过 valgrind 的实践来介绍
valgrind 实践 valgrind 是 Linux 下, 开源代码 (GPL V2) 的仿真调试工具的集合, 包括以下工具:
Memcheck:</description></item><item><title>c++ 内存管理</title><link>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E7%BC%96%E8%AF%91%E5%92%8C%E5%86%85%E5%AD%98%E7%9B%B8%E5%85%B3/c++%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86/</link><pubDate>Thu, 13 Oct 2022 00:00:00 +0000</pubDate><guid>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E7%BC%96%E8%AF%91%E5%92%8C%E5%86%85%E5%AD%98%E7%9B%B8%E5%85%B3/c++%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86/</guid><description>ELF 文件: 可执行与可链接格式(Executable and Linkable Format)是一种用于可执行文件, 目标代码, 共享库和核心转储(core dump)的标准文件格式, 每个ELF文件都由一个ELF header 和紧跟其后的文件数据部分组成, 可以参考 ELF 文件的构成如下:
我们可以看见可执行程序内部都是分段进行存储的.
.text section: 代码段, 通常存放已编译程序的机器代码, 一般操作系统加载后, 这部分是只读的 .rodata section: 只读数据段, 此段的数据不可修改, 存放程序中会使用的常量 data section: 数据段, 主要用于存放已初始化的全局变量, 常量 bss section: 未初始化的全局标量, 仅是占位符, 不占据任何实际磁盘空间 目标文件格式区分初始化和非初始化是为了空间效率
操作系统在加载 ELF 文件时, 会按照文件结构, 依次读取每个 section 中的内容, 并将其加载到内存中, 同时为该进程分配空间, 并将 pc寄存器 指向代码段的起始位置, 然后启动进程
内存分区 C++ 程序在运行时, 会按照不同的功能划分不同的段: 栈, 堆, 全局/静态存储区, 常量存储区, 代码区
栈: 目前绝大多数CPU体系是基于栈来运行程序的, 栈中主要存放函数的局部变量, 函数参数, 返回地址等, 栈空间一般由操作系统进行默认分配或者程序指定分配, 栈空间在进程生存周期一直都存在, 当进程退出时, 操作系统才会对栈空间进行回收 堆: 动态申请的内存空间, 就是由 malloc 函数或者 new 函数分配的内存块, 由系统控制它的分配和释放, 可以在程序运行周期随时进行申请和释放, 如果进程结束后还没收释放, 操作系统会自动回收 全局区/静态存储区: 主要为 .</description></item><item><title>变量定义与生命周期</title><link>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E7%BC%96%E8%AF%91%E5%92%8C%E5%86%85%E5%AD%98%E7%9B%B8%E5%85%B3/%E5%8F%98%E9%87%8F%E5%AE%9A%E4%B9%89%E4%B8%8E%E7%94%9F%E5%AD%98%E5%91%A8%E6%9C%9F/</link><pubDate>Thu, 13 Oct 2022 00:00:00 +0000</pubDate><guid>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E7%BC%96%E8%AF%91%E5%92%8C%E5%86%85%E5%AD%98%E7%9B%B8%E5%85%B3/%E5%8F%98%E9%87%8F%E5%AE%9A%E4%B9%89%E4%B8%8E%E7%94%9F%E5%AD%98%E5%91%A8%E6%9C%9F/</guid><description>作用域 与 生命周期 这两个属性, 以 空间(作用域) 和 时间(生命周期) 两个不同的维度来描述一个 变量
作用域 一个变量可以被引用的范围
全局变量: 全局变量只需在一个源文件中定义, 就可以作用域所有的源文件, 其他不包含全局变量定义的源文件需要用 extern 关键字再次声明这个全局变量 静态全局变量: 与全局变量的区别在于如果程序包含多个文件的话, 它作用域定义它的文件里, 不能作用到其他文件里, 即被 static 关键字修饰过的变量具有文件作用域, 即使两个不同的文件都定义了相同的名字的静态全局变量, 它们也是不同的变量 局部变量: 它是自动对象(auto), 在程序运行期间不是一直存在,而是只在函数执行区间存在, 函数的依次调用执行结束后, 变量被撤销, 其所占的内存也被收回, 局部变量对于函数外部的程序说是不可见的, 实际是以 {} 为所用与 静态局部变量: 它直被初始化依次, 自从第一次被初始化知道程序运行结束都一直存在, 它只对定义自己的函数体始终可见, 只有定义该变量的函数内部可以使用和访问该变量 生命周期 变量可以存在(被引用)的时间段
全局变量: 在真个程序运行期间都会一直存在, 都可以随时访问, 当程序结束时, 对应的变量会自动销毁, 内存被系统回收 局部变量: 仅限于函数被调用期间, 当函数调用结束时, 该变量会自动销毁 静态局部变量: 在程序运行期间一直存在, 局部静态变量只能被初始化一次 从分配内存空间看 不同生命周期的变量, 在程序内存中的分布位置是不一样的, 内存分为 代码区(.text), 全局数据区(.data, .bss, .rodata), 堆区(heap), 栈区(stack), 不同的内存区域, 对应不同的生命周期
静态变量一般存储在数据段, 包括 data 段.</description></item><item><title>堆与栈</title><link>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E7%BC%96%E8%AF%91%E5%92%8C%E5%86%85%E5%AD%98%E7%9B%B8%E5%85%B3/%E5%A0%86%E4%B8%8E%E6%A0%88/</link><pubDate>Thu, 13 Oct 2022 00:00:00 +0000</pubDate><guid>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E7%BC%96%E8%AF%91%E5%92%8C%E5%86%85%E5%AD%98%E7%9B%B8%E5%85%B3/%E5%A0%86%E4%B8%8E%E6%A0%88/</guid><description/></item><item><title>内存对齐</title><link>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E7%BC%96%E8%AF%91%E5%92%8C%E5%86%85%E5%AD%98%E7%9B%B8%E5%85%B3/%E5%86%85%E5%AD%98%E5%AF%B9%E9%BD%90/</link><pubDate>Thu, 13 Oct 2022 00:00:00 +0000</pubDate><guid>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E7%BC%96%E8%AF%91%E5%92%8C%E5%86%85%E5%AD%98%E7%9B%B8%E5%85%B3/%E5%86%85%E5%AD%98%E5%AF%B9%E9%BD%90/</guid><description>内存对齐 计算机中内存的地址空间是按照 byte 来划分的, 从理论上讲对任何类型变量的访问, 可以从内存中的任意地址开始, 但实际情况是: 在访问特定类型变量的时候通常在特定的内存地址访问, 这就需要对这些数据在内存中存放的位置进行限制, 各种类型数据按照一定的规划在空间上排列, 而不是顺序地一个接一个地排放, 这就是对齐, 编译器将程序中地每个 数据单元 的地址安排在机器字的整数倍的地址指向的内存之中
为什么要内存对齐 主要是由于 CPU 的访问内存的特性决定, CPu 访问内存时并不是以字节为单位来读取内存, 而是以机器字长为单位, 实际机器字长由 CPU 数据总线宽度决定的, 实际 CPU 运行时, 每一次控制内存读写信号发生时, CPU 可以从内存中读取数据总线宽度的数据, 并将其写入到 CPU 的通用寄存器中
比如, 32位cpu, 机器字长为 4字节, 数据总线宽度为 32 为, 如果该 CPU 的地址总线宽度也为 32 位, 则其可以访问的地址空间为[0, 0xffffffff]
内存对齐的主要目的是为了减少 CPU 访问内存的次数, 加大 CPU 访问内存的吞吐量
假设: 读取 8 个字节的数据, 按照每次读取 4 个字节的速度, 则 8 个字节需要 CPU 耗费 2 次读取操作
CPU 始终以字节访问内存, 如果不进行内存对齐, 很可能增加 CPU 访问内存的次数</description></item><item><title>智能指针的创建</title><link>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E7%BC%96%E8%AF%91%E5%92%8C%E5%86%85%E5%AD%98%E7%9B%B8%E5%85%B3/%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88%E7%9A%84%E5%88%9B%E5%BB%BA/</link><pubDate>Wed, 12 Oct 2022 00:00:00 +0000</pubDate><guid>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E7%BC%96%E8%AF%91%E5%92%8C%E5%86%85%E5%AD%98%E7%9B%B8%E5%85%B3/%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88%E7%9A%84%E5%88%9B%E5%BB%BA/</guid><description>make_unique 和 make_share 优先使用 std:make_unique 和 std:make_shared, 而非使用 new;
make_unique: 减少代码量, 能够加快编译速度, 定义两边类型时, 编译器需要进行类型推导, 会降低编译速度, 某些意外情况下可能会导致内存泄漏, 但是 make_unique 不允许自定析构器, 不接受 std:initializer_list 对象 make_shared: 这个主要是可以减少对堆中申请内存的次数, 只需要申请一次即可</description></item><item><title>智能指针简介与使用</title><link>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E7%BC%96%E8%AF%91%E5%92%8C%E5%86%85%E5%AD%98%E7%9B%B8%E5%85%B3/%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88%E7%AE%80%E4%BB%8B%E4%B8%8E%E4%BD%BF%E7%94%A8/</link><pubDate>Wed, 12 Oct 2022 00:00:00 +0000</pubDate><guid>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E7%BC%96%E8%AF%91%E5%92%8C%E5%86%85%E5%AD%98%E7%9B%B8%E5%85%B3/%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88%E7%AE%80%E4%BB%8B%E4%B8%8E%E4%BD%BF%E7%94%A8/</guid><description>智能指针 为了解决动态内存分配时带来的内存泄露以及多次释放同一块内存空间而提出的
传统的指针在申请完成后, 必须要调用 free 或 delete 来释放指针, 否则容易产生内存泄漏的问题
smart pointer 遵循 RAII 原则, 当 smart pointer 对象创建时, 即为该指针分配了相应的内存, 当对象销毁时, 析构函数会自动释放内存, 需要注意的是, 智能指针不能像普通指针那样支持加减运算(迭代操作)
智能指针的类型 unique_ptr: 独享所有权的智能指针, 资源智能被一个指针占有, 该指针不能拷贝构造和赋值, 但可以进行移动构造和移动赋值构造(调用 move() 函数), 即一个 unique_ptr 对象赋值给另一个 unique_ptr 对象, 可以通过该方法进行赋值 shared_ptr: 与 unique_ptr 不同的是, shared_ptr 中的资源可以被多个指针共享, 但是多个指针指向同一个资源不能被释放多次, 因此使用计数机制表明资源被几个指针共享; 通过 use_count() 查看资源的所有者的个数, 可以通过 unique_ptr, weak_ptr 来构造, release 释放资源的所有权, 同时将计数减一, 当计数减为 0 时候,会自动释放内存空间, 从而避免了泄露 需要注意的是, shared_ptr 并不是线程安全的, 但 shared_ptr 的计数是原子操作实现的, 利用 atmoic CAS 指令实现 通过 shared_ptr 的内存模型, 当引用计数和 weak cout 同时为 0 时, shared_ptr 对象才会被最终释放掉 通过查看 shared_ptr 的源代码可以看到如下, shared_ptr 实际成员包含两个指针, 一个指向对象资源的指针 ptr, 另一个指向管理区域的指针 ___cntrl_, 具体 __cntrl_ 指针的区域包括 deleter, allocator, shared_ptr 对象的引用计数, weak_ptrs 的对象的引用计数</description></item><item><title>什么是类</title><link>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/%E4%BB%80%E4%B9%88%E6%98%AF%E7%B1%BB/</link><pubDate>Thu, 28 Apr 2022 00:00:00 +0000</pubDate><guid>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/%E4%BB%80%E4%B9%88%E6%98%AF%E7%B1%BB/</guid><description/></item><item><title>c++ 在visual studio和XCode上的差异</title><link>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/different_on_win_and_mac/</link><pubDate>Sat, 18 Sep 2021 00:00:00 +0000</pubDate><guid>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/different_on_win_and_mac/</guid><description>描述 Visual Studio 和 Xcode 在处理 C++ 方面的主要差异在于它们所使用的编译器、工具链以及它们各自的集成开发环境（IDE）特性。以下是一些关键的不同点：
1. 编译器 Visual Studio 使用的是 Microsoft 的 C++ 编译器（MSVC），该编译器有自己的语法扩展和特性，虽然它也支持大部分的 C++ 标准。 Xcode 使用的是 LLVM/Clang 编译器，这是一个开源编译器，其设计目标之一就是提供清晰的诊断信息，并且它对现代 C++ 标准的支持非常好。 2. IDE 特性 Visual Studio 提供了丰富的功能，如智能感知（IntelliSense）、实时模板编辑、重构工具等，这些都是针对微软的开发环境优化的。 Xcode 也有类似的功能，如自动补全（Code Completion）、实时错误提示（Live Issue Identification）等，这些功能针对 macOS 和 iOS 开发进行了优化。 3. API 和库支持 在 Visual Studio 中，可以使用 Windows API 来开发应用程序，以及 Microsoft 提供的其他库。 在 Xcode 中，更可能使用 Cocoa 或 Cocoa Touch（对于 iOS 开发）API，以及 BSD 和 POSIX 的相关库。 4. 构建系统 Visual Studio 使用 MSBuild 构建系统来管理项目。 Xcode 使用自己的构建系统，不过也可以配置使用外部构建工具如 CMake。 5.</description></item><item><title>如何避免写 c 代码的时候 Unhandled Exception:Stack overflow</title><link>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/exception/exception_stack-overflow/</link><pubDate>Tue, 23 Mar 2021 00:00:00 +0000</pubDate><guid>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/exception/exception_stack-overflow/</guid><description>避免 C 程序出现堆栈溢出异常（Stack Overflow Exception）通常需要注意以下几点 优化递归算法：如果程序中使用了递归，确保递归深度可控，避免无限递归或递归层次过深。 减少局部变量的数量和大小：函数内部声明的局部变量会占用堆栈空间，特别是对于大型数组或结构体。优化代码，尽量减少或优化局部变量的使用。 动态内存分配：考虑使用动态内存分配函数（如 malloc()、calloc()、realloc()）来管理大型数据结构，而不是将它们放在栈上。 避免过度递归：如果递归是必须的，确保递归调用的深度受到控制，并考虑是否可以改写为迭代方法。 增加堆栈空间限制：虽然不推荐频繁增加堆栈空间的大小，但是可以通过编译器或操作系统的设置，适当增加堆栈空间的限制。这种方法应谨慎使用，并仅当确实需要时才考虑。 使用静态分配：对于较小的数据结构或变量，优先使用静态分配（即在函数外部或静态存储区声明变量）而不是动态分配或放在堆栈上。 编译器优化：现代编译器通常会进行优化，包括函数调用和参数传递的优化。确保使用编译器的优化选项，以提高程序的效率和性能。 定期检查和测试：对于可能存在堆栈溢出风险的代码，定期进行检查和测试是很重要的，以发现潜在的问题并进行优化和改进。</description></item><item><title>左值和右值</title><link>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E8%AF%AD%E8%A8%80%E7%89%B9%E6%80%A7%E7%9B%B8%E5%85%B3/%E5%B7%A6%E5%80%BC%E5%92%8C%E5%8F%B3%E5%80%BC/</link><pubDate>Thu, 24 Oct 2019 00:00:00 +0000</pubDate><guid>https://choy-mutao.github.io/%E5%8D%9A%E5%AE%A2_60/c++%E7%AC%94%E8%AE%B0/leetcode/c++%E8%AF%AD%E8%A8%80%E7%89%B9%E6%80%A7%E7%9B%B8%E5%85%B3/%E5%B7%A6%E5%80%BC%E5%92%8C%E5%8F%B3%E5%80%BC/</guid><description/></item></channel></rss>