【发布时间】:2016-03-21 17:41:06
【问题描述】:
如果我想使用预处理器 #define 语句来轻松定义和计算常量和常用函数,并利用更少的 RAM 开销(而不是使用 const 值)。但是,如果一起使用许多宏,我不确定它们是如何解决的。
我正在设计我自己的 DateTime 代码处理,类似于 linux 时间戳,但用于具有代表 1/60 秒的刻度更新的游戏。我更愿意将值声明为链式,但想知道硬编码值是否会执行得更快。
#include <stdint.h>
// my time type, measured in 1/60 of a second.
typedef int64_t DateTime;
// radix for pulling out display values
#define TICKS_PER_SEC 60L
#define SEC_PER_MIN 60L
#define MIN_PER_HR 60L
#define HRS_PER_DAY 24L
#define DAYS_PER_WEEK 7L
#define WEEKS_PER_YEAR 52L
// defined using previous definitions (I like his style, write once!)
#define TICKS_PER_MIN TICKS_PER_SEC * SEC_PER_MIN
#define TICKS_PER_HR TICKS_PER_SEC * SEC_PER_MIN * MIN_PER_HR
#define TICKS_PER_DAY TICKS_PER_SEC * SEC_PER_MIN * MIN_PER_HR * HRS_PER_DAY
// ... so on, up to years
//hard coded conversion factors.
#define TICKS_PER_MIN_H 3600L // 60 seconds = 60^2 ticks
#define TICKS_PER_HR_H 216000L // 60 minutes = 60^3 ticks
#define TICKS_PER_DAY_H 5184000L // 24 hours = 60^3 * 24 ticks
// an example macro to get the number of the day of the week
#define sec(t)((t / TICKS_PER_DAY) % DAYS_PER_WEEK)
如果我使用 sec(t) 宏,它使用由 3 个之前的宏 TICKS_PER_SEC * SEC_PER_MIN * MIN_PER_HR * HRS_PER_DAY 定义的 TICKS_PER_DAY,那么在我调用 sec(t) 的代码中的任何地方都会出现这种情况:
(t / 5184000L) % 7L)
还是每次都扩展为:
(t / (60L * 60L * 60L * 24L)) % 7L)
以便在每一步都执行额外的乘法指令?这是宏和 const 变量之间的权衡,还是我误解了预处理器的工作原理?
更新:
根据许多有用的答案,将扩展为常量表达式的宏链接的最佳设计是将定义包装在 括号 中以表示
1.正确的操作顺序:
(t / 60 * 60 * 60 * 24) != (t / (60 * 60 * 60 * 24))
2.通过将常量值组合在一起来鼓励编译器进行常量折叠:
// note parentheses to prevent out-of-order operations
#define TICKS_PER_MIN (TICKS_PER_SEC * SEC_PER_MIN)
#define TICKS_PER_HR (TICKS_PER_SEC * SEC_PER_MIN * MIN_PER_HR)
#define TICKS_PER_DAY (TICKS_PER_SEC * SEC_PER_MIN * MIN_PER_HR * HRS_PER_DAY)
【问题讨论】:
-
预处理器不会优化它,但是任何值得它的盐的编译器都会把它折叠成一个单一的常量值。
-
所以如果我使用诸如 gcc 之类的,我应该期望它被视为单个值?有没有办法检查它的作用?
-
这些定义不是嵌套的;它们是链式扩展:使用宏的扩展,其扩展使用更多宏。如果存在嵌套,则意味着宏的主体可以定义宏。
-
您应该从
sec的定义中删除分号,以便它扩展为一个表达式。否则sec(x) + 10之类的东西将不起作用。
标签: c macros constants c-preprocessor