美文网首页
11-12 逻辑 位 递增 / 自由存储 / lambda /

11-12 逻辑 位 递增 / 自由存储 / lambda /

作者: my_passion | 来源:发表于2022-06-21 19:14 被阅读0次

第11章 选择适当的 操作

11.1 逻辑 / 位 / 递增减

1 逻辑

    && || !
    
    (1) 操作数
    
        指针/算术 类型
    
    (2) 短路特性: 逻辑上 需要 时, && 和 || 才会对 第2实参求值
    
        while (p && !whitespace(*p) ) ++p;

2 位

    & | ~(非) ^(异或) >> <<
    
    含义 & 应用(位向量)
        
            unsigned 整型 
                每1位 表示 集合的1个成员

    (1) & 求 交 
            
            从 32 位 int 中 提前中间 16位
            
            constexpr unsigned short middle(int n)
            {
                static_assert( sizeof(int) == 4, "unexpected int size" );
                static_assert( sizeof(short) == 2, "unexpected short size" );
                
                return (n >> 8) & 0xFFFF;
            }
            
            int x = 0xFF00FF00;  // 假定 sizeof(int) = 4
            short y = middle(x); // y = 0x00FF
            
    (2) | 求 并
        
        在 现有状态 上 添加内容
            
            enum ios_base::iostate
            {
                goodbit = 0, eofbit = 1, failbit = 2, badbit = 4
            };
            
            state |= eofbit;
    
    (3) ~ 求 补
        
    (4) ^ 异或: 相异( 有变化 ) 为 1
             
        判 `状态 是/否 变化`
        
            现值 与 旧值 异或 => result 作 条件 转换为 `bool 值 true/false` 
            
            if ( 结果 可转换为 true <=> 至少有 1 位 有变化 => 有变化 )
        
            int old = cin.rdstate(); // rdstate() 返回 状态
            // ...
            if( cin.rdstate() ^ old ) // 有变化吗 ? 

3 递 增/减

    (1) char* strcpy(char*, const char*); // <string.h>
    
        循环 拷贝 以 0 结尾的 C 风格字符串
    
        void cpy(char* p, const char* q);
        
        6 个版本
        
        1) `读 2 次 字符串`
            
            先求 strlen(): 第 1 次 读
            
            int length = strlen(q);
            for(int i = 0; i <= length; i++)
                p[i] = q[i];
                
            |
            |   性能提升
            |/
            
        2) `读 1 次 字符串`
            
            没遇到 结束符 0 时, copy
            
            + 
            
            遇到时, 跳出 + dst 置 结束符 0
            
            for(int i = 0; q[i] != 0; i++)
                p[i] = q[i];
            p[i] = 0; 
        
        3) 用 `指针操作 替换 数组下标操作`
        
            while (*q != 0)
            {
                *p = *q;
                p++;
                q++;
            }
            *p = 0;
        
        4) `后置递增: 允许 先使用值 再 递增`
            
            while(*q != 0)
                **p++ = *q++;
            *p = 0;
        
        5) `赋值表达式 的 值` 是 右操作数的值/赋值后左操作数 的值 
        
            *p++ = *q++ 的值是 *q
            
            => while(*p++ = *q++) 终止条件是 *q == 0
            
                1> copy: *p = *q -> 结果 *q 转换为 bool 值 -> 编译器 先存着
                2> 递增
                3> 判 递增前 *q (若 0) 转换的 bool 值 -> 若 false -> exit => 结束符 已 copy 给 dst
        
            while(*p++ = *q++)
            {}
        
        6) 循环体空 => 略去
        
            while(*p++ = *q++);

11.2 自由存储

(1) 引入

    命名对象 lifetime 由其 scope 决定
            |
            |   希望 `分离 对象 与` 对象创建语句所在 `scope`
            |/
        new 运算符
            分配的对象 `位于 自由存储 之上`
    
(2) delete 运算符

    1) delete / delete[] nullptr; 什么也不做
            
    2) 被 delete 的对象类型 含 dtor
    
        delete 将 
            
        [1] 调用 该 dtor 
            
            释放 该对象 所管理的资源 (若有)
            
        [2] 释放 该对象所占内存空间 
            
            以供后续使用

1 内存管理

    (1) 自由存储 的 问题
    
        1) 对象泄露
            
            new 但忘了 delete ---> 可能资源耗尽
            
        2) 过早 delete
            
        3) 重复 delete
            
            同一对象被释放 2 次
                2 次调用 dtor      
    
    (2) 解决
    
        1) 优先`scoped 对象`
            
            必要时再 自由存储

        2) RAII  
        
            [1] 将 自由存储 对象 的 `ptr` 放到 `handle 对象` 中 
                                            |
                                            |   尽可能 作 
                                            |/
                                        `scope 内 变量`
             
                string / vector / unique_ptr / shared_ptr
            
            [2] 很多用 自由存储 的场合
            
                都可以用 `move 语义` 替代
                
                    从 函数返回 表示 大对象的 句柄 即可
                
            RAII
                字面含义
                    句柄 ctor 中 创建资源, dtor 中 释放资源
                    
                本质含义
                    让 句柄 own 资源, dtor 中 释放资源 
                    
                    =>
                    `资源 与 句柄 的 lifetime 管理 统一起来`
                    
                    只要不泄露句柄, 就能不泄露资源
                    
                    至于让 ctor 创建资源, 还是 把资源创建好, 再交给 ctor 保管, 无优劣之分
                    
                    可用 Factory Method 返回 创建好的资源, client 就不用管 里面怎么实现了

2 除非必须直接使用 char*, 否则一般而言, std::string 更好

    (1) new 创建 对象的数组
    
        char* save_string(const char* p)
        {
            char* s = new char[strlen(p) + 1];
            strcpy(s, p);
            return s;
        }
        
        int main(int argc, char* argv[])
        {
            // ...
            char* p = save_string( argv[1] );
            // ...
            delete[] p;
        }
            |
            |   (2) 
            |       
            |       简化 + 不操心 delete / delete[]
            |/
        string save_string(const char* p)
        {
            return string{p};
        }
        int main(int argc, char* argv[])
        {
            // ...
            string s = save_string( argv[1] );
            // ...
        }
        
    (2) delete / delete[] 必须清楚 分配的对象 有多大
    
        new 分配的对象 比 静态对象 所占空间 稍大(至少存得下 对象 size)

3 重载 new

    默认下, new 运算符 在 自由存储 上 创建对象
        |
        |/
    想在 `指定空间` 分配对象
            |
            |   重载 new: placement new <new>
            |/
    目标空间地址 作 参数 
        
    void* buf = reinterpret_cast<void*>(0xF00F); // 明确的地址
    
    void* operator new(size_t, void* p) { return p; } // placement new <new>
                        /            |\
                 _ _ _ /_ _ _ _ _ _ _|
                |     / 
                |/   /
    X* p = new(buf) X; // 在 buf 处 构建 X, 
                       // 调 operator new( sizeof(X), buf )
    // ===
    clas B
    {
    public:
        virtual void* alloc(size_t) = 0;
        virtual void free(void*) = 0;
        // ...
    };
    
    void* operator new(size_t sz, B* pB)
    {
        return pB->alloc(sz);
    }
    
    extern B* gpPersistent;
    extern B* gpShared;
    
    void g(int n)
    {
        X* p = new(gpPersistent) X(i); // 在 持续存储 上 分配 X
        X* q = new(gpShared) X(i);     // 在 共享内存 上 分配 X
        // ...
    }
    
    // 销毁: 显式调用 dtor
    void destory(X* pX, B* pB)
    {
        pX->~X();      // 显式调用 dtor: 清理资源
        pB->free(pX); // 释放内存
    }

11.3 列表 {}

        {} 列表
            |
            |   及其 底层数组 `lifetime` 与 initializer_list 对象 相同
            |/
    编译器 构建 临时数组 作 `底层数组`
                    |   
                    |   不可修改
                    |/
    构建 `initializer_list 对象` 管理之
        
        小对象: 以 值传递 可行

11.4 lambda 表达式


(1) `回调`
    1) 操作 作 实参 传给 算法
    
    2) 再通过 function pointer / function object / lambda 
        
        去调 函数调用运算符 operator() (paraList)
        
        function pointer 本身不能 持有 state, 只能由 算法 提供

(2) mutable 修饰符

    默认下, operator() 是 const: const 含义

    lambda body 内 
        不修改 通过 `值捕获的变量 的 copy`
        note:而不是 不修改 通过引用捕获的变量的状态           

(3) vs. 函数      
    
    函数 无 捕获功能
    
    => lambda 可作 局部函数, 普通函数不能
    
1 实现

    函数对象

    void f(const vector<int>& v, ostream& os, int n)
    {
        for_each(v.begin(), v.end(), 
                 [&os, n](int x) { if(x % n == 0) os << x << '\n';  } );
    }               |         | 
                    |         
    class F         |         |
    {              /|           
        ostream& os;|         |
        int n;      |         
    public:        /          |
        F(ostream& s, int nn):  os(s), n(nn) {}
                              /
        void operator() (int x) const
        {
             if(x % n == 0) os << x << '\n';  }
        }
    };
    void f(const vector<int>& v, ostream& os, int n)
    {
        for_each(v.begin(), v.end(), F{os, n} );
    }
    
2 捕获

    (1) lambda 主要用途
        
        `封装` 部分代码 以便将其用 `作 参数`
        
        定义处使用
            而不用像 函数/函数对象 那样, 分离定义与使用
    
    (2) 3种捕获形式
    
        1) 隐式捕获
        
            [=] 引用传递
            [&] 值传递
            
        2) 显式捕获
        
            [捕获列表]
                可捕获 this
                
        3) 混合 隐式 与 显式 捕获
            
            捕获列表中 `没出现的 名字(外层 context 局部变量)`
                通过 引用 / 值 传递, 列出的 通过 值/引用
                     /       |
                    /       [=, 捕获列表]
                   /                  | 
                [&, 捕获列表]       必须 以 & 为 前缀
                        |           => 不可出现 this
                        |/
                    不能 以 & 为 前缀
                    => 可出现 this
                    
    (3) 很多时候, 既可 捕获, 也可 传实参
            
    (4) lambda 与 this
    
        1) 捕获 this: [this]
                        
            通过 this 访问 `类对象的 成员`
        
        2) [this] 与 [=] 不兼容
        
            => 多线程 中易发生 `竞争条件`
        
        
        class A
        {
            int value;
            int result;
        public:
            void execute()
            {
                [this]() { result = f(value); }
            }
            
            int f(int v);
        };

11.5 显式 类型转换 / 强转 casting

1 列表 T {v}

    (1) 用 值 v 构造 T 类型的临时对象

    (2) 好处
        
        只执行 "行为良好的"  类型转换
                
        存在
        [1] v 向 T 的 `非窄化` 类型转换, 或
    
        [2] 正确的 `T 类型  ctor`
            T{} 
                构造函数: 类型 T  默认值

2 4种 命名转换

    const_cast
        为某些 const 对象 获得 写入权利
        
        仅对 const / volatile 
        
    reinterpret_cast
        改变 位模式 的 含义
            
        `非关联类型 间` 转换
            
            1> 整数 -> 指针
            
            2> 指针 -> 非关联指针
        
            IO_device* p = reinterpret_cast<IO_device*>(0xFF00); // 0xFF00 处的 设备
            
    static_cast
    
        `关联类型 间` 转换
            
            `原始内存` 指针转换
            
                int* p = static_cast<int*>(my_allocator(100) );
            
            1> 整数 -> 枚举 / 浮点 -> 整数
            
            2> 指针 -> 同一类层次中 其他指针
            
            3> (单参数) explicit ctor
                    |
                    |   
                    |/
                X::X(T v)
                
            4> `类型转换运算符`
                    |
                    |   X(类类型) 向 T(任意类型) 的 类型转换
                    |/
                X::operator T() 

    dynamic_cast
        `动态检查` 类层次 关系
    
        执行 `运行时检查` // static_cast 不做 运行时检查

3 C 风格 转换

    (T)v
      |
      |/
    可执行 命名转换 中 除 dynamic_cast 之外的 3种转换的 任意组合

4 函数形式 的转换

    T(v)
     |
     |  对 内置类型 T <=>
     |/
    (T) V       

第12章 函数

12.1 函数声明

    1 返回 void 
        
        并不存在 void 值, 但可 `调用 void 函数` 作 `另一 void 函数 的 返回值`
            
            void g(int*p);
            
            void h(int* p)
            {
                // ...
                return g(p); // <=> g(p); return;
            }
        
    2 inline 函数
        
        遵循 ODR(一次定义法则) 
        
            定义出现在 多个编译单元 (通常是因为 inline 放 头文件)
                定义必须保持一致

    3 constexpr 函数
        
        遵循 ODR(一次定义法则) 
        
            可视为 更严格的 inline
        
    4 local static 对象

        在 `函数多次调用` 间 `维护1份公共信息`, 

            而无需使用 `global 变量` -> 可能被 其他不相关含访问   

12.2 传参

1 引用传递

    与 引用初始化 规则相同
    
        `字面值 / 常量 / 需类型转换的参数` 可传给 `const 引用` (const t&)
                            |
                            |/
                转换后的值 存入 临时变量 
                            |
                            |   传给
                            |/
                        const 引用
            
        float fsqrt(const float&);                  
        void g(double d)
        {
            float r = fsqrt(d); // 传递 存 static_cast<float>(d) 的 临时变量
        }

2 传 数组的引用: paraType 数组引用 类型

    函数模板 实参推断: size 也是 paraType 的一部分 
    
    template<typename T, int N>
    void f( T(&r)[N] );

12.3 重载函数

1 自动 重载解析

    (1) 概念
        
        调用函数 f 时, 由 compiler 据 `实参类型` 
            与 `scope 中` 名为 f 的 各函数 作 `最佳匹配`
    
    (2) 匹配顺序
         
        1) 精确 匹配
        
            [1] 无须 类型转换
            
            [2] 仅需 简单类型转换
                
                数组名 -> 指针
                函数名 -> 函数指针
                T      -> const T
        
        2) 提升 后匹配
            
            [1] bool/char/short -> int
                         
                ... unsigned 版本 
                            
            [2] float -> double 
        
        3) 标准类型转换 后匹配
        
            [1] 算术类型 间 `强转`
                
                整型间、整型与浮点型间 
            
            [2] OO/GP 中 指针 `隐转`
            
                类层次 中 
                    Derived* -> Base*
            
                模板中
                    T* -> void*
                    
        4) 用户自定义类型转换 后匹配
        
            [1] 单参数 explicit ctor
            
            [2] 类型转换运算符 X::operator T() 
        
        5) 使用 函数声明中 省略号 ...匹配
    
        二义性 => compile error 
                能找到的 最高层级(同一层) 中 不止1个匹配 
                
    (3) 函数模板 的 special
        
        模板实参推断 -> 特例化版本 -> 参与 重载解析规则
        
        23.4.2 节

        ——————————————————————————————————————————————————————————————————————————————————————————————
        模板 函数           |  (先) `引用推断` (模板实参推断) + (再决定) 右值引用传递 / (还是) 左值引用传递 
                            |           |
            右值引用模板实参|           | 区分 左值 和 右值
                            |           |/
                            |   X 类型 的 `左值 / 右值` 被 `推断为 X& / X`
                            |       
        ———————————————————————————————————————————————————————————————————————————————————————————————             
        非模板 函数      |  (右) 值 绑定到 右值引用
                            |               |
            右值引用实参  |             移动 
        ——————|—————————————————————————————————————————————————————————————————————————————————————————    
              |
              |/                      
        void f(vector<int>&& );         
    
    (4) 模板函数 重载: 23.4.3 节
        
        ——————————————————————————————————————————————————————
            5句话
        ——————————————————————————————————————————————————————
        1) 找 `所有 特例化版本`
        ——————————————————————————————————————————————————————
        2) 找 `更特殊的`
        ——————————————————————————————————————————————————————
        3) 与 普通函数 一起, 按 `普通函数 重载解析` 规则
        ——————————————————————————————————————————————————————
        4) 普通函数 与 特例化版本 匹配得一样好, `优选 普通函数`
        ——————————————————————————————————————————————————————
        5) `无法确定 最佳匹配` => 二义性 => 编译报错
        ————————————————————————————|——————————————————————————
                                    |
        二义性消除
            template<typename T>
            T max(T, T);
            
            max('a', 1); // max<char, char> 还是 max<int>(int)
            max(2.7, 4); // max<double, double> 还是 max<int>(int)
                
            1> 显式限定
                
                max<int>('a', 1);    // max<int>(int)
                max<double>(2.7, 4); // max<double>(double)
                
            2> 封装 1层
    
        1) 找参与重~析的 `所有 特例化版本`
            
            调 sqrt(z)
                sqrt<double>(complex<double>) 与
                sqrt<complex<double>>(complex<double>) 都成为 候选
                
        2) 若 2个 模板函数 都可调用, 找`更特殊的`
                
                调 sqrt(z)
                    sqrt<double>(complex<double>) 比
                            |
                            |
                            T
                    sqrt<T>(complex<T>)
                    
                        sqrt< complex<double> >(complex<double>)
                                |
                                |
                                T
                        sqrt<T>(T)
                        
                    更特殊
                
                因为, 任何匹配 sqrt<T>(complex<T>) 的 调用, 也都能匹配 sqrt<T>(T)
                    
        3) 还留在 `候选集` 中的 
                      |
                      |
            `模板函数 和 普通函数`
                      |
                      | 用
                      |/
            `普通函数 重载解析规则`           
                
                sqrt(2)
                    sqrt<int>(int) 优于 sqrt(double)

            note:
                参与了 `模板实参推断` 的 `(模板) 函数实参`,                 
                    不能再进行 提升、标准类型转换、用户自定义类型转换
        
        4) 若 普通函数 与 特例化版本 `匹配的一样好`
            
            优选 `普通函数`
            
                sqrt<2.0>
                    sqrt(double) 优于 sqrt<double>(double)
        
        5) 多个一样好的匹配, 且 
        
            `无法确定 最佳匹配` => 二义性 => 编译报错

2 重载 与 returnType

    重载解析过程 `不考虑 returnType`

3 重载 scope

    —————————————————————————————————————————————————————————————————       
            1) 显而易见 的 scope: context scope / 函数调用前的 global scope
    显式
            2) using 指示 / using 声明
                引入的 跨 类 作用域
    ————————————————————————————————————————————————————————————————        
            3) 重载函数 被 类成员调用
                引入 类 及其 基类 scope
    隐式 
            4) ADL 引入的 scope / 关联 namespace
            
                ——————————————————————————————————————————————————————— 
                                    |   ADL 的 关联名字空间             
                                    |   ( associated namespace ) ADL scope
                ———————————————————————————————————————————————————————
                函数调用 f(arg...)  |
                的参数 args... 是   |
                    ———————————————————————————————————————————————————
                    内置类型        |       无 
                    ———————————————————————————————————————————————————
                    namespace 成员    |   namespace 
                    ———————————————————————————————————————————————————
                    class 成员        |   类 + 其基类 + 类所在的 namespace
    ———————————————————————————————————————————————————————————————————             

4 函数 重载 优先级 (14.2.4 节)

    ———————————————————————————————————————————————————————————————————
                                函数 重载 优先级
    函数调用 f(arg...) 在 
    ———————————————————————————————————————————————————————————————————
    (1) 非类成员调用 
        
                                [1] 函数调用 所在 scope (global scope) 
        
                                [2] ADL scope
    ——————————————————————————————————————————————————————————————————— 
    (2) 类成员调用 
        ———————————————————————————————————————————————————————————————
        1) 命名函数调用 
            
                                [1] 优先 类 scope + 基类 scope 
            
                                [2] 再   ADL scope 
        ———————————————————————————————————————————————————————————————
        2) 匿名函数调用(运算符)
            
                                [1] 类 scope + 基类 scope + ADL scope 
    ———————————————————————————————————————————————————————————————————                                                          
    // === (1)
    1) 命名函数调用 f(x) + 在 类 D 成员 g() 内调用, `实参 x` 类型 N::S 
    
        find scope 
            D+Base 的 scope
            x 的 ADL scope 
            global 
            
        find order 
            [1] 先 D+Base 的 scope 中 找, 找到 Base::f(N::S) => 匹配调用 
    
    2) 实参1
        find order  
            [1] 先 D+Base 的 scope 中 找, 没找到 
            [2] ADL 无法实现 => 查不到 h(int) => 编译报错
    
    namespace N
    {
        struct S {int i};
        
        void f(S);
        void h(int);
    };
    
    struct Base
    {
        void f(N::S); 
    };                                  
                                        
    struct D: Base                      
    {                               
            
        void g(N::S x)  
        {                   
            f(x);    // 1)
            h(1);    // 2) 
        }
    };
    
    // === (2) 匿名函数调用
    匿名函数调用 operator!(X) 在 类 Z 成员 f() 内调用, `实参 x` 类型 X  
    
        find scope: 
            Z 的 scope & global scope 
        
        find order: 2 个 scope 同优先级 
            
            Z::operator!() 不匹配 
            
            ::operator!(X) 匹配 => 调用 
            
    X operator!(X);

    struct Z
    {                                                       
        Z operator!();             
                                     
                                     
        X f(X x)            
        {                               
            // ...                          
            return !x;  //(1)
        }           
                        
        int f(int x) 
        { return !x; } //(2) 对 int 调 内置 !
    };
    
    // === 3 命名函数调用 f(x, 1) + 非类成员调用 
    find scope: 
        global scope 
        参数 x 的 ADL scope: N::X 是 namespace N 的 成员 => ADL scope = N 
    
    find order:
        2 个 scope 同优先级 
        
        N::f(X, int) 比 N2::f(N::X, unsigned) 更匹配
        
        => 调 N::f(X, int) 
        
    namespace N
    {
        template <class T>
            void f(T, int);
        class X {};
    };
    
    namespace N2
    {
        N::X x;
        
        void f(N::X, unsigned);
        
        void g()
        {     
            f(x, 1); // 调 N::f(X, int)
        }
    };

12.4 函数指针

1. C <-> C++ 函数指针 的 赋值/初始化

    type `精确匹配`
            
    (1) 实参 与 形参 `函数指针 类型` 必须 `精确匹配`
                
        C 编译
            using CFT = int(const void*, const void*);
            void ssort(void* base, size_t n, size_t sz, CFT cmp);
        
        C++ 编译
            int cmp(const User* p, const User* q) // User: 用户自定义类型 struct
            {
                return strcmp(p->id, q->id);
            }
            
        cmp 作 实参 调 ssort => 两种 函数指针 类型 非 精确匹配 => 编译报错
        
        解决
            中间层 
                    const void* p 
                        |
                        |/
                    static_cast<const User*>(p)
                    
                int cmp(const void* p, const void* q)
                {
                    return strcmp(static_cast<const User*>(p)->id, static_cast<const User*>(q)->id);
                }
                
                |   C与C++ 链接方式/链接方式 (=>调用机制) 不同 => 
                |/
            仍报错
                
    (2) C & C++ code 链接方式/链接规范 要相同 (=>调用机制 相同)
                            
        // file1.c 
        extern "C"
        {
            using CFT = int(const void*, const void*);
            void ssort(void* base, size_t n, size_t sz, CFT cmp);
        }
        
        // file2.cpp
        extern "C" int cmp(const void* p, const void* q);
                    

2 作 操作参数 调 算法/函数

回调

3 成员 函数指针 与 普通函数指针 本质不同

        |
        |/
     是 vtbl 的 index: 0 / 1 / 2 / ...

12.5 宏

编译器 接触 程序前, 宏 会 `重排程序文本`
    => 调试器、交叉引用、性能评测工具 很难发挥作用

    (1) 宏名 不允许 `重载`
        
        宏 无法处理 `递归`
        
    (2) 尽管尽量用 括号() 将 `宏的参数 括起来`
    
        只能解决 简单语法问题,
            |
            | 但对于
            |/
        `宏产生的 副作用` 仍 `无能为力`
        
        #define MIN(a, b) ( ( (a) < (b) ) ? (a) : (b) )
                          |                            |
                          |/                           |/   
                         最外层 也加 括号           不是语句 => 末尾没 分号
                         
        int x = 1;
        int y = 10;
        int z = MIN(x++, y++); // x 变成 3, y 变成 11
        
                                  ? 会使 x/y 均 递增 => 选中 (x++) => x 再递增
                                    |
                                    |
                ( ( (x++) < (y++) ) ? (x++) : (y++) )
                    |
                    | (x++) < (y++) 是表达式 
                    |/
                  其值为 true

    (3) 宏 的应用
    
        1) `字符串 拼接`
        
            ## 
            
            #define NAME2(a, b) a##b
            int NAME2(hack, cah)();
            
            展开为
            int hackcah();
    
        2) 宏 的 `空 参数列表`
    
            #define EMPTY() std::cout << "empty\n"
            
            EMPTY(); // 输出 "empty\n"
    
        3) 变长参数 宏
        
            #define err_print(...) fprintf(stderr, "error: %s %d\n", __VA_ARGS__)
                                                                        |
                                                                        |/
                                                                     把 实参 当作 字符串
            err_print("The answer", 54);
            
            // 输出: The answer 54
            
1 条件编译

    有一种 宏 无法替代
    
        条件编译
        
            #ifdef 等

2 预定义宏

    __cplusplus 
        C++ 编译器中 定义, C 编译器 没有
            C++11 中其 值为 201103L
            
    __LINE__
        当前源文件 的 代码行数

3 编译指令

相关文章

  • 11-12 逻辑 位 递增 / 自由存储 / lambda /

    第11章 选择适当的 操作 11.1 逻辑 / 位 / 递增减 1 逻辑 2 位 3 递 增/减 11.2 自由存...

  • Lambda,中国版的星际文件FIL

    Lambda,中国版的星际文件FIL Lambda,分布式存储公链。 Lambda是一个高速、安全且可扩展的区块链...

  • C++ lambda和function

    lambda表达式 lambda表达式又称为匿名表达式,是C11提出的新语法。[]存储lambda表达式要捕获的值...

  • 操作系统笔记

    1 计算机体系 存储器的层次结构与高速缓存 当存储器的层次结构满足以下条件时:a 容量递增b 存取时间递增c 访问...

  • 线性表算法设计题(一)

    题目 将两个递增的有序链表合并为一个递增的有序链表。要求结果链表仍使用原来两个链表的存储空间,不另外占用其他的存储...

  • rocket mq 底层存储源码分析(4)-索引构建

    在《rocket mq 底层存储源码分析(1)-存储总概》章节中,我们提到rockmq 有两类索引,一类是【逻辑位...

  • 线性表之单向链表

    基本概念 线性表按照其存储的特点可以分为顺序存储和链式存储,线性表的顺序存储结构的特点是逻辑关系相邻的元素在物理位...

  • Java笔记之Java8新特性

    本笔记来自 计算机程序的思维逻辑 系列文章 Lambda表达式 Lambda表达式 语法 匿名函数,由 -> 分隔...

  • 01. 链表

    01. 链表 1、链表的概述 1.1 链式存储: 用一组任意类型的存储单元存储线性表,在逻辑上面相邻的结点在物理位...

  • 第 14 课 PostgreSQL 数据存储结构

    PostgreSQL 数据存储结构分为:逻辑储存和物理存储 逻辑存储结构是KingbaseES内部的组织和管理数据...

网友评论

      本文标题:11-12 逻辑 位 递增 / 自由存储 / lambda /

      本文链接:https://www.haomeiwen.com/subject/eyyevrtx.html