[英]Check if a macro argument is a pointer or not
是否有一些“好”的方法来检查传递给宏的变量是否是指针? 例如
#define IS_PTR(x) something
int a;
#if IS_PTR(a)
printf("a pointer we have\n");
#else
printf("not a pointer we have\n");
#endif
这个想法是,这不是在运行时而是在编译时完成的,例如:根据变量是否为指针,我们得到不同的代码。 所以我希望 IS_PTR() 以某种方式评估为某种常量表达式。 我是否以错误的方式提出这个想法?
正如您在问题中暗示的那样,通过#if
中的预处理器肯定无法观察到。 预处理器对类型一无所知,只知道由它们构造的标记和表达式。
C11 有一个新功能,可以让您观察特定的指针类型,但不是一般的“指针”。 例如,你可以做某事
#define IS_TOTOP(X) _Generic((X), default: 0, struct toto*: 1)
或者如果您希望宏也适用于数组
#define IS_TOTOPA(X) _Generic((X)+0, default: 0, struct toto*: 1)
周围已经有一些编译器实现了这一点,即 clang,对于 gcc 和其他编译器,您已经可以使用一些内置函数来模拟该功能,请参阅P99 。
NULL 几乎是您唯一可以寻找的东西。 没有办法确定某物是否是指针。
我找到了一个或多或少的_Generic
解决这个问题的方法。
警告:可能会触发误报(参见下面的示例)。
#define __INTERNAL_CHECK_POINTER(x) _Generic((x),\
int: 0, unsigned int: 0,\
long: 0, unsigned long: 0,\
long long: 0, unsigned long long: 0,\
float: 0, double: 0,\
long double: 0, \
default: 1)
/**
* Determines whether the variable has likely a pointer type (but may be triggered false-positive)
*/
#define IS_LIKELY_A_POINTER(x) ((sizeof(x) == sizeof(void*)) && __INTERNAL_CHECK_POINTER(x) ? 1 : 0)
演示:
char c = 0;
printf("c is a pointer: %s\n", IS_LIKELY_A_POINTER(c) ? "Yes" : "No");
unsigned long long l = 0;
printf("l is a pointer: %s\n", IS_LIKELY_A_POINTER(l) ? "Yes" : "No");
double d = 0.0;
printf("d is a pointer: %s\n", IS_LIKELY_A_POINTER(d) ? "Yes" : "No");
unsigned char* cp = 0;
printf("cp is a pointer: %s\n", IS_LIKELY_A_POINTER(cp) ? "Yes" : "No");
struct tm* tp = 0;
printf("tp is a pointer: %s\n", IS_LIKELY_A_POINTER(tp) ? "Yes" : "No");
char ia[] = {0, 1, 2, 3, 4, 5, 6, 7};
printf("ia is a pointer: %s\n", IS_LIKELY_A_POINTER(ia) ? "Yes" : "No");
这将输出:
c is a pointer: No
l is a pointer: No
d is a pointer: No
cp is a pointer: Yes
tp is a pointer: Yes
ia is a pointer: Yes // false-positive!
如果您(像我一样)正在寻找一些日志记录(为特定变量绘制或不绘制*
)并且您不是在寻找防故障结果,试试这个,它可能会有所帮助。 干杯!
注意它不会在 MSVC 下编译; 使用 gcc/clang/等。 代替或使用此条件进行自己的后备实现:
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
// use _Generic code
#else
// ¯\_(ツ)_/¯
#endif
在 Clang 和 GCC 上,如果P
是具有指针类型的对象,则__builtin_classify_type(P)
的计算结果为 5。
有一种方法既没有误报也没有误报,但是如果参数既不是数组也不是指针,编译就会中断。
#define is_same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
/* Is a an array? */
#define is_array(a) (!is_same_type((a), &(a)[0]))
/* Is p a pointer? */
#define is_pointer(p) (!is_array(p))
它需要 GCC 内置__builtin_types_compatible_p()
。
行为:
如果 p 是一个数组,它将评估为 0;
否则,如果 p 是指针,它将评估为 1;
否则,编译将中断。
您可以通过结合 @alx 和 @zneak 拥有的内容并输入 __builtin_choose_expr 来获得完整的答案:
#define is_same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_pointer_or_array(p) __builtin_classify_type(p) == 5
#define is_pointer(p) __builtin_choose_expr(is_pointer_or_array(p), \
is_same_type(p, &(*__builtin_choose_expr(is_pointer_or_array(p), p, &p))), 0)
因此,__builtin_choose_expr(expr, a, b) 将返回 a 如果 expr 非零,否则返回 b。 该手册暗示编译器不应看到未选择的任何参数(a 或 b),这意味着我们可以在其中包含任何无效语法的内容。 不幸的是,它实际上并没有那样工作,并且仍然会引发编译器错误,因此我们需要在内部使用 __builtin_choose_expr 的第二次调用来“隐藏”这个语法错误。 你不能使用像 '1' 这样的常量作为表达式,即使该语句在技术上总是正确的。 我认为这是由于 gcc 和编译器如何分解这一切。 第二个参数 b 永远不会被返回,但它仍然需要是有效的语法。 所以 &p 适用于这种情况。
如果我们逐字使用@Netherwire 提供的演示,我们会看到上面无法处理不完整的结构。 尽管如此,我们没有得到误报,同时也没有在非指针/数组上失败。 我认为这对于大多数用例来说应该足够好了。
这是@Netherwire 的演示,使用完整的结构进行了修改:
char c = 0;
printf("c is a pointer: %s\n", is_pointer(c) ? "Yes" : "No");
unsigned long long l = 0;
printf("l is a pointer: %s\n", is_pointer(l) ? "Yes" : "No");
double d = 0.0;
printf("d is a pointer: %s\n", is_pointer(d) ? "Yes" : "No");
unsigned char* cp = 0;
printf("cp is a pointer: %s\n", is_pointer(cp) ? "Yes" : "No");
struct TM {} tm;
struct TM *tp;
tp = &tm;
printf("tp is a pointer: %s\n", is_pointer(tp) ? "Yes" : "No");
char ia[] = {0, 1, 2, 3, 4, 5, 6, 7};
printf("ia is a pointer: %s\n", is_pointer(ia) ? "Yes" : "No");
这输出:
c is a pointer: No
l is a pointer: No
d is a pointer: No
cp is a pointer: Yes
tp is a pointer: Yes
ia is a pointer: No
嘘! 我们没有误报,而且定义能够处理的不仅仅是数组。
编辑:如果你给它一个文字,这会抛出一个错误,如果你想在定义中使用它可能是一个问题。 我们需要做的就是添加一个 used 变量来替换 &p:
char UNREACHABLE_VAR;
#define is_pointer(p) __builtin_choose_expr(is_pointer_or_array(p), \
is_same_type(p, &(*__builtin_choose_expr(is_pointer_or_array(p), p, &UNREACHABLE_VAR))), 0)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.