要检查的示例代码
#include<stdio.h>
int main(void)
{
const int i = 1;
printf("Variable i is %s\n",
__builtin_constant_p(i) ? "a const variable" : "not a const variable");
return 0;
}
输出:
Variable i is not a const variable
__builtin_constant_p()
不是确定变量是否属于 const
类型的正确 API 吗?
您可以使用通用选择(自 C11 起):
#include <stdio.h>
#define __is_constant_int(X) _Generic((&X), \
const int *: "a const int", \
int *: "a non-const int")
int main(void)
{
const int i = 1;
printf("Variable i is %s\n", __is_constant_int(i));
return 0;
}
您可以使用内置函数 __builtin_constant_p 来确定某个值在编译时是否已知为 常量 ...”
(强调我的)注意这是一个 gcc 编译器扩展,因此不符合标准。
C 除了 enum-constants 之外没有符号常量。
const
限定对象仍然是变量。因此测试失败。请参阅典型应用手册。
备注:
假设显示的代码只是一个示例,您的问题看起来像一个 XY 问题。 C 是静态类型的,因此当您使用这样的构造时,完整类型是众所周知的。拥有类似函数的唯一方法是宏。但是在宏中隐藏不同的行为会导致代码读者感到困惑。她必须记住每次调用时的这种差异。
而是使用两个具有不同行为的函数并相应地命名它们。当调用者知道类型时,他可以使用正确的函数,并且代码的任何读者(包括几周后的您!)都会立即知道其中的差异。
对于
GCC
/LLVM
,您可以使用__builtin_types_compatible_p
,
__builtin_choose_expr
和 __builtin_classify_type
执行此操作
一般不需要为每种指针类型提供案例。唯一的缺点是你需要
显式检查所有嵌套级别(即您需要一个单独的宏
T const * const *
和 T const **
所以写代码会很痛苦
过去的双三重嵌套)。
// GCC / LLVM support __builtin_classify_type.
#define is_p(x) \
(__builtin_classify_type(as_not_void(x)) == 5 /* 5 is ptr type. */)
/* GCC does not const evaluate `__builtin_classify_type` if `x` is void. */
#define as_not_void(x) \
(__builtin_choose_expr(__builtin_types_compatible_p(__typeof__(x), void), \
0, (x)))
// Choose never const pointer if `x` is not a pointer. This allows us to
// compiler `*(x)` when building the constant types.
#define as_ptr(x) (__builtin_choose_expr(is_p(x), (x), ((void **)(NULL))) + 0)
// Rebuild `T *` -> `T const *`
#define make_kp(x) __typeof__(*as_ptr(x)) const *
// Rebuild `T **` -> `T const **`
#define make_kpp(x) __typeof__(*as_ptr(*as_ptr(x))) const **
// Rebuild `T **` -> `T const * const *`
#define make_kpkp(x) __typeof__(*as_ptr(*as_ptr(x))) const * const *
#define build_kp_checker(macro, x) \
__builtin_types_compatible_p(macro(x), __typeof__(as_ptr(x)))
// API
// Is `x` T const *
#define is_kp(x) build_kp_checker(make_kp, x)
// Is `x` T const **
#define is_kpp(x) build_kp_checker(make_kpp, x)
// Is `x` T const * const *
#define is_kpkp(x) build_kp_checker(make_kpkp, x)
总体思路是,如果
x
是指针类型,我们将其重建为
我们想要检查和使用的 const
指针的级别
__builtin_types_compatible
看看我们是否重建了相同类型或
新的。
有关的解决方案将检查所有单/双嵌套的情况 指针。不幸的是,需要 4 个额外的宏才能完成三重操作 然后8次做四次。如果你向它传递一个三重嵌套指针,它仍然会编译,但它无法识别像
T const * const * const *
这样的模式,并且与 T const * const *
不同。
#define _stringify(X) #X
#define stringify(X) _stringify(X)
#define is_const(x) __builtin_constant_p(x)
#define const_ptr_info(T) \
{ \
T _tmp; \
printf("\n%-24s: is_kp=%d(%d), is_kpp=%d(%d), is_kpkp=%d(%d)\n", \
stringify(T), is_kp(_tmp), is_const(is_kp(_tmp)), is_kpp(_tmp), \
is_const(is_kpp(_tmp)), is_kpkp(_tmp), \
is_const(is_kpkp(_tmp))); \
}
#define const_ptr_info_arr(T) \
{ \
T _tmp[4]; \
printf("\n%-24s: is_kp=%d(%d), is_kpp=%d(%d), is_kpkp=%d(%d)\n", \
stringify(T), is_kp(_tmp), is_const(is_kp(_tmp)), is_kpp(_tmp), \
is_const(is_kpp(_tmp)), is_kpkp(_tmp), \
is_const(is_kpkp(_tmp))); \
}
typedef struct do_structs_work {
char a;
int b;
} do_structs_work_t;
typedef struct do_unions_work {
char a;
int b;
} do_unions_work_t;
int
main(int argc, char ** argv) {
printf("\n----Testing Non-ptrs----\n");
const_ptr_info(int);
const_ptr_info(const int);
printf("\n----Testing Structs----\n");
const_ptr_info(do_structs_work_t);
const_ptr_info(do_unions_work_t);
printf("\n----Testing double points----\n");
const_ptr_info(void **);
const_ptr_info(const void **);
const_ptr_info(void const **);
const_ptr_info(void const * const *);
printf("\n----Testing single pointers----\n");
const_ptr_info(void * const *);
const_ptr_info(void *);
const_ptr_info(void const *);
const_ptr_info(void * const);
const_ptr_info(const void *);
const_ptr_info(int *);
const_ptr_info(int const *);
const_ptr_info(int * const);
printf("\n----Testing Arrays----\n");
const_ptr_info_arr(int const);
const_ptr_info_arr(int);
const_ptr_info_arr(int const *);
const_ptr_info_arr(int * const);
}
产量:
----Testing Non-ptrs----
int : is_kp=0(1), is_kpp=0(1), is_kpkp=0(1)
const int : is_kp=0(1), is_kpp=0(1), is_kpkp=0(1)
----Testing Structs----
do_structs_work_t : is_kp=0(1), is_kpp=0(1), is_kpkp=0(1)
do_unions_work_t : is_kp=0(1), is_kpp=0(1), is_kpkp=0(1)
----Testing double points----
void ** : is_kp=0(1), is_kpp=0(1), is_kpkp=0(1)
const void ** : is_kp=0(1), is_kpp=1(1), is_kpkp=0(1)
void const ** : is_kp=0(1), is_kpp=1(1), is_kpkp=0(1)
void const * const * : is_kp=1(1), is_kpp=0(1), is_kpkp=1(1)
----Testing single pointers----
void * const * : is_kp=1(1), is_kpp=0(1), is_kpkp=0(1)
void * : is_kp=0(1), is_kpp=0(1), is_kpkp=0(1)
void const * : is_kp=1(1), is_kpp=0(1), is_kpkp=0(1)
void * const : is_kp=0(1), is_kpp=0(1), is_kpkp=0(1)
const void * : is_kp=1(1), is_kpp=0(1), is_kpkp=0(1)
int * : is_kp=0(1), is_kpp=0(1), is_kpkp=0(1)
int const * : is_kp=1(1), is_kpp=0(1), is_kpkp=0(1)
int * const : is_kp=0(1), is_kpp=0(1), is_kpkp=0(1)
----Testing Arrays----
int const : is_kp=1(1), is_kpp=0(1), is_kpkp=0(1)
int : is_kp=0(1), is_kpp=0(1), is_kpkp=0(1)
int const * : is_kp=0(1), is_kpp=1(1), is_kpkp=0(1)
int * const : is_kp=1(1), is_kpp=0(1), is_kpkp=0(1)
这就是我们想要的。
C 中如何检查变量是否为“const”限定符类型?
使用 GCC,您可以执行以下操作:
#define IS_CONST(x) \
__builtin_types_compatible_p( \
__typeof__(x)*, \
const __typeof__(x)* )
__builtin_types_compatible_p
忽略顶级限定符,但这些是指针。
__builtin_constant_p() 不是确定变量是否为 const 类型的正确 API 吗?
否,
__builtin_constant_p
检查表达式的 value 在编译时是否已知,而不是类型(如果 const
合格)。
与新标准化的
typeof
完全通用(长期以来一直受到许多(gcc、clang、tinycc)编译器的支持):
#define ISCONST(Lval) _Generic(&(Lval), typeof(Lval) const*: 1, default: 0)
//the ptr-indirection gets around _Generic's
//rvalue (top-level-qualifier dropping) conversion
//TEST:
int const x = 42; int y = 43;
_Static_assert(ISCONST(x) && !ISCONST(y),"");