gpt4 book ai didi

c - 将 C 要求应用于未选择的 _Generic 案例

转载 作者:行者123 更新时间:2023-12-05 08:49:44 24 4
gpt4 key购买 nike

(注意这是一个语言律师问题。)

已弃用的问题

更新: 我搞砸了问题 0。当我写这篇文章时,我正在查看 C 2018 6.5.2.2 6 中的参数-参数类型规则,它们不在约束部分中,因此可能被编译器忽略。我忽略了 Constraints 部分中的 6.5.2.2 2,因此需要编译器来诊断不匹配的类型。如果我注意到这一点,我就不会问问题 0。

this question ,我们需要这样的代码:

int AddVersion0(int a, int b       ) { return a+b;   }
int AddVersion1(int a, int b, int c) { return a+b+c; }

typedef int (*TypeVersion0)(int, int);
typedef int (*TypeVersion1)(int, int, int);

#define Foo(f, a, b) _Generic((f), \
TypeVersion0: (f)((a), (b)), \
TypeVersion1: (f)((a), (b), 0) \
)

#include <stdio.h>

int main(void)
{
printf("%d\n", Foo(AddVersion0, 3, 4));
printf("%d\n", Foo(AddVersion1, 3, 4));
}

(Foo 已经用函数f 参数化,方便演示和分析。在原来的上下文中,这是不需要的。)

使用默认开关,可选择添加 -std=c18,GCC 10.2 和苹果 Clang 11.0 reject this code提示是一个错误,而不是警告,一个函数调用的参数太多(AddVersion0 在第一次使用 Foo 的第二种情况下),而另一个函数调用的参数太少(AddVersion1在第一种情况下第二种)。

问题0:这段代码是否严格符合C标准,所以GCC和Clang拒绝它是错误的?不匹配的情况不仅不会在执行程序中评估,而且在 _Generic 处理后它们实际上也不存在,因为 _Generic 被定义为产生“结果表达式”,而不是C 2018 6.5.1.1 中的“结果值” 3. GCC 和 Clang 正在对未成为程序一部分的函数调用的函数调用应用运行时约束。 6.5.1.1 3 包括:

If a generic selection has a generic association with a type name that is compatible with the type of the controlling expression, then the result expression of the generic selection is the expression in that generic association.

问题一

接下来,考虑这个解决方法:

int AddVersion0(int a, int b       ) { return a+b;   }
int AddVersion1(int a, int b, int c) { return a+b+c; }

typedef int (*TypeVersion0)(int, int);
typedef int (*TypeVersion1)(int, int, int);

int NeverCalled();
#define Sanitize(Type, f) _Generic((f), Type: (f), default: NeverCalled)
#define Foo(f, a, b) _Generic((f), \
TypeVersion0: Sanitize(TypeVersion0, (f))((a), (b)), \
TypeVersion1: Sanitize(TypeVersion1, (f))((a), (b), 0) \
)

#include <stdio.h>

int main(void)
{
printf("%d\n", Foo(AddVersion0, 3, 4));
printf("%d\n", Foo(AddVersion1, 3, 4));
}

GCC 10.2 和 Apple Clang 11.0 都没有提示这一点。

问题 1:编译器有理由提示这个吗?由于 NeverCalled 未使用原型(prototype)声明,因此 C 2018 6.5.2.2 6 并未说明任何调用具有未定义的行为,除非函数是定义且类型不包含原型(prototype)和参数类型与参数类型不匹配。但是函数根本没有定义,所以条件是未触发。

(我问编译器是否有投诉的理由,因为当然允许编译器投诉任何事情,作为不会阻止编译程序的警告,但问题是编译器是否可以推断出这个的某些方面代码违反了 C 标准的某些方面。)

最佳答案

您的第一个代码片段没有形成有效的符合标准的翻译单元。

Foo(AddVersion0, 3, 4)展开后,基本变成:

_Generic((AddVersion0),
TypeVersion0: (AddVersion0)((3), (4)),
TypeVersion1: (AddVersion0)((3), (4), 0)
)

就此问题而言,它等同于:

_Generic(1,
int: AddVersion0(3, 4),
void*: AddVersion0(3, 4, 0)
)

通用选择的语法定义(在第 6.5.1.1 节中)为:

Syntax
generic-selection:

_Generic ( assignment-expression, generic-assoc-list )

通用关联列表:

generic-association
generic-assoc-list , generic-association

通用关联:

type-name : assignment-expression
default : assignment-expression

现在第二个无效案例的赋值表达式被解析为函数调用后缀表达式(§6.5.2)(其中后缀表达式也是赋值表达式):

postfix-expression:

[...]
postfix-expression ( argument-expression-listopt )

后面关于函数调用的部分(§6.5.2.2p2)在约束段落中说:

If the expression that denotes the called function has a type that includes a prototype, the number of arguments shall agree with the number of parameters.

(其中“被调用函数”是AddVersion0隐式转换为函数指针,原型(prototype)为2个参数,参数个数为3)

因此,第二个分支中的表达式违反了“应”要求,因为提供了不同数量的参数。

该标准仅对其他通用关联有此说明(摘自 §6.5.1.1p3):

None of the expressions from any other generic association of the generic selection is evaluated.

并没有说允许它们是无效的表达式,所以也不异常(exception)。


至于解决方法,您可以转换为正确的函数类型,这不会是 UB,因为永远不会评估错误类型的函数调用:

#define Foo(f, a, b)    _Generic((f),  \
TypeVersion0: ((TypeVersion0)(f))((a), (b)), \
TypeVersion1: ((TypeVersion1)(f))((a), (b), 0) \
)

但这仍然会在 gcc(但不是 clang)中针对“通过不兼容类型调用的函数”发出警告。更改为 ((int(*)())(f) 似乎是一种悲观情绪,如果函数在不同的翻译单元中,则更改调用约定。

您还可以将 Sanitize 与空函数指针一起使用:

#define Sanitize(Type, f)   _Generic((f), Type: (f), default: (Type) 0)

您的解决方法与此方法的工作原理相同(即正确链接和执行):

int NeverCalled();
int main() {
if (0) NeverCalled();
}

它是 UB,因为通用选择仍然“使用”NeverCalled。在附件 J,未定义的行为中,这是这样写的:

The behavior is undefined in the following circumstances:

  • [...]
  • An identifier with external linkage is used, but in the program there does not exist exactly one external definition of the identifier

关于c - 将 C 要求应用于未选择的 _Generic 案例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63419320/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com