如何在C中解析链式宏?

Aar*_*son 5 c macros const c-preprocessor

如果我想使用预处理程序#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)
Run Code Online (Sandbox Code Playgroud)

如果我用sec(t)宏,它使用TICKS_PER_DAY它由3个以前的宏定义TICKS_PER_SEC * SEC_PER_MIN * MIN_PER_HR * HRS_PER_DAY,随处可见确实是发生在我的代码调用sec(t):

(t / 5184000L) % 7L)
Run Code Online (Sandbox Code Playgroud)

或者它每次扩展到:

(t / (60L * 60L * 60L * 24L)) % 7L)
Run Code Online (Sandbox Code Playgroud)

以便在每一步执行额外的乘法指令?这是宏和const变量之间的权衡,还是我误解了预处理器的工作原理?

更新:

根据许多有用的答案,链接宏的最佳设计扩展为常量表达式是将括号括括号

1.正确的运作顺序:

(t / 60 * 60 * 60 * 24) != (t / (60 * 60 * 60 * 24))
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

tni*_*les 1

请参阅gcc 预处理器宏文档,特别是类对象宏

\n\n

我认为编译器在这里也发挥了作用。例如,如果我们只考虑预处理器,那么它应该扩展到

\n\n

(t / (60L * 60L * 60L * 24L)) % 7L)

\n\n

但是,编译器(无论优化如何?)可能会将其解析为

\n\n

(t / 5184000L) % 7L)

\n\n

因为这些是独立的常量,因此代码执行会更快/更简单。

\n\n

注意1:您应该在定义中使用“(t)”来防止意外的扩展/解释。\n注意2:另一个最佳实践是避免使用 ,undef因为这会降低代码的可读性。请参阅有关宏扩展如何受此影响的注释(类对象宏部分)。

\n\n

更新:来自“类对象宏”部分:

\n\n
\n

当预处理器扩展宏名称时,宏的扩展将替换宏调用,然后检查扩展以查找更多要扩展的宏。例如,

\n\n

#define TABLESIZE BUFSIZE\n #define BUFSIZE 1024\n TABLESIZE\n ==> BUFSIZE\n ==> 1024\n \n 首先扩展 TABLESIZE 以生成 BUFSIZE,然后扩展该宏以生成最终结果 1024。

\n\n

请注意,定义 TABLESIZE 时未定义 BUFSIZE。TABLESIZE 的 \xe2\x80\x98#define\xe2\x80\x99 完全使用您指定的扩展\xe2\x80\x94(在本例中为 BUFSIZE\xe2\x80\x94),并且不会检查它是否也包含宏名称。仅当您使用 TABLESIZE 时,才会对其扩展扫描更多宏名称的结果。

\n
\n\n

(强调我的)

\n