C 中如何检查变量是否为“const”限定符类型?

问题描述 投票:0回答:5

要检查的示例代码

#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 吗?

c gcc constants
5个回答
5
投票

您可以使用通用选择(自 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;
}

3
投票

RTFine手册

您可以使用内置函数 __builtin_constant_p 来确定某个值在编译时是否已知为 常量 ...”

(强调我的)注意这是一个 gcc 编译器扩展,因此不符合标准。

C 除了 enum-constants 之外没有符号常量。

const
限定对象仍然是变量。因此测试失败。请参阅典型应用手册。


备注:

假设显示的代码只是一个示例,您的问题看起来像一个 XY 问题。 C 是静态类型的,因此当您使用这样的构造时,完整类型是众所周知的。拥有类似函数的唯一方法是宏。但是在宏中隐藏不同的行为会导致代码读者感到困惑。她必须记住每次调用时的这种差异。

而是使用两个具有不同行为的函数并相应地命名它们。当调用者知道类型时,他可以使用正确的函数,并且代码的任何读者(包括几周后的您!)都会立即知道其中的差异。


0
投票

对于

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)

这就是我们想要的。


0
投票

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
合格)。


0
投票

与新标准化的

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),"");
© www.soinside.com 2019 - 2024. All rights reserved.