sizeof(""+0) != sizeof(char *)是一个Bug还是未定义行为?

15

以下是一个C程序:

#include <stdio.h>

int main(void)
{
    printf("%u %u %u\n",sizeof "",sizeof(""+0),sizeof(char *));
    return 0;
}
在Linux上使用GCC编译时,输出为1 4 4,但在Windows上使用Microsoft Visual C++编译时,输出为1 1 4。我预期的结果是GCC的结果。它们的差异是因为MSVC有一个错误还是因为sizeof(""+0)是未定义的?对于两个编译器,无论使用哪个字符串文字或整数常量,行为(即打印的中间值是否等于第一个值或最后一个值)都是相同的。ANSI C标准中6.2.2.1 - Lvalues and function designators中相关的参考文献如下:

除非它是 sizeof 操作符的操作数...否则,具有'type'数组类型的lvalue将转换为具有指向数组对象的初始元素的'type'指针类型的表达式,并且不是lvalue。

然而,在sizeof(""+0)中,“Except”不应该适用,因为数组/字符串文字是+的操作数,而不是sizeof的操作数。

也许微软正在进行某种优化,用 '\0' 替换 ""?如果你取 sizeof(""+(long long)0) 会发生什么? - Lundin
'\0'替换""将违反类型系统。更有趣的是,sizeof(""+1)是多少? - Fred Foo
你在技术上得到了UB,因为sizeofsize_t类型,必须使用%zu进行打印。 - phuclv
3个回答

7
因为"fooabc"char[7]类型,sizeof("fooabc")的结果与sizeof(char[7])相同。然而,数组可以被隐式转换为指针(一些人错误地称之为“衰减”)。由于这对算术运算符(+)是必要的,所以""+0将具有char*类型。而char指针的大小可能与数组不同。在这方面,MSVC的行为似乎存在问题。

1
我不会说使用“decay”这个术语是错误的。它已经被广泛使用,并且如果我没记错的话,可能也可以在标准文本中找到(我现在无法检查)。 - eq-
@eq- 它不会。 §6.3.2.1 “3 除非它是 sizeof 运算符或一元 & 运算符的操作数,或者是用于初始化数组的字符串字面值,否则具有类型“type 的数组”的表达式将被转换为具有类型“指向数组对象的初始元素的 type 指针”的表达式,并且不是左值。” - Josh Lee
@eq 不是标准文本,但是是常用术语。 - M.M
@JoshLee:标准中有许多情况使用一个术语来描述几个相关的结构,但没有为每个结构指定单独的术语。最过载的术语可能是“对象”,但“转换”也包括几种不相关的操作,其中一些缺乏单独的术语。使用诸如“衰减”和“强制转换”等术语似乎比引用“将数组对象转换为指向第一个元素的指针”的转换和“在使用值编写或初始化lvalue时执行的无需转换”的转换更有用。 - supercat

1

我猜这是MSVC的错误。

""+0获取""(已转换为char*类型)的地址并将0加到它上面。该表达式的类型为char*

试着做一下: ""+0的值是多少? ""+1的值是多少?以及sizeof(""+ 1)的值是多少?


1

看起来是 MSVC 中的一个 bug。 显然,它的优化器在进行适当的类型分析之前会先删除 +0


在这种情况下,尝试使用sizeof("" +(volatile const char *)0)并查看是否有任何区别。(虽然我猜测微软也可能会误编译volatile...) - Lundin
3
@Lundin:指针不能相加,因此建议的sizeof(“” +(volatile const char *)0)是编译器错误--也就是说,该表达式无法解析。如果您想进行类似的测试,请使用:extern int x; sizeof("" + x);无需为x提供定义,因为它实际上没有被使用,编译器将无法对其进行优化。 - David Rodríguez - dribeas

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