括号在确定数组大小时是否有影响?

26

以下程序在gcc 4.8.2上会打印相同的数字两次:

#include <stdio.h>

int main()
{
    char a[13];
    printf("sizeof a  is %zu\n", sizeof a );
    printf("sizeof(a) is %zu\n", sizeof(a));
}
根据这篇Reddit帖子,gcc在这方面不符合标准,因为带括号的表达式不在例外列表中,不会发生从数组到指针的衰减。
这个人说得对吗?以下是相关标准引用:
“除非它是sizeof运算符或一元&运算符的操作数,或者是用于初始化字符类型数组的字符字符串字面值,或者是用于初始化元素类型与wchar_t兼容的数组的宽字符串字面值,否则具有'type'数组类型的lvalue被转换为具有类型'pointer to type'的表达式,该表达式指向数组对象的初始成员且不是lvalue。”
只是为了澄清,他认为(a)应该触发数组到指针的衰减,因为括号不在上述列表(sizeof运算符、一元&运算符、字符串字面值作为初始化器)。

7
不,那个人很困惑。 - M.M
1
用他自己的话来说,我同意“终极困惑”。 - chqrlie
1
我大约有15年没有处理这些东西了,但我肯定还记得一个场景,其中sizeof是很重要的,括号的存在或缺失是有意义的 - 决定你是否正在获取指针的大小或元素的大小,或者类似于那样的东西。 - Hot Licks
现在我有点困惑。那段代码的目的是什么?你希望从sizeof表达式中得到什么?数组的长度吗?但你已经知道了。字符的大小吗?那为什么不用sizeof(char)呢?指针的大小,因为数组基本上就是指针? - jamesqf
@jamesqf 我想知道将数组放入括号中是否会触发数组指针衰减。 - fredoverflow
2个回答

26

在C标准中,看似多余的括号是否影响程序的语义是一个长期存在的问题,至今仍未得到充分解决。

通常有人声称((void*)0)技术上不是空指针常量,因为没有规定带括号的空指针常量是空指针常量。

一些编译器会对char s[] = ("abc");发出错误提示,因为虽然字符数组可以从字符串字面值初始化,但该规则并不包括带括号的字符串字面值。

还有许多类似的例子。你已经找到其中之一了。

据我所知,共识基本上是C++应该采用的规则,但C从未正式采纳。C++使带括号的表达式在功能上等同于非带括号的表达式,并明确规定了一些例外情况。这将一次性解决所有这些问题。

因此,从技术上讲,那个人可能是正确的,但这是一个过于严格的标准解释,没有人真正遵循,因为众所周知,标准在这里只是有缺陷而已。


1
很遗憾,我们无法简单地测试标识符是否引用指针或数组,这使得sizeof(argv)容易出错。如果sizeof(argv)sizeof argv评估为不同的内容,那就更糟了。真正需要的是一个countof(a)运算符,它评估为数组的元素数量,并在应用于非数组时发出编译时错误。 - chqrlie
@chqrlie 我宁愿拥有创建 countof 所需的构建块(我通常将其命名为 LENGTHOF,但两者都可以)。使用 C11 的 _Generic,我认为唯一缺少的就是类似于 C++11 的 decltype。如果我们有了它,我们就能够静态验证 argv 的类型与 &*argv 不同。 - user743382
我对 LENGTHOF(a) 不是很感冒,因为它可能会让人们混淆字符串的长度和相应字节数组的大小。我同意使用工具会更好,例如这个:typeof(a) != typeof(&*(a)) - chqrlie
请查看此线程以了解有关此类宏的讨论:@chqrlie 请见此链接 - M.M
@chqrlie 你可能也会对这个问题感兴趣。 - fredoverflow

21
从 C99 的 6.5.1 中关于括号表达式的规定可知:
其类型和值与非括号表达式相同。
乍一看,这似乎与您所提到的例外列表(6.3.2.1)相冲突:
除了作为 sizeof 运算符或一元 & 运算符的操作数,或是用于初始化数组的字符串字面量之外,类型为 "type 的数组" 的表达式会被转换为类型为 "type 的指针" 的表达式......
然而,此列表是针对运算符/操作数的上下文;而括号并未出现在运算符的分类中(基于第 6.5 节结构所暗示的分类)。

由于一些勇敢的人花费了许多时间将PDF文档转换为HTML,因此您可以直接链接到该部分:http://www.iso-9899.info/n1256.html#6.5.1p5,http://www.iso-9899.info/n1256.html#6.3.2.1p3...或者对于C11:http://www.iso-9899.info/n1570.html#6.5.1p5,http://www.iso-9899.info/n1570.html#6.3.2.1p3。 - autistic
1
C11似乎只允许在括号版本(6.5.3#1)中使用类型名称。关于这一点,将括号放在一元表达式周围的版本在语法上将是未加括号的版本,其中括号是一元表达式的一部分。这使得编译器的工作变得更加困难。编辑:C99也是如此。 - too honest for this site

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