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

yr9zkbsy  于 2023-10-16  发布在  其他
关注(0)|答案(5)|浏览(150)

要检查的示例代码

#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()是否不是正确的API来确定变量是否为const类型?

zaqlnxep

zaqlnxep1#

您可以使用常规选择(自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;
}
3wabscal

3wabscal2#

RTFineManual:
你可以使用内置函数__builtin_constant_p来确定一个值在编译时是否已知是**常量 *.
(强调我的)注意这是一个gcc编译器扩展,因此不符合标准。
C语言中没有符号常量,只有 * n-constants *。const限定对象仍然是变量。因此,测试失败。典型应用请参见手册。

备注:

假设显示的代码只是一个例子,您的问题看起来像一个XY问题。C是静态类型的,因此在你使用这样一个构造时,完整的类型是众所周知的。要想有类似功能的东西,唯一的方法就是宏。但是在宏中隐藏不同的行为会导致代码的读者混淆。她必须记住每一次调用的区别。
相反,使用两个具有不同行为的函数,并相应地命名它们。由于调用者知道类型,他可以使用正确的函数和代码的任何读者(包括几周后的你!)会立即知道差异。

zpjtge22

zpjtge223#

对于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来查看我们是否重建了相同的类型或新类型。
解决方案about将检查单/双嵌套指针的所有情况。不幸的是,这将需要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)

这正是我们想要的

cigdeys3

cigdeys34#

如何在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检查表达式的 * 值 * 在编译时是否已知,而不是const限定的类型。

bvjxkvbb

bvjxkvbb5#

完全通用于新标准化的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),"");

相关问题