【问题标题】:typeof uses in C, besides macrostypeof 在 C 中使用,除了宏
【发布时间】:2014-05-16 02:21:19
【问题描述】:

众所周知,在宏中使用typeof 使它们与类型无关,例如container_of() 和Linux 内核中的许多其他宏。毫无疑问,typeof 关键字在这些宏中使用时会释放出很大的力量。

这个问题是关于typeof 关键字的进一步使用。除了宏之外,关键字还能在哪些其他上下文中为 C 代码带来很多好处?

【问题讨论】:

  • typeof 是非标准的;它似乎是一个 gcc 特定的扩展。
  • 老实说想不出。除了宏,一切都已经是强类型的,所以你已经知道你正在处理的任何项目的类型。我认为您已经发现了它们的独特用处。
  • C 中的类型不能做太多事情——毕竟这种语言相当简单。然而,C++ 是完全不同的事情,很多花哨的代码都是依赖 typeof 和更新的 decltype 语句编写的。

标签: c linux-kernel


【解决方案1】:

有些人(包括我自己)不喜欢 C++ const_cast<> 运算符的语法,因为;

  • 它似乎命名错误,因为它删除 const
  • 它似乎违反了 DRY,因为它需要一个冗余类型 arg。

但我错了:它并没有错名,因为它也可以添加 const 和/或volatile "cv" 限定符,而且它只是部分违反 DRY,因为编译器会捕捉任何错误。所以我不太喜欢它并使用它:它比 C 风格的演员更安全。

使用 gcc 的 typeof,您可以在 C 中拥有几乎相同的类型安全性。

以下 C 代码示例给出了一个 CONST_CAST(T, x) 宏,并说明了它的用法:

#define REMOVE_QUALIFIER(cv, T, x) /* this macro evaluates its args only once */   \
    __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), cv T), ((T)(x)), \
    (void)0)
#define ADD_QUALIFIER(cv, T, x) /* this macro evaluates its args only once */      \
    __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), T), ((cv T)(x)), \
    (void)0)
#ifdef __GNUC__
#define CONST_CAST(T, x)  REMOVE_QUALIFIER(const, T, x) // "misnamed"
#else
#define CONST_CAST(T, x)  ((T)(x)) // fallback to standard C cast
#endif

void foo(void);
void foo(void) {
    const int *a   = 0;
    const float *x = 0;
    int *b        = a;                          // warning
    int *c        = (int *)a;                   // no warning, unsafe standard cast
    int *d        = (int *)x;                   // no warning, and likely wrong
    int *e        = CONST_CAST(int *, a);       // ok
    int *f        = CONST_CAST(int *, x);       // error
    unsigned *g   = CONST_CAST(unsigned *, a);  // error
    const int **h = &b;                         // warning
    const int **i = ADD_QUALIFIER(const, int **, &b); // ok
    const int **j = ADD_QUALIFIER(const, int **, &x); // error
}

此技术还可用于更改类型的符号,让人想起 C++ 的 std::make_signedstd::make_unsigned,或 Boost 特征。例如:

#define MAKE_UNSIGNED(T, x)  ADD_QUALIFIER(unsigned, T, x) // T usually char*

【讨论】:

    【解决方案2】:

    gcc 的typeof 的使用是另一个重新解释演员表,使用联合双关语。

    它可以应用于标量和结构,也可以应用于指针。它只给出一个 R 值。

    #ifdef __GNUC__
    #define PUN_CAST(T, x) (((union {typeof(x) src; T dst;})(x)).dst)
    #else
    #define PUN_CAST(T, x) (*(T*)&(x)) //<-- classic pun: breaks strict aliasing rules
    #endif
    

    警告:您可以使用它来将指针转换为 4 或 8 字节的数组,反之亦然。但是你不能使用它将一个指针转换为另一个指针,以避免严格的别名规则。

    【讨论】:

      【解决方案3】:

      typeof 的第二种用法是生成指向常量的指针,或指向函数返回值的指针,如下例所示:

      #include <stdio.h>
      #include <time.h>
      #include <sys/socket.h>
      
      #define AMPERSAND(x)  (&(typeof(x)){x})
      
      int main(void) {
          printf("%s\n", ctime(AMPERSAND(time(0)))); // pointer to time_t
          setsockopt(0, SOL_SOCKET, SO_REUSEADDR, AMPERSAND(1), sizeof 1);
          return 0;
      }
      

      这允许直接的函数组合,而不必将临时变量保存在命名变量中。 (不幸的是,这并没有扩展到 g++。)

      【讨论】:

        【解决方案4】:

        typeof 的一个用途是 const-cast 一个二维数组。在 gcc 中,构造:

          extern void foo(const int a[2][2]); // or equivalently a[][2]
          int a[2][2];
          foo(a);
        

        将生成:

        “警告:从不兼容的指针类型传递 'foo' 的参数 1”。

        (请参阅http://c-faq.com/ansi/constmismatch.html 了解原因。)解决此问题的一种方法是使用类似大锤的演员阵容,例如:

          foo((void *)a);
        

        这样的演员会很乐意接受你可能错误地给予的任何东西。

        但我们可以更加精致。通过使用以下代码示例中给出的 cast-macro CONST_CAST_2D,可以消除警告。更重要的是,如果您尝试将其应用于二维数组以外的任何内容,您将收到编译器错误/警告。 CONST_CAST_PP 的工作原理类似,用于指向指针的指针。

        #define CONST_CAST_2D(x)  ((const typeof((x)[0][0])(*)[countof((x)[0])])(x))
        #define CONST_CAST_PP(x)  ((const typeof(**(x))**)(x))
        #define countof(x)        (sizeof(x) / sizeof 0[x]) // semi-standard define
        
        static void foo(const int a[][2]) {} // takes const
        static void bar(const int **b)    {} // takes const
        
        int main(void) {
            int a[2][2];           // non-const
            int **b;               // non-const
            foo(CONST_CAST_2D(a)); // ok
            bar(CONST_CAST_PP(b)); // ok
            return 0;
        }
        

        CONST_CAST_PP 为常见问题提供了一个干净而可靠的解决方案,例如:

        CONST_CAST_2D 解析:

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-08-27
          • 2021-12-23
          • 2015-08-10
          相关资源
          最近更新 更多