使用匿名结构体指针作为常量来杜绝魔数,是否合理/值得? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
liuidetmks
V2EX    C++

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

  •  
  •   liuidetmks 64 天前 2675 次点击
    这是一个创建于 64 天前的主题,其中的信息可能已经有所发展或是发生改变。

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


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

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

    @aprikyblue c
    kneep
        11
    kneep  
       63 天前   1
    @liuidetmks @ysc3839 可能是在说全局指针地址的读取可能会多产生一条指令,而常量通常不需要。应该不是在说你比较了指针指向的内容。
    ysc3839
        12
    ysc3839  
       63 天前 via Android
    @liuidetmks 地址数值是存在全局变量里的,要先读全局变量,再比较数值
    geelaw
        13
    geelaw  
       63 天前   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  
       63 天前 via Android
    代码静态扫描能解决的问题,为什么要通过编码解决?
    chenxytw
        15
    chenxytw  
       63 天前
    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  
       63 天前
    楼上已经说了,用魔数 greppability 更好,在超大型项目中更有用
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2740 人在线   最高记录 6679       Slect Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 12:45 PVG 20:45 LAX 04:45 JFK 07:45
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86