C++ 内存对齐

C/C++内存对齐详解 内存对齐规则 对齐系数(也叫对齐模数):gcc中默认 #pragma pack(4),可以通过预编译命令 #pragma pack(n),n = 1,2,4,8,16 来改变这一系数 有效对齐值:是给定值 #pragma pack(n) 和结构体中 最长数据类型长度中较小的那个。有效对齐值也叫 对齐单位 规则一:结构体第一个成员的偏移量 offset 为 0,以后每个成员相对于结构体首地址的 offset 都是该成员大小与有效对齐值中较小那个的整数倍,如有需要编译器会在成员之间加上填充字节 规则二:结构体的总大小为 有效对齐值 的整数倍,如有需要编译器会在最末一个成员之后加上填充字节 注意:上面两条规则都需要得到满足 注意:成员变量首地址偏移和对齐都是与【有效对齐值】进行比较,而有效对齐值是对齐系数与结构体中最长数据类型中的较小者 // 64 位程序 struct { int i; char c1; char c2; }x1; struct{ char c1; int i; char c2; }x2; struct{ char c1; char c2; int i; }x3; struct { short i; char c1; char c2; }y1; struct{ char c1; short i; char c2; }y2; struct{ char c1; char c2; short i; }y3; int main() { printf("%ld\n",sizeof(x1)); // 输出8 printf("%ld\n",sizeof(x2)); // 输出12 printf("%ld\n",sizeof(x3)); // 输出8 cout << "----------------" << endl; printf("%ld\n",sizeof(y1)); // 输出4 printf("%ld\n",sizeof(y2)); // 输出6 printf("%ld\n",sizeof(y3)); // 输出4 return 0; } 既要考虑首地址偏移,又要是有效对齐值(对齐单位)的整数倍 y1 y2 y3结构体:...

January 6, 2022 · 1 min · Rick Cui

自定义智能指针

使用模板类 重载 -> 操作符 重载 * 操作符 template<typename T> class MyAutoPtr{ public: MyAutoPtr(T * t){ ptr = t; } ~MyAutoPtr(){ if(ptr != NULL){ delete ptr; ptr = NULL; } } T* operator->(){ // 相当于 ptr-> , 所以返回 ptr 指针即可,将所有操作转发给真正的指针变量 return ptr; } T& operator*(){ // 相当于 (*ptr) ,所以返回 ptr 指向的对象引用即可 return *ptr; } private: T* ptr; }; class A{ public: A(int a){ cout << "A(int)..." << endl; this->a = a; } ~A(){ cout << "~A()....

January 4, 2022 · 1 min · Rick Cui

C++ 多线程

一、进程与线程 进程是资源分配和调度的一个独立单位;而线程是进程的一个实体,是 CPU 调度和分配的基本单位 同一个进程中的多个线程的内存资源是共享的,各线程都可以改变进程中的变量。因此在执行多线程运算的时候要注意执行顺序 二、并行与并发 并行(parallellism)指的是多个任务在同一时刻同时在执行 并发(concurrency)是指在一个时间段内,多个任务交替进行。虽然看起来像在同时执行,但其实是交替的 三、多任务处理 多线程是多任务处理的一种特殊形式,一般情况下,有基于进程和基于线程的两种类型的多任务处理方式。 基于进程的多任务处理是程序的并发执行 基于线程的多任务处理是同一程序的片段的并发执行 四、C++11 线程管理 thread 库 C++11 提供了多线程库,使用时需要 #include <thread> 头文件,该头文件主要包含了对线程的管理类 std::thread 以及其他管理线程相关的类 每个应用程序至少有一个进程,而每个进程至少有一个主线程,除了主线程外,在一个进程中还可以创建多个子线程。每个线程都需要一个入口函数,入口函数返回退出,该线程也会退出,主线程就是以 main 函数作为入口函数的线程 主线程退出后,运行中的子线程也会被销毁 std::thread 的构造函数需要的是可调用(callable)类型,除了函数外,还可以调用 lambda 表达式、重载了 () 运算符的类的实例 把函数对象传入 std::thread 时,应传入函数名称(不带括号) 当启动一个线程后,一定要在该线程 thread 销毁前,调用 join() 或者 detach(),确定以何种方式等待线程执行结束 detach 方式,启动的线程自主在后台运行,当前的代码继续往下执行,不等待新线程结束 join 方式,等待关联的线程完成,才会继续执行 join() 后的代码 在以 detach 的方式执行线程时,要将线程访问的局部数据复制到线程的空间(使用按值传递),一定要确保线程没有使用局部变量的引用或者指针,除非你能肯定该线程会在局部作用域结束前执行结束 1、调用全局函数启动线程 #include <thread> using namespace std; void func(int i){ cout << i << endl; } int main() { for(int i = 0; i < 4; ++i){ // 创建一个线程t,第一个参数为调用的函数,第二个参数为传递的参数 thread t(func, i); // 表示允许该线程在后台运行 t....

January 4, 2022 · 2 min · Rick Cui

临时变量不能绑定 no-const 左值引用类型

error: cannot bind non-const lvalue reference of type ‘T&’ to an rvalue of type ‘T’ 如果一个参数是以非 const 引用传入,c++ 编译器就有理由认为程序员会在函数中修改这个值,并且这个被修改的引用在函数返回后要发挥作用。 但如果你 把一个临时变量当作非 const 引用参数传进来,由于临时变量的特殊性,程序员并不能操作临时变量,而且临时变量随时可能被释放掉。 所以,修改一个临时变量是毫无意义的。据此,c++ 编译器加入了临时变量不能作为非 const 引用的这个语义限制。 c++ 中临时变量是右值类型,不能取引用,只能在当前行使用,不能作为非 const 的引用参数 std::move() 返回的也是一个右值 对于临时变量或字面量的右值引用可以使用 cosnt & 或 && 类型的参数接收 一般来说,右值的地址不能通过解引用来获得,因为它们是字面量,或者因为它们本质上是临时的(例如由函数或显式构造函数调用返回的值)。通过将一个对象传递给这个函数,可以获得一个引用它的右值。 class T{ public: T(int v){ a = v; } T operator+(T &t){ T temp = this->a + t.a; return temp; } void printT(T &t){ cout << "a = " << t....

January 4, 2022 · 1 min · Rick Cui

操作符重载

还有类型转换函数 #include <iostream> using namespace std; class Complex{ // friend Complex complexAdd(Complex &c1, Complex &c2); // friend Complex operator+(Complex &c1, Complex &c2); // friend Complex& operator+= (Complex &c1, Complex &c2); friend ostream& operator<<(ostream &os, Complex &c); friend istream& operator>>(istream &is, Complex &c); public: Complex(){ cout << "Complex()..." << endl; a = 0; b = 0; } Complex(int a, int b){ cout << "Complex(int, int)..." << endl; this->a = a; this->b = b; } ~Complex(){ cout << "~Complex()....

January 3, 2022 · 4 min · Rick Cui