GCC 使用笔记

1. 前言 和其他现代语言不同,C/C++ 的编译系统是散装的,由众多工具组成。比如包管理器有 Conan,Vcpkg 等等,构建系统有 Cmake, Ninja, autotools, scons 以及原始的 Makefile. 也有想要一统天下的 Bazel 说句题外话,我深入用过 Conan + Cmake 和 Bazel 两种方案,并在公司负责这两种编译系统的开发和维护。相比来说,我认为前者方案适合纯 C/C++/cuda 项目,非常方便好用,官方提供的库菜单很多,对交叉编译支持完备。后者则更适合大型、复杂项目,以及多语言,多个模块项目,Bazel 目标依赖系统做的很好,上游目标改动后会触发下游目标编译,这点是 Conan 和 Cmake 系统无法做到的。 但是 Bazel 对 C/C++ 的交叉编译没有支持,Cuda 规则没有官方提供,编译器规则开发经过多次迭代,可能前几年使用的在新版中已经无法使用,相对维护成本高很多。 回到正题,无论采用那种构建系统,最后总都归到编译器,主流的有 GCC 和 Clang 两种,我只用过 GCC,因此本文主要记录一些使用中觉得比较重要的点,供自己记录参考 2. 依赖问题 依赖问题应该是编译中大家遇到最多的问题。 简单来说,编译过程分为 编译 和 链接 两步,如果你写的代码没问题,那我们在进行编译时主要会遇到以下三种错误 编译的时候报找不到头文件 编译的时候报找不到依赖库 编译过了运行时候报找不到对应符号 下面我们分别讨论如何解决上述问题 2.1 找不到头文件 这个问题最容易解决,编译器默认只会查询 /usr/include, /usr/local/include 等几个标准路径下的头文件,将这些路径和代码里引用的相对路径拼起来就是头文件位置。 而对于放在非标准路径下的头文件,我们可以通过 -I 参数告诉编译器,如下 gcc -I{include_dir} -o my_program my_program.c 还有一种方法是使用 isystem gcc -isystem {include_dir} -o my_program my_program....

May 17, 2024 · 1 min · 192 words · Croak

C++ 知识点随意笔记

说明 本文主要记录一些 C++ 知识点,记录的知识点没有任何规律和归纳,好记性不如烂笔头,供自己查看。 知识点笔记 1. C++ list 和 vector 区别 Vector 是数组,List 是链表 具体来说,区别如下 Chatgpt 答案 数据结构: vector:基于数组的容器,提供快速的随机访问能力,即可以在常数时间内访问任何元素。然而,向vector中间插入或删除元素可能较慢,因为这可能导致内存中大量元素的移动。 list:基于双向链表的容器,不支持直接的随机访问。这种结构使得在任何位置(包括中间)插入和删除元素都非常快速,因为这仅仅涉及到修改指针。 内存分配: vector:通常以块的形式分配内存,每当其空间不足以容纳更多元素时,就会进行重新分配和复制整个数组到新的内存块。这可以导致较大的内存移动和复制开销。(所以最好使用 reserve 方法提前预留空间,以免每次添加元素都需要重新分配内存) list:通常为每个新元素单独分配内存。因此,它的内存分配更为频繁,但删除或添加元素时无需移动其他元素。 性能考量: vector:由于数据连续存储,对缓存友好,这可以提升遍历性能。适合用于经常需要访问元素的场景。 list:由于元素不连续存储,每次访问元素都可能导致缓存未命中,遍历性能较低。但在需要频繁插入和删除的情况下表现更好。 功能特性: vector 支持直接通过索引访问元素(例如 v[3]),而 list 不支持这种操作,必须通过迭代器遍历访问。 list 允许进行快速的双向遍历,而 vector 则优化了向后遍历。 适用场景: vector:适合元素数量变化不大,或主要在末尾添加或删除元素的情况。 list:适合元素数量频繁变动,或在列表中间频繁插入和删除元素的场景。 2. C++ 类的拷贝创建,如果成员中有指针,怎么做 该问题本质是说如何在拷贝创建是否需要进行深拷贝 所以需要将这个问题分开讨论 不需要对指针进行深拷贝,即创建出的实例和之前的实例共用这个地址里的数据,这样减少拷贝。但是如果该地址成员是一个 raw ptr, 会很难处理。如果在析构中 free,会出现 double free,如果不 free,会内存泄漏。所以最好是使用 shared_ptr 解决该类问题 需要进行深拷贝,即你需要创建出的实例拷贝地址数据,创建完全新的。例如 stl 中 vector 就是如此。这时需要定义拷贝构造函数和拷贝赋值运算符,以实现深拷贝。 class MyClass { public: int* data; // 构造函数 MyClass(int val) : data(new int(val)) {} // 拷贝构造函数 auto b = MyClass(a) MyClass(const MyClass& other) : data(new int(*other....

May 15, 2024 · 1 min · 163 words · Croak

C++ Rust Copy and Move

1. 介绍 今天再次读到 Rust 所有权章节时有感,想在此简单讨论下 C++ 和 Rust 的深浅拷贝机制。Rust 和 C++ 在很多特性上十分相似,这里说的相似并不是说设计一样,而是说其想要解决的问题是相同的,但是采用的方法不同。深浅拷贝就是一个例子。 2. 深浅拷贝 在编程中,深拷贝和浅拷贝是处理对象和数据结构时常见的两种拷贝方式,它们在拷贝复杂对象(如,包含其他对象或动态分配的内存的对象)时的行为上有本质的区别。 浅拷贝 浅拷贝(Shallow Copy)仅复制对象的顶层结构,不复制对象内部的深层数据。如果对象中含有指针指向动态分配的内存,浅拷贝会复制指针的值,但不会复制指针所指向的内存。因此,原始对象和拷贝对象会共享部分数据。在某些情况下,这可能导致问题,如: 如果一个对象被释放(例如,析构函数被调用),它可能会释放共享的内存,使得另一个对象中的相应指针变成悬挂指针(指向已经被释放的内存)。 对共享数据的修改会影响到所有共享此数据的对象。 浅拷贝在处理简单对象或仅需要复制值而不需要独立修改数据的场景中比较合适。 深拷贝 深拷贝(Deep Copy)不仅复制对象的顶层结构,还递归地复制对象内部的所有内容,包括指向的对象和动态分配的内存。这意味着原始对象和拷贝对象在内存中是完全独立的;对一个对象的修改不会影响到另一个对象。 深拷贝通常用于: 需要复制的对象包含了指向动态分配内存的指针。 想要完全独立地修改原始对象和拷贝对象,而不希望它们之间有任何数据上的依赖。 深拷贝相比浅拷贝在实现上更复杂,可能也更耗费资源,因为需要逐个复制对象内部的所有元素,并且可能涉及到递归拷贝。 Move Move 直接将对象的顶层结构移动到新的变量中,之前的变量会失效 从内存的角度 浅拷贝: 只复制栈上的值,也就是该对象的地址,不复制堆上的数据 深拷贝: 复制栈上的值,不复制堆上的数据 Move: 直接将栈上的值赋给新的变量名,之前的失效 3. C++ 中的深浅拷贝和 Move 深拷贝 在现代 C++ 中 (C++11 及之后),有一些典型类型都是深拷贝 std::string std::vector std::map … 使用该类型赋值是需要注意 浅拷贝 C/C++ 中直接使用地址就是一种浅拷贝,这种浅拷贝存在风险,及开发者需要手动管理该地址对应的内存,如果忘记 free 或者提前 free 都会内存错误,造成 coredump. 当然,在现代 C++ 中,智能指针可以解决该问题,我们可以使用 shared_ptr 进行浅拷贝 Move C++ 中 std::move 实现了 Move 功能,该功能将所有权转换到新的变量...

March 21, 2024 · 1 min · 128 words · Croak

CPP cast

Intro Casting in C++ is a way to convert a value from one data type to another. There are four casting operators in C++, each serving different purposes and with different levels of safety and functionality. This paper will introduce when and how to use them with examples. std::static_cast cppreference explanation It is the most commonly used cast in C++. It is used for conversions between compatible types, you can think of it as converting between basic c++ types....

February 20, 2024 · 3 min · 441 words · Croak

Gdb quick start

Intro This paper will introduce you how to quickly use gdb GDB Commands Debug program Debug program with gdb is very simple, just follow below steps Build your program with debug info gcc {your options} -o0 -g3 -ggdb Start gdb # without args gdb {program} # with args gdb --args {program} {args...} Add breakpoints (gdb) b {filename}:{line} # for example (gdb) b test.cc:20 # the gdb will auto find file by filename and program debug info # if gdb can't find file, add file search dir (gdb) dir {filepath} Run program and debug # In gdb command line (gdb) run # when program reach one breakpoint, you can print symbol (gdb) print {symbol name} # even with object's function, such as (gdb) print list....

February 20, 2024 · 2 min · 271 words · Croak