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

68 天前
 liuidetmks

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


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

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

@aprikyblue c
kneep
68 天前
@liuidetmks @ysc3839 可能是在说全局指针地址的读取可能会多产生一条指令,而常量通常不需要。应该不是在说你比较了指针指向的内容。
ysc3839
68 天前
@liuidetmks 地址数值是存在全局变量里的,要先读全局变量,再比较数值
geelaw
67 天前
@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
67 天前
代码静态扫描能解决的问题,为什么要通过编码解决?
chenxytw
67 天前
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
67 天前
楼上已经说了,用魔数 greppability 更好,在超大型项目中更有用

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://ex.noerr.eu.org/t/1164518

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX