我不禁要弄明白,为什么三元条件的第三个操作数(错误条件表达式)即使在控制条件的计算结果为真时也要进行隐式类型转换,反之亦然?
我可能只能用C语言标准规范要求操作数是“兼容的”(我到处都听到这个术语,但我不确定它有多准确),就像在任何表达式中一样,编译器确保它们是“兼容的”。但为什么首先要有这样的限制呢?三元运算符不像其他运算符,因为它可以自由地忽略不被计算的表达式。对于其他操作,很明显,操作数应该是相同类型的,以使特定操作按预期工作。
这也只有在隐式类型转换发生在编译时才有意义,老实说,我甚至不知道它是什么时候发生的。由于某种原因,我找不到这个问题的合理答案,但我确实找到了this文章。这篇文章对我来说没有任何意义,尤其是这一行:
C语言中的隐式类型转换是在程序执行过程中由编译器将一种数据类型转换为另一种数据类型。
它是由编译器执行的,还是在程序执行过程中执行的?我可能是一个容易混淆的人,但据我所知,编译器在程序执行期间没有任何事情要做。
所以,有人能解释一下为什么ITC转换是必要的吗?因为对我来说,这会产生不必要的开销。以下是一个概念验证:
#include <stdio.h>
int main (void)
{
printf("%zu\n", sizeof( 0 ? 2.0 : 3 ));
return 0;
}
结果是8
。
- 我还问了ChatGPT ITC何时发生,它在运行时写道。我问它是否确定,并验证这些信息,毫不奇怪,它改变了它的叙述。
2条答案
按热度按时间7xllpg7q1#
理解C评估模型很重要:该语言是在“抽象机器”上定义的,其中“编译时”和“运行时”之间没有区别。在可观察到的副作用上存在部分排序。
当条件运算符的第三个操作数被求值时,在抽象机中将
3
转换为3.0
。编译器负责生成与抽象机相同的输出。因为这种转换没有副作用,所以编译器可以提前准备它。例如,编译器可能在编译期间准备转换后的结果,以避免需要任何运行时汇编指令。
您的
sizeof
示例与此问题无关,因为sizeof
的结果仅取决于两个参数的类型;它不依赖于参数的值,也不依赖于如果条件被求值,哪个参数可能被选择。nnsrf1az2#
C是一种静态类型语言,除了可变修改的类型。(可变修改类型是涉及可变长度数组的类型。)因此,每个表达式都必须有一个类型。因此,在出现条件运算符的地方,它形成的表达式必须有一个类型。
在大多数情况下,条件运算符的类型由C 2018 6.5.15 5和6中的规则确定,包括对算术操作数使用 * 通常的算术转换 *。这些规则导致在编译类型时确定的静态类型,但涉及可变长度数组的指针除外。(可变长度数组不能是条件操作数的操作数,因此可变修改的类型仅作为指针存在。
因此,当计算条件运算符表达式时,无论计算第二个和第三个操作数中的哪一个,都必须转换为结果类型。这就是操作中存在隐式转换的原因。
除非C实现能够在转换期间确定条件并优化表达式,否则在程序执行期间执行此求值。在任何情况下,都不会转换未求值的操作数。
(Note在C标准中有一个defect。如果第二个或第三个操作数是可变修改的类型,并且条件选择另一个操作数进行求值,则C实现在不对未选择的操作数的大小方面求值的情况下无法知道结果应该是什么类型。
我可能只能从C语言标准规范要求操作数“兼容”这一事实来推理
C标准不要求操作数兼容。“兼容”是C标准中的一个特定术语,表示两个类型可以完成为同一类型。例如,
int []
和int [3]
是兼容的,因为未知大小的数组可以被完成为3个元素的数组。条件表达式可以是
x ? 3. : -1
,即使double
和int
是不兼容的类型。条件运算符的第二个和第三个操作数的约束在C 2018 6.5.15 3中。如果操作数是算术的,则这些约束不要求它们是兼容的。
这也只有在隐式类型转换发生在编译时才有意义…
我看不出有什么理由得出这个结论。在
double foo(int x) { return x; }
中,int
x
被隐式转换为double
,这种转换发生在运行时(除非优化允许在编译时完成)。为什么条件运算符中的隐式转换会有所不同?C语言中的隐式类型转换是在程序执行过程中由编译器将一种数据类型转换为另一种数据类型。
它是由编译器执行的,还是在程序执行过程中执行的?
你引用的那篇文章写得很差。这意味着转换发生在程序执行期间,但由编译器编写的指令执行。