我正在读“No Bugs!”作者David Thielen和第3章讨论了一种确定给定代码路径是否被命中的方法。它建议使用一个检查参数的宏,如果是,则执行一个汇编指令,生成一个特定的中断(0x3)来创建一个调试器断点。如果宏的参数为false,则它什么都不做。
然后,宏看起来像这样:
#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语法和围绕“”)的组合
答案 0 :(得分:3)
您从本书中获得的建议主要是创建自己的assert()
功能。
在大多数系统上,您应该只使用assert()
中提供的assert.h
宏。如果断言被触发并且您在调试器下运行,则断言将触发断点(再次 - 在大多数系统上)。
如果由于某种原因你不能使用标准的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
的内联汇编。
答案 1 :(得分:1)
由于您无论如何都在表达式中使用指令,因此请使用带有指令语法的条件构造而不是带有表达式语法的条件构造。
#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)