- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
(注意这是一个语言律师问题。)
更新: 我搞砸了问题 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
在第一种情况下第二种)。
_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-expressiondefault
:
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/
给定以下源代码: #include #include #include static inline void closedirp(DIR **p) { if (*p) c
在 C11 中,我可以创建一个原型(prototype)如下所示的函数: void myVaFunc(const char* const conv, ...); 我可以这样运行它: myVaFunc(
我正在阅读这个 C11 的 _Generic维基百科上的例子: #define cbrt(X) _Generic((X), long double: cbrtl, \
我需要在 C 中使用 _Generic,以便能够根据变量类型返回专门的值。 const char *encode_char_ptr(char *x) { return (x ? x : "NU
所以我正在尝试实现“通用打印宏”: #include #include #define CHECK(x) printf(#x " =" \ _Generic((x), float:
我试图学习如何使用“新的”C11 泛型表达式,但我遇到了困难。 考虑以下代码: #include #include #define test(X, Y, c) \ _Generic((X)
如何向 c11 _Generic Functions 添加额外类型? 您是否必须#undef/re-#define 它?(如果是这样,下面的工作是否可行)还是有更好的方法? #define to_st
我对 C11 的 _Generic 机制感到高兴 - 类型切换是我在 C++ 中怀念的东西。然而,事实证明它很难编写。 例如,给定函数: bool write_int(int); bool write
有没有办法在同一表达式中多次使用 _Generic 关键字来创建单个字符串文字? 我正在寻找的是一种生成单一格式字符串以传递给 printf 的方法,所有转换说明符都适合适当的类型。 写this时答案
我有一个这样的模板: template.h ---------- // Declare a function "func_type()" void JOIN(func_, T)(T t) { retu
我正在使用 C 的在线编译器和调试器。此网页使用 GCC 5.4.1 C99。当我测试这段代码时, https://www.onlinegdb.com/online_c_compiler #inclu
在下面的代码中: struct Person { char* name; int age; }; struct Book { char* title; char* au
我正在设置一些大宽度整数类型,因此我大量使用宏来使这些类型尽可能像基本整数类型一样可用。我一直遇到的一个问题是,如果我在宏扩展中大量使用 _Generic 表达式,而不是最小化 _Generic 使用
我想在C中组合两种类型来调用函数(例如具有不同列和行的乘法 vector 和矩阵): #define CC_FIRST(a) _Generic((a), int: int8d) #def
我有兴趣编写一个类型验证宏,它只在类型不是 int/short/long 或指针时发出警告。 我遇到的问题是指针可以是任何类型的指针。 #define INT_OR_POINTER_AS_UINTPT
我似乎无法将参数传递给需要不同参数的函数(或传递给实现第一个类型的子集的其他 _Generic 宏)。 #define DEBUG_PRINT(x,...) _Generic((x),
可能我在这里做错了一些非常基本的事情,但我无法弄清楚那可能是什么。我可以解释我的代码片段,但我认为我在这里尝试做的事情非常明显:为我的 DynamicArray 的 dynarr_printf 创建一
我正在尝试在 C11 中使用 _Generic 宏生成重载函数,并且我已经停止了对零参数函数的支持,例如: #define msg(_1) _Generic((_1), char*: msg_stri
使用 gcc.exe(Rev3,由 MSYS2 项目构建)8.2.0。 我试图构建一个宏来自动在两种类型之间进行类型转换,其中两个参数永远不应该是同一类型。我的问题是如果我不包含相同类型的情况,编译器
我想使用 C11 _Generic 关键字根据静态类型填充 union ,如: typedef union { double d; long l; const char*s;
我是一名优秀的程序员,十分优秀!