【问题标题】:Multiline macro function with "return" statement带有“return”语句的多行宏函数
【发布时间】:2014-10-16 17:42:00
【问题描述】:

我目前正在做一个项目,一个特定的部分需要一个多行宏函数(据我所知,常规函数在这里不起作用)。

目标是制作一个堆栈操作宏,将任意类型的数据拉出堆栈(作为函数调用的内部堆栈,不是高级“堆栈”数据类型)。如果它是一个函数,它看起来像这样:

type MY_MACRO_FUNC(void *ptr, type);

其中type 是从堆栈中提取的数据类型。

我目前有一个适用于我的平台 (AVR) 的有效实现:

#define MY_MACRO_FUNC(ptr, type) (*(type*)ptr); \
    (ptr = /* Pointer arithmetic and other stuff here */)

这让我可以写如下内容:

int i = MY_MACRO_FUNC(ptr, int);

正如您在实现中看到的那样,这是有效的,因为分配i 的语句是宏中的第一行:(*(type*)ptr)

然而,我真正想要的是能够有一个声明之前,在任何东西被破坏之前验证ptr 是一个有效的指针。但是,这会导致宏被扩展为 int i = 指向该指针检查。有没有办法在标准 C 中解决这个问题?感谢您的帮助!

【问题讨论】:

  • 如何验证ptr 是否有效?使用表达式(并且可能是逗号的操作数)?什么是“算术和其他东西”? ptr(赋值前)的原值能否恢复?如果可以,那就有可能,否则,您需要一些临时对象,这至少会使它变得困难。
  • 三元运算符可能会做你想做的事,但无论如何我很容易想象出重大问题。不涉及违反严格别名的公然潜力,您所拥有的只是一个类型和一个前景指针。假设它实际上不是有效的,那么确切地说你是初始化i 的替代值源? (或者您只是计划彻底终止流程)?
  • @mafso ptr 将被验证为非NULL 并具有预期的对齐方式(对齐为 AVR 上的有效整数指针)。你能解释一下“逗号操作数”部分吗?在弹出这个值之后,算法只是将ptr 分配给堆栈上的下一个点。我相信可以恢复原来的价值,是的。
  • @WhozCraig 另一种选择是拨打abort,是的。

标签: c macros


【解决方案1】:

正如 John Bollinger points out,扩展到多个语句的宏可能会产生令人惊讶的结果。制作多个语句(和声明!)单个语句的一种方法是将它们包装成一个块(由do ... while(0) 包围,例如参见here)。

然而,在这种情况下,宏的计算结果应该是某种东西,所以它必须是一个表达式(而不是一个语句)。除了声明、迭代和跳转语句(forwhilegoto)之外的所有内容都可以转换为表达式:可以使用逗号运算符对多个表达式进行排序,if-else-子句可以替换由条件运算符 (?:)。

鉴于ptr的原始值可以恢复(为了举例,我假设“算术和其他东西”加4)

#define MY_MACRO_FUNC(ptr, type) \
    ( (ptr) && (uintptr_t)(ptr)%4 == 0 \
        ? (ptr) += 4 , *(type*)((ptr) - 4) \
        : (abort() , (type){ 0 }) )

请注意,我在 ptr 和整个表达式周围加上括号,参见例如here 解释一下。

?: 的第二个和第三个操作数必须是同一类型,所以我在abort 调用之后包含了(type){0}。这个表达式永远不会被计算。你只需要一些有效的虚拟对象;这里,type 不能是函数类型。

如果您使用 C89 并且不能使用复合文字,则可以使用 (type)0,但不允许使用结构或联合类型。


请注意,Gcc 有一个扩展名Statements and Declarations in Expressions

【讨论】:

  • 啊,这应该完全符合我的需要,并且在此过程中我学到了很多关于 ?: 运算符的强大功能。非常感谢您的解释!
【解决方案2】:

这很讨厌:

#define MY_MACRO_FUNC(ptr, type) (*(type*)ptr); \
    (ptr = /* Pointer arithmetic and other stuff here */)

它可能会在某些看似不起眼的情况下产生意想不到的结果,例如

if (foo) bar = MY_MACRO_FUNC(ptr, int);

考虑一下:如果 foo 为 0 会发生什么?

我认为你最好以分配弹出值而不是“返回”它的形式来实现它:

#define MY_POP(stack, type, v) do { \
  if (!stack) abort_abort_abort(); \
  v = *((type *) stack); \
  stack = (... compute new value ...); \
} while (0)

【讨论】:

  • 你的例子说明了为什么我总是在 if 语句后使用大括号,哈哈。但是,你的观点被采纳了。我很想像你展示的那样实现它,但不幸的是我不能改变宏的签名。但是,您给了我一个想法,可能是关闭那个宏来构建它。我试试看,谢谢。
猜你喜欢
  • 1970-01-01
  • 2011-10-29
  • 1970-01-01
  • 2016-06-12
  • 1970-01-01
  • 2018-03-07
  • 2011-08-26
  • 2015-12-04
  • 1970-01-01
相关资源
最近更新 更多