V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
liuidetmks
V2EX  ›  C++

使用匿名结构体指针作为常量来杜绝魔数,是否合理/值得?

  •  
  •   liuidetmks · 8 天前 · 1894 次点击

    在项目中,用匿名结构体的地址作为常量来标识不同业务类型(例如 A 、B 、C 、D……),

    这样做的目的是彻底避免魔术数字( magic number ),并且希望在逻辑判断中直接通过指针比较。

    代码 h 文件

    typedef struct __BizType *BizTypeRef;
    
    // 对外暴露的常量声明
    extern const BizTypeRef kBizTypeA;
    extern const BizTypeRef kBizTypeB;
    extern const BizTypeRef kBizTypeC;
    extern const BizTypeRef kBizTypeD;
    
    

    实现文件

    
    // 定义结构体
    struct __BizType {
        int value;
    };
    
    // 定义常量指针
    const BizTypeRef kBizTypeA = &(const struct __BizType){ .value = 1 };
    const BizTypeRef kBizTypeB = &(const struct __BizType){ .value = 2 };
    const BizTypeRef kBizTypeC = &(const struct __BizType){ .value = 3 };
    const BizTypeRef kBizTypeD = &(const struct __BizType){ .value = 4 };
    

    使用

    // 使用示例
    void handleBiz(BizTypeRef type) {
        if (type == kBizTypeA) {
            // 处理业务 A
        } else if (type == kBizTypeB) {
            // 处理业务 B
        }
    }
    
    16 条回复    2025-10-12 09:19:48 +08:00
    r6cb
        1
    r6cb  
       8 天前
    为什么不用枚举?
    liuidetmks
        2
    liuidetmks  
    OP
       8 天前
    @r6cb 枚举还是数字啊,还是能使用 1 当做参数传入。 我想完全只使用定义的这几个变量,政出一门
    aprikyblue
        3
    aprikyblue  
       8 天前   ❤️ 2
    你看看 enum class
    cwxiaos
        4
    cwxiaos  
       8 天前 via iPhone
    或许可以叫做更安全的枚举,限制下游代码乱搞
    minamo
        5
    minamo  
       8 天前 via Android
    如果你真这么讨厌魔数,倒也不是不行,但我觉得不值得
    iOCZS
        6
    iOCZS  
       8 天前
    本该在语法层解决的问题,在用户侧试图解决,会引入复杂性
    ysc3839
        7
    ysc3839  
       7 天前 via Android   ❤️ 1
    这么写的话进行比较时可能并不是直接比较常量,而是要从全局变量里读取值,降低性能。
    而且实现文件里这么取临时对象的地址,不怕编译器把这四个对象都优化成使用同一空间?不怕去到悬垂指针出现什么 UB ?
    需要枚举类型的地方,能直接传递整数,是 C 允许隐式转换的问题,建议想办法调整编译参数,禁止这种行为,或者迁移到更严格的 C++。
    xuanbg
        8
    xuanbg  
       7 天前
    魔数有什么问题吗?其实魔数一点问题都没有,你先定义一个常量,无非就是脱裤子放屁——多此一举
    metalvest
        9
    metalvest  
       7 天前
    为什么不直接用 std::type_index
    liuidetmks
        10
    liuidetmks  
    OP
       7 天前 via iPhone
    @metalvest c


    @ysc3839 全局作用域不会 ub 吧
    比较的话是比较两个指针地址,性能不存在问题的

    @xuanbg 有时候业务变化了,魔数可能哪里弄漏了,这里把结构体匿名,提供一个统一构造方法,方便处理一点。

    @aprikyblue c
    kneep
        11
    kneep  
       7 天前   ❤️ 1
    @liuidetmks @ysc3839 可能是在说全局指针地址的读取可能会多产生一条指令,而常量通常不需要。应该不是在说你比较了指针指向的内容。
    ysc3839
        12
    ysc3839  
       7 天前 via Android
    @liuidetmks 地址数值是存在全局变量里的,要先读全局变量,再比较数值
    geelaw
        13
    geelaw  
       7 天前   ❤️ 1
    @aprikyblue #3 楼主写的是 C 不是 C++,没有 enum class 。(当然,换成 C++ 似乎是比较好的选择。)这一点可以从这段代码可以编译知道(见下面第二点)。

    ————

    几个可以挑剔的点:

    一双下划线是保留标识符。

    二是初始化的时候 constness 不合适,注意 const BizTypeRef 是 struct __BizType * const 而不是 struct __BizType const *,于是这里会丢失 const ,如果实现方(很容易无意间)尝试修改只读复合字面量的 .value 的话会有 UB 。

    三是,如果实现方不需要数据,那么实际上没有必要使用 value ,用 non-const 复合字面量本身就可以确保几个表达式的对象不占据相同的位置(但是 const 复合字面量可能会是同一个对象)。

    ————

    @ysc3839 #7 楼主的版本有不同的值,所以无法是同一个位置。在文件作用域的复合字面量是静态存储期。
    cybort
        14
    cybort  
       7 天前 via Android
    代码静态扫描能解决的问题,为什么要通过编码解决?
    chenxytw
        15
    chenxytw  
       7 天前
    Your solution is not better than use `0xdeadbeef01`, `0xdeadbeef02` or other long and unique value to define the enum. The long and unique value is friendly to search and replace.
    coyove
        16
    coyove  
       7 天前
    楼上已经说了,用魔数 greppability 更好,在超大型项目中更有用
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   Solana   ·   2479 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 11:09 · PVG 19:09 · LAX 04:09 · JFK 07:09
    ♥ Do have faith in what you're doing.