首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C11 _Generic使用

C11 _Generic使用
EN

Stack Overflow用户
提问于 2016-10-17 22:20:31
回答 4查看 4.7K关注 0票数 9

我试图学习如何使用“新”C11通用表达式,但我遇到了麻烦。

考虑以下代码:

代码语言:javascript
复制
#include <stdlib.h>
#include <stdio.h>

#define test(X, Y, c) \
    _Generic((X), \
        double:     _Generic((Y), \
                        double * :  test_double, \
                        default: test_double \
                    ), \
        int:        _Generic((Y), \
                        int * : test_int, \
                        default: test_int \
                    ) \
    ) (X, Y, c)


int test_double(double a, double *b, int c);
int test_int(int a, int *b, int c);

int test_double(double a, double *b, int c) { return 1; }
int test_int(int a, int *b, int c) { return 2; }

int main()
{
    double *t = malloc(sizeof(double));
    int *s = malloc(sizeof(int));
    int a1 = test(3.4, t, 1);
    int i = 3;
    int a2 = test(i, s, 1);
    printf("%d\t", a1);
    printf("%d\n", a2);
    return 0;
 }

这一切都很好,但我还是不明白为什么"_Generic((Y),.“中的默认情况)在"_Generic((X),.“结尾省略它时)是必要的。没有任何后果。

实际上,如果删除这两个默认值,就会得到一个错误(gcc 5.4.0),它说“类型为‘double*’的选择器不兼容任何关联”,即“宏展开时"int a1 = test(3.4,t,1);在宏展开测试时使用”int *“(i,s,1)。

“默认”真的有必要吗?还是我遗漏了什么?在第一种情况下,为什么要这样呢?如果我只有可以调用的test_double和test_int,为什么我应该为一些根本不应该编译的东西设置默认的大小写呢?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2016-10-17 22:47:50

TL;DR

选择发生在编译时,但是意味着其他(未选中)代码被丢弃。它仍然是有效的,这意味着.

如果未使用默认值,且没有任何类型名称与控制表达式的类型兼容,则程序将不会编译。

(来源)

这是一个令人惊讶的问题:

没有默认情况下的“第一个Y":

代码语言:javascript
复制
#define test(X, Y, c) \
    _Generic((X), \
        double:     _Generic((Y), \
                        double * :  test_double \
                    ), \
        int:        _Generic((Y), \
                        int * : test_int, \
                        default: test_default \
                    ) \
    ) (X, Y, c)

我明白这个错误:

prog.c:6:30:错误:'_Generic‘类型的选择器'int *’与任何关联都不兼容

注意,它抱怨一个不兼容的int *!为什么?好吧,让我们来看看报道的台词:

代码语言:javascript
复制
    int a2 = test(i, s, 1);

Xint型,Yint *型。

现在来了一个重要的部分:扩展发生unconditionally.因此,即使Xint类型的,X的第一个关联(当它是double类型时)必须是一个格式良好的程序。因此,如果Y是一个int *,那么必须有良好的格式:

代码语言:javascript
复制
_Generic((Y), \
             double * :  test_double \
              ), \

既然int *不是double *,事情就会在这里破裂。

不过,我只是浏览了一下标准(实际上是N1570),却找不到任何具体指定这种行为的东西。我想在这种情况下,我们可以报告一个缺陷,这个标准太模糊了。我现在正试着这么做。

票数 6
EN

Stack Overflow用户

发布于 2016-10-18 00:32:06

不幸的是,_Generic在标准中没有得到充分的说明。通常的解释似乎是,在非选定的情况下,表达方式不得包含任何违反约束的行为。

一个更简单的例子:

代码语言:javascript
复制
int main(void)
{
    int x;

    _Generic(0, int: x = 5, float: x = (void)0);
}

这段代码在gcc中给出了一个约束冲突,因为它对所有相关表达式(不仅仅是选定的表达式)执行约束检查,而x = (void)0包含一个约束冲突。

将此原则应用于没有默认情况的代码中,我们看到的问题是,当使用Y作为声明为int *s的变量实例化宏时,关联表达式之一是_Generic(s, double * : test_double),这是一个约束冲突,因为没有匹配的大小写。

票数 8
EN

Stack Overflow用户

发布于 2016-10-17 22:44:38

显然,这是因为嵌套的_Generic选择。

问题是,对于_Generic,必须在编译时满足所有可能的泛型关联的类型兼容性。即使只选择一个关联,但未选中的关联仍必须具有一些兼容的关联。

关联默认处理与该_Generic选择的其他关联不兼容的每个关联。

假设您删除了int: _Generic((Y),上的默认关联。如果这样做,则选择双重关联,但int关联仍然必须处理double*类型,这通常是默认情况下完成的。在这种情况下,只有int*关联,它输出一个错误。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40096584

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档