【问题标题】:How do I create #define macro with a ternary operator with one operand that does nothing?如何使用一个三元运算符和一个什么都不做的操作数创建#define 宏?
【发布时间】:2014-01-01 20:17:19
【问题描述】:

我正在阅读“没有错误!” David Thielen 和第 3 章讨论了一种确定给定代码路径是否已被命中的方法。它建议使用宏来检查参数,如果为真,则执行生成特定中断 (0x3) 的汇编指令以创建调试器断点。如果宏的参数为假,则它什么也不做。

然后,宏看起来像这样:

#ifdef DEBUG
#define Trap(t)     ( (t) ? __asm__("int $0x3") : )
#endif

然而,这段代码会导致 gcc 的编译错误:

int_test.c:16:35: error: expected expression before ‘__asm__’

我从here 了解到,因为gcc 的asm 是块语句而不是表达式,所以我必须使用statement expressions 才能以这种方式使用asm。所以,现在变成了:

#define Trap(t)     ( (t) ? ({ __asm__("int $0x3"); }) : )

编译器仍然报错:

int_test.c:16:64: error: expected expression before ‘)’ token

好的,所以现在我必须这样做?

#define Trap(t)     ( (t) ? ({ __asm__("int $0x3"); }) : ({ ; }) )

这看起来真的很愚蠢。如果t 为假而不使用这种烦人的语法,我不能让预处理器不插入任何内容吗?

注意:为了简单起见,我省略了一些其他类似的宏,并且我已经调整了书中的语法以适用于 gcc(例如将书中的 _asm 替换为 asm 以及使用 AT&T语法和在 "") 中包围程序集

【问题讨论】:

  • 如果只需要副作用而不需要值,为什么要使用三元运算符而不是 if 语句?
  • +1 以使用 if 而不是三元运算符。 但是我不得不问,你到底为什么要使用汇编指令来触发断点,而不是仅仅使用真正的调试器并在感兴趣的地方设置断点?这似乎是一种非常、非常原始、不必要、低级且彻头彻尾的古老调试方法......
  • 传统上这是通过逗号运算符完成的。例如。 cond && (function_with_side_effects(), 1) 虽然我不确定这是否适用于 GCC 内联汇编语句,因此您可能需要将实际断点放在单独的函数中。有什么特别的原因你不想把整个东西变成一个(内联)函数?
  • @David Norris:它不是普通交互式断点的替代品,而是一个有用的补充。它们通常或多或少地用作断言,除了不一定是错误的条件(例如,突出很少使用的代码路径)
  • @DavidNorris 如果我只使用调试器,每次运行 gdb 时都必须手动设置断点,对吗?除非有办法保存 gdb 配置文件或其他东西。无论哪种情况,我都只是想从这本书中学习,但它是 92 年出版的,所以我猜它已经过时了。

标签: c macros


【解决方案1】:

您从书中得到的建议基本上是创建您自己的assert() 功能。

在大多数系统上,您应该只使用assert.h 中提供的assert() 宏。如果触发了断言并且您在调试器下运行,则断言将触发断点(同样 - 在大多数系统上)。

如果由于某种原因您不能使用标准的assert.h 功能(例如,可能某种不提供该功能的准系统嵌入式系统工具链),您可以执行以下操作:

#ifdef DEBUG_MODE
    void DoAssert(void);
    #define ASSERT(expr) ((expr) ? (void) 0 : DoAssert())
#else
    #define ASSERT(expr) ((void) 0)
#endif

并将您需要捕获的任何内容放入调试器中的 DoAssert() 函数中 - 在您的情况下,这将是 int 3 的内联程序集。

【讨论】:

  • 啊,我明白了。这本书是在 92 年出版的,所以也许 assert() 库是在它出版之后才引入的。
  • assert() 在当时确实存在。真正的区别在于这种类型的 trap() 是非致命的。这样,您可以在您的程序知道如何处理的故障(例如丢失的文件)或您想了解的其他一些边缘情况中放置一个永久断点
【解决方案2】:

由于您无论如何都在表达式中使用指令,因此请使用带有指令语法的条件构造,而不是带有表达式语法的条件构造。

#define Trap(t)     ({ if (t) __asm__("int $0x3"); })

或者,让它成为一个函数。

static inline void Trap(long t) {
    if (t) __asm__("int $0x3");
}

虽然由于您不会将Trap 用作表达式,因为它返回void(因此您只能在, 的左侧或作为顶级表达式使用它),你不妨把整个事情变成一个指令。

#define Trap(t)     do {if (t) __asm__("int $0x3");} while (0)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-08-31
    • 1970-01-01
    • 2021-02-26
    • 2017-04-30
    • 2012-04-02
    • 1970-01-01
    • 2020-11-05
    • 2018-03-17
    相关资源
    最近更新 更多