在我正在开发的一个应用程序中,我们有一个宏将时钟时间转换为一个短整数的内部表示:
#define CLOCK_TIME(hr, min) (s32)(((hr) * 60 + (min)) * 0x10000 / (24 * 60))
自然地,我们会按照时钟上显示的时间和分钟来写。所以,例如,6:30将是CLOCK_TIME(6, 30)
。这一切都很好,直到有人试图输入CLOCK_TIME(6, 08)
,编译器吐出一个错误,说08
不是一个有效的八进制常数。我们尝试了一些变通方法,但它们导致了自己的问题。例如,用1##min - 100
替换min
解决了这个特定的问题,但这样宏将无法使用任何不是2位十进制文字的输入。有没有一种方法可以允许08
和09
在宏中工作,同时允许参数可以是个位数、表达式或变量?
是的,我意识到不使用前导零是一种解决方案,但是让时间以时钟上的方式显示可以提高可读性和可用性。
编辑:一个额外的约束是,如果时间是一个常数,它 * 必须 * 在编译时计算,而不是运行时。
6条答案
按热度按时间ego6inou1#
任意位数的解(在执行限度内)(但不是表达式或变量)是
1##min*2 - 2##min
。这可以被认为是(10p+x)·2 −(2·10p+x),其中 p 是 x 中的未知位数,它等于(2·10p+2x)−(2·10p+x)= 2x−x = x。这避免了浮点问题。例如,
23
将被123*2 - 223
替换,其等于23。08
将是108*2 - 208
= 8,而8
将是18*2 - 28
= 8。处理数字、表达式和变量的一个解决方案是在C之外编写自己的脚本。人类不需要将他们的程序限制在C预处理器中。您可以编写自己的脚本来删除此宏调用中数字的前导零,然后在编译源代码之前使用此脚本处理源代码。
rwqw0loc2#
正如@dimich建议的那样,您可以添加科学计数法
e0
。这将首先强制数字为双精度型,然后您可以将其类型转换回
int
。这是因为
08e0
作为(double)8.000
是有效的,即使08
是无效的八进制。IDEOne Link
k5ifujac3#
有没有一种方法可以让08和09在宏中工作,同时也允许参数可以是个位数,* 表达式或变量 *?
(强调我的)
不,绝对没有办法使用C预处理器来实现这一点。
e1xvtsh34#
但如果输入不是2位十进制文字,则宏将失败
下面的代码:
输出123456。
有没有一种方法可以让08和09在宏中工作,同时也允许参数可以是个位数,表达式或变量?
不知道
如果你愿意使用运行时,你可以做任何事情,只要解析表达式的整个字符串表示,检查它是否是一个八进制数,然后采取不同的分支。
我的看法:我觉得你想改变C语言的一个内部部分是令人困惑的。八进制数字可能会让语言的新用户感到困惑,但我发现这样的宏用法会让世界其他地方感到困惑。我宁愿在宏旁边留下评论来关注和学习八进制数字。
yhqotfr85#
考虑到传递给宏的值是数字,使用
gcc
可以从字符串转换为整数,由于statement expression,可以消除前导0:这解决了前导0的问题,但计算是在运行时完成的,并且这仅适用于传递给宏的数字(没有变量或表达式)。
3gtaxfhh6#
将其定义为宏而不是函数,只有当你打算在编译时计算这个数字时才有意义。因此,假设宏在编译时 * 不 * 获得传递的运行时变量,而只是数字...
...那么就有可能变出一些邪恶的魔法宏语言(不推荐),以
hh:mm
作为输入,但也允许h:mm
(但不允许hh:m
)。调用方看起来像这样:邪恶的宏看起来像这样:
说明:
?:
操作符,其优先级非常低,以至于您可以将几乎任何内容作为操作数写入。它的工作方式类似于一堆静态Assert(如果您喜欢,可以将宏的结果传递给_Static_assert
)。STR
辅助宏将输入转换为字符串文字,如"hh:mm"
,整个宏都在字符串上工作。STRLEN
检查是否有足够的数字,然后检查:
是否在允许的位置之一。CALCULATE_STUFF
中执行。CALCULATE_STUFF
有两个参数,根据使用的输入格式,它会进行字符到整数的转换,将第一个数字乘以10。这个宏的开销为零,
CLOCK_TIME(6:30)
等调用在编译时被整数替换。