15

The following C program:

#include <stdio.h>

int main(void)
{
    printf("%u %u %u\n",sizeof "",sizeof(""+0),sizeof(char *));
    return 0;
}

outputs 1 4 4 when compiled with GCC on Linux, but outputs 1 1 4 when compiled with Microsoft Visual C++ on Windows. The GCC result is what I would expect. Do they differ because MSVC has a bug or because sizeof(""+0) is undefined? For both compilers the behaviour (i.e. whether the middle value printed is equal to the first value or the last value) is the same no matter what string literal or integer constant you use.

A relevant reference in the ANSI C Standard seems to be 6.2.2.1 - Lvalues and function designators:

Except when it is the operand of the sizeof operator ... an lvalue that has type 'array of type' is converted to an expression that has type 'pointer to type' that points to the initial element of the array object and is not an lvalue.

Here though the "Except" should not apply because in sizeof(""+0) the array/string literal is an operand of + not sizeof.

phuclv
  • 37,963
  • 15
  • 156
  • 475
cdev
  • 787
  • 5
  • 10
  • Perhaps MS is doing some sort of optimization, replacing "" with '\0'? What happens if you take sizeof(""+(long long)0) ? – Lundin Feb 01 '11 at 12:34
  • Replacing `""` with `'\0'` would violate the type system. More interestingly, what's `sizeof(""+1)`? – Fred Foo Feb 01 '11 at 12:39
  • technically you got UB because `sizeof` is of type `size_t` which [must be printed using `%zu`](https://stackoverflow.com/q/940087/995714) – phuclv Apr 05 '21 at 14:44

3 Answers3

7

Because "fooabc" is of type char[7], sizeof("fooabc") yields the same as sizeof(char[7]). However, arrays can be implicitly converted - the part you quoted - to pointers (some people wrongly call this "decay"), and since this is necessary for the arithmetic (+) to work, ""+0 will have a type of char*. And a char pointer can have a different size than the array. In that regard, MSVC's behavior seems broken.

user562374
  • 3,817
  • 1
  • 22
  • 19
  • 1
    I'd hardly say that using the term "decay" is wrong. It's very much established usage, and IIRC might also be found in the Standard text (I'm unable to check right now). – eq- Feb 01 '11 at 17:55
  • @eq- It does not. §6.3.2.1 “3 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*’’ that points to the initial element of the array object and is not an lvalue.” – Josh Lee Feb 02 '11 at 16:45
  • @eq it's not in the standard text, but is common terminology – M.M Jun 21 '15 at 10:25
  • @JoshLee: There are a number of cases where the Standard uses one term to describe several related constructs, but does not specify individual terms for each. The most horribly overloaded term is probably "object", but "convert" also encompasses several disjoint kinds of actions, some of which lack individual terms. It seems much more useful to use terms like "decay" and "coercion" than to refer to "conversion of an array object into a pointer to the first element" and "a conversion that is performed without a cast when using a value to write or initialize an lvalue". – supercat Jul 13 '18 at 15:54
1

I guess it's an error of MSVC.

""+0 takes the address of "" (which is decayed in type char*) and sums 0 to it. That expression type is char*.

Play a little with it: what's the value of ""+0? and of ""+1? and of sizeof(""+1)?

peoro
  • 25,562
  • 20
  • 98
  • 150
1

Looks like a bug in MSVC. Apparently it's optimizer removes the +0 before doing a proper type analysis.

Fred Foo
  • 355,277
  • 75
  • 744
  • 836
  • In that case, try out sizeof("" + (volatile const char*)0) and see if it makes any difference. (Though I guess MS are likely to miscompile volatile as well...) – Lundin Feb 01 '11 at 12:40
  • 3
    @Lundin: You cannot add two pointers together, so the suggested `sizeof( "" + (volatile const char*)0 )` is a compiler error --i.e. the expression cannot be resolved. If you want a similar test, just use: `extern int x; sizeof( ""+x );` No need to provide a definition for `x` as it is not actually *used*, and the compiler will be unable to optimize it away. – David Rodríguez - dribeas May 16 '11 at 21:15