C11中_Generic的语法和示例用法

96

我听说C11添加了泛型。我查了一下谷歌,看了一些文章,了解到有一个新的关键字(_Generic)等等。但我好像还是无法完全理解。

它是否类似于C#中的泛型或C ++中的模板?能否给我简要解释一下C11泛型的定义、语法和一个简单的使用示例?


3
你可以阅读或下载C11标准的草案(PDF版本)。其中在第6.5.1.1节中有一个例子。 - pmg
4
在C++中,没有类似于“泛型”的东西。 - Griwes
15
@Griwes 给你。还有更多挑剔的地方吗? - ApprenticeHacker
3个回答

131

我看过的最好的例子启发了下面(可运行)的示例,它为疯狂的内省解锁了各种奇怪的可能性...

#include <stdio.h>
#include <stddef.h>
#include <stdint.h>

#define typename(x) _Generic((x),        /* Get the name of a type */             \
                                                                                  \
        _Bool: "_Bool",                  unsigned char: "unsigned char",          \
         char: "char",                     signed char: "signed char",            \
    short int: "short int",         unsigned short int: "unsigned short int",     \
          int: "int",                     unsigned int: "unsigned int",           \
     long int: "long int",           unsigned long int: "unsigned long int",      \
long long int: "long long int", unsigned long long int: "unsigned long long int", \
        float: "float",                         double: "double",                 \
  long double: "long double",                   char *: "pointer to char",        \
       void *: "pointer to void",                int *: "pointer to int",         \
      default: "other")

#define fmt "%20s is '%s'\n"
int main() {

  size_t s; ptrdiff_t p; intmax_t i; int ai[3] = {0}; return printf( fmt fmt fmt fmt fmt fmt fmt fmt,

     "size_t", typename(s),               "ptrdiff_t", typename(p),     
   "intmax_t", typename(i),      "character constant", typename('0'),
 "0x7FFFFFFF", typename(0x7FFFFFFF),     "0xFFFFFFFF", typename(0xFFFFFFFF),
"0x7FFFFFFFU", typename(0x7FFFFFFFU),  "array of int", typename(ai));
}
                 ╔═══════════════╗ 
═════════════════╣ Amazeballs... ╠═════════════════════════════════════
                 ╚═══════════════╝ 
            size_t is 'unsigned long int'
         ptrdiff_t is 'long int'
          intmax_t is 'long int'
character constant is 'int'
        0x7FFFFFFF is 'int'
        0xFFFFFFFF is 'unsigned int'
       0x7FFFFFFFU is 'unsigned int'
      array of int is 'other'

1
哇,你使用什么编译器?在我的机器上(Linux,GCC 4.9+),最新类型显示为“指向整数的指针”:整数数组是“指向整数的指针”。 - user1284631
17
这很不错,但你永远无法以这种方式涵盖所有类型... 无论如何,“Amazeballs”加一。 - einpoklum
3
实际上,@einpoklum,你可以通过这种方式涵盖所有非复合类型......共有五种整数类型及其对应的无符号类型,三种浮点类型及其对应的复数类型,总共16种基本类型。当你包括存储类(普通、constvolatilevolatile const)时,就会得到64个不同的变体。虽然繁琐,但并非不可能。每种类型的指针会使这个数字翻倍,而指向指针的指针会进一步增加它......但这样总共只有192个变体......唉!但仍然不是不可能的。 - SGeorgiades
1
@SGeorgiades:但是您也希望typename()也适用于复合类型。 - einpoklum
@jdk1.0:你不能使用这些定义来获取任意结构体的类型。同样,也不适用于数组(忽略衰减),联合或枚举。我想这就涵盖了所有情况。 - einpoklum
显示剩余3条评论

65

这篇文章是一个非常好的介绍。以下是概述:

通用选择使用新关键字实现:_Generic。语法类似于类型的简单switch语句:_Generic('a',char:1,int:2,long:3,default:0)评估为2(C中的字符常量是int)。

基本上,它像一种switch,其中标签是类型名称,其针对第一个表达式(上面的'a')进行测试。结果成为评估_Generic()的结果。


1
刚刚意识到我链接的是你已经链接的同一个页面...不过也难怪...这真的是关于这个东西唯一好的信息来源,很奇怪... - Alex Gray

5

我使用的是CLion 1.2.4版本,但是它目前不支持C11语法,因此我使用GNU C99中的以下代码替代了_Generic

#include <stdio.h>

int main(int argc, char **argv) {
    char *s;
    if (__builtin_types_compatible_p(__typeof__(s), long)) {
        puts("long");
    } else if (__builtin_types_compatible_p(__typeof__(s), char*)) {
        puts("str");
    }
    return (0);
};

36
“这篇文章讲述了如何使用代替_Generic的方法,并在一个询问如何使用_Generic的问题中获得了8个赞。” - Michael Mrozek
18
这个答案提供了一种替代方法,即使没有其他作用,也说明了所问的概念。任何替代方法的一个特征是它总会提供对所问对象的新和独特视角。老师们经常使用这种技术。如果你站在一个地方盯着某物但仍然不理解它,可以走动一下,从另一个角度看它。这总是会提供新信息的。无论是否有意,这个答案提供了这样的东西。(+1 是为此) - ryyker
13
这是GNU C,不是C99。 - osvein

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接