26

The following program prints the same number twice on 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));
}

According to this reddit post, gcc is not standard-conformant in this respect, because a parenthesized expression is not on the list of exceptions for when array-to-pointer decay does not happen.

Is this guy correct? Here is the relevant standard quote:

Except when it is the operand of the sizeof operator or the unary & operator, or is a character string literal used to initialize an array of character type, or is a wide string literal used to initialize an array with element type compatible with wchar_t, an lvalue that has type 'array of type' is converted to an expression that has type 'pointer to type' that points to the initial member of the array object and is not an lvalue.

Just to be clear, he argues that (a) should trigger array-to-pointer decay, because parentheses are not covered in the list above (sizeof operator, unary & operator, string literal as initializer).

fuz
  • 76,641
  • 24
  • 165
  • 316
fredoverflow
  • 237,063
  • 85
  • 359
  • 638
  • 7
    No, that guy is seriously confused – M.M May 31 '15 at 13:25
  • 1
    In his own words, I would agree with *terminally confused* – chqrlie May 31 '15 at 13:46
  • 1
    I haven't dealt this this stuff for about 15 years, but I'm definitely recalling a scenario with, I think, `sizeof` where the presence or absence of parentheses was significant -- determined if you were taking the size of the pointer or the size of the element, or something like that. – Hot Licks May 31 '15 at 14:08
  • Now I'm confused. What exactly is the purpose of that code? What are you expecting to get back from your sizeof expressions? The length of the array? But you KNOW that. The size of a char? Then why not sizeof (char)? The size of a pointer, since arrays basically are pointers? – jamesqf May 31 '15 at 18:42
  • @jamesqf I want to know if putting an array into parentheses triggers array-to-pointer decay. – fredoverflow May 31 '15 at 19:01

2 Answers2

26

Whether seemingly redundant parentheses affect the semantics of a program is a long-standing issue in the C standard that still hasn't been adequately resolved.

It is commonly claimed that ((void*)0) is technically not a null pointer constant, because there is no rule that says a parenthesised null pointer constant is a null pointer constant.

Some compilers issue an error for char s[] = ("abc");, because while a character array can be initialised from a string literal, that rule doesn't cover parenthesised string literals.

There are many similar examples. You've found one of them.

From what I can tell, the concensus is basically that the rule should be what C++ does, but what C never formally adopted. C++ makes a parenthesised expression functionally equivalent to the non-parenthesised expression, with a few explicitly-stated exceptions. This would cover all those issues at once.

So technically, the guy could be considered correct, but it's an overly strict interpretation of the standard that nobody really follows, since it's common knowledge that the standard is simply faulty here.

  • 1
    It is bad enough that one cannot simply test if an identifier refers to a pointer or an array, making `sizeof(argv)` error prone. It would be even worse if `sizeof(argv)` and `sizeof argv` evaluated to a different thing. What is really needed is a`countof(a)` operator that evaluates to the number of elements of an array and issues a compile time error when applied to a non array. – chqrlie May 31 '15 at 14:28
  • @chqrlie I'd rather have the building blocks needed to create `countof` (I've usually seen it named `LENGTHOF`, but either works) ourselves. With C11's `_Generic`, all that's missing, I think, is something like C++11's `decltype`. If we had that, we'd be able to statically verify that the type of `argv` is different from `&*argv`. –  May 31 '15 at 14:33
  • I find `LENGTHOF(a)` less appealing because of the potential confusion between the length of a string and the size of the corresponding byte array. I agree that the tools would suffice, such as this: `typeof(a) != typeof(&*(a))` – chqrlie May 31 '15 at 14:49
  • @chqrlie [see this thread](http://stackoverflow.com/questions/19452971/array-size-macro-that-rejects-pointers) for discussion of such a macro – M.M May 31 '15 at 14:53
  • @chqrlie You may also be interested in [this question](http://stackoverflow.com/questions/12784136/). – fredoverflow May 31 '15 at 14:58
21

From C99, 6.5.1, on parenthesized expressions:

Its type and value are identical to those of the unparenthesized expression.

At first glance, it would appear that this conflicts with the exception list you're referring to (6.3.2.1):

Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type "array of type" is converted to an expression with type "pointer to type"...

However, this list is in the context of operators/operands; parentheses don't appear to be deemed an operator (based on the categorisation implied by the structure of section 6.5).

Oliver Charlesworth
  • 252,669
  • 29
  • 530
  • 650
  • You could link directly to the section thanks to some brave soul who spent many hours converting the pdf document to html: http://www.iso-9899.info/n1256.html#6.5.1p5, http://www.iso-9899.info/n1256.html#6.3.2.1p3 ... or for C11: http://www.iso-9899.info/n1570.html#6.5.1p5, http://www.iso-9899.info/n1570.html#6.3.2.1p3 – autistic May 31 '15 at 14:25
  • 1
    C11 appears to allow a _type-name_ only in the parenthized version (6.5.3#1). Regarding this, a version with parenthesis around a _unary-expression_ would syntactically be the unparenthized version with the parenthesis being part of the _unary-expression_. Makes the compiler's job not easier. Edit: same for C99. – too honest for this site May 31 '15 at 14:51