2

I have a snippet of code that defines (what I believe to be) an empty array, i.e. an array containing no elements:

int a[] = {};

I compiled the snippet with gcc with no problem

A colleague attempting to get that same code to compile under MSVS made the modification:

int* a = NULL;

No he obviously thought that was an equivalent statemnent that would be acceptable to the MSVS compiler.

However, later in the code I retrieve the no. of elements in the array using the following macro:

#define sizearray(a)  (sizeof(a) / sizeof((a)[0]))

when doing so:

sizearray({}) returns 0

this is as I would expect for what I believe to be a definition of an empty array

sizearray(NULL) returns 1 

I'm thinking that sizeof(NULL)/sizeof((NULL)[0])) is actually 4/4 == 1

as NULL == (void*)0

My question is whether:

int a[] = {}; 

is a valid way of expressing an empty array, or whether its poor programming practice.

Also, is it the case that you can't use such an expression with the MSVS compiler, i.e. is this some sort of C99 compatibility issue?

UPDATE:

Just compiled this:

#include <stdio.h>

#define sizearray(a)  (sizeof(a) / sizeof((a)[0]))

int main()
{
    int a[] = {};
    int b[] = {0};
    int c[] = {0,1};

    printf("sizearray a = %lu\n", sizearray(a));
    printf("sizearray b = %lu\n", sizearray(b));
    printf("sizearray c = %lu\n", sizearray(c));

    return 0;
}

using this Makefile:

array: array.c
    gcc -g -o array array.c

My compiler is:

gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9) 

compiles without any complaint, output looks like this:

bph@marvin:~/projects/scratch/c/array$ ./array
sizearray a = 0
sizearray b = 1
sizearray c = 2

very curious? could it secretly be a C++ compiler, not a C compiler?

Tried John Bodes suggestion of additional compiler flags and can confirm that the compilation does then fail:

gcc --std=c11 --pedantic -Wall -g -o array array.c
array.c: In function ‘main’:
array.c:7:15: warning: ISO C forbids empty initializer braces [-Wpedantic]
     int a[] = {};
               ^
array.c:7:9: error: zero or negative size array ‘a’
     int a[] = {};
         ^
Makefile:2: recipe for target 'array' failed
make: *** [array] Error 1
bph
  • 10,728
  • 15
  • 60
  • 135
  • no - let me double check! – bph Mar 01 '18 at 15:16
  • very possibly.. – bph Mar 01 '18 at 15:17
  • There is no array here. – user2736738 Mar 01 '18 at 15:18
  • You just initialize array with certain number as member. {0} this initialize all the array members to 0. – danglingpointer Mar 01 '18 at 15:18
  • @LethalProgrammer.: Not array. – user2736738 Mar 01 '18 at 15:19
  • Is it compiled without warnings? – i486 Mar 01 '18 at 15:27
  • 1
    Add the flags `--std=c11 --pedantic --Wall` to your gcc command, and you'll get warnings. – John Bode Mar 01 '18 at 15:59
  • this does indeed cause the build to fail, will update the OP with the output - but to be --pedantic, you have to specify -Wall not --Wall ;) – bph Mar 02 '18 at 14:47
  • @JohnBode I'm thinking your flags are maybe a very good starting point for all C dev work? Is that what you generally use? Maybe its like a defacto setting to help produce better quality code? – bph Mar 02 '18 at 14:59
  • I've been using -std=c99 -Wall up until now.. – bph Mar 02 '18 at 15:01
  • 1
    @bph - `-Wall -Werror` should be in every build command IMO - it turns on all warnings, and makes all warnings errors, meaning you *have* to fix them all before the code will build. That's not always realistic (some old APIs will trigger warnings in newer compilers), but it should at least be a starting point. `--pedantic` will reject most (not all!) compiler extensions, giving maximum portability. But, sometimes you want those extensions. – John Bode Mar 02 '18 at 15:58

3 Answers3

7

Empty initializers are invalid in C. So

int a = {};

is ill-formed. See 6.7.9 Initialization.

sizearray(NULL) is not valid either. Because the sizearray macro would expand to:

sizeof 0 /sizeof 0[0])

If NULL is defined as 0. This is not valid because the 0[0] isn't valid because of there's no pointer or array involved (as required for pointer arithmetic - remember a[b] is equivalent to *(a + b)).

Or, it would expand to:

(sizeof(((void *)0)) / sizeof((((void *)0))[0]))

if NULL was as ((void*)0). This is not valid because pointer arithmetic is not allowed on void pointers. See 6.5.6, 2 and void* is an incomplete type. Similar issue be present for whatever the definition of NULL is in an implementation (C standard is flexible with the definition of null pointer constant i.e., NULL. See 7.19, 3).

So in both cases, what you see is compiler specific behaviours for non-standard code.

user2736738
  • 30,591
  • 5
  • 42
  • 56
P.P
  • 117,907
  • 20
  • 175
  • 238
  • ah - massive apologies but I got a typo in the OP, it should have said int a[] = {}; – bph Mar 02 '18 at 14:55
  • 1
    Fortunately, the answer doesn't change. `int a[] = {};` is invalid as well. `{}` is what's usually known as "universal initializer" in C (i.e., you can use it initialize object of any type). You can use it to initialize scalers too (e.g., `int a = {1};` is valid). But empty initializers aren't allowed regardless of what object you are initializing. So `int a[] = {};` is just as invalid as `int a = {};`. – P.P Mar 02 '18 at 15:01
  • I think its clear that I shouldn't use it even though, by chance, my compiler seems to do what I expected, i.e. treat it as an empty array (of length 0). Applying the additional compiler flags as suggested by JohnBode did cause the build to then fail (see updates in OP) – bph Mar 02 '18 at 15:04
5

This is not an array, but it's not a scalar either: it's a syntax error.

The C11 draft says, in §6.7.9.11 (Initialization Semantics):

The initializer for a scalar shall be a single expression, optionally enclosed in braces. The initial value of the object is that of the expression (after conversion); the same type constraints and conversions as for simple assignment apply, taking the type of the scalar to be the unqualified version of its declared type.

But there has to be something between the braces, it can't be empty.

So I'd argue that the question is missing something, and this was not the actual code.

unwind
  • 391,730
  • 64
  • 469
  • 606
  • 1
    this answer is also quite useful -> https://stackoverflow.com/questions/201101/how-to-initialize-all-members-of-an-array-to-the-same-value – bph Mar 01 '18 at 15:24
  • have you seen the OP update? I'm really surprised it compiles bearing in mind all these answers showing that it shouldn't – bph Mar 02 '18 at 14:41
  • can syntax errors compile? i.e. could a syntax error fall into the category of 'compiler specific behaviours for non-standard code' and still compile? – bph Mar 02 '18 at 14:51
3

It's not an array, it's the brace initialization syntax.

Short word, you can write this:

int a = {1234};

It does not initialize a with an array, it just assigns 1234. If there are 2 or more values, that's be an error.

Brace initialization disables value truncating, so:

char b = 258; // Valid, same as b = 2
char b = {258}; // Wrong, can't truncate value in braces

And empty braces are just zero-initializers, so int a = {} is equivalent to int a = {0}

iBug
  • 35,554
  • 7
  • 89
  • 134
  • what's value truncating? – bph Mar 01 '18 at 15:21
  • 4
    Empty braces in standard C is a syntax error. GCC and C++ have different interpretations. I don’t think your comments about value truncation apply in C either. – Jonathan Leffler Mar 01 '18 at 15:22
  • @bph 258 is too big for a byte (0-255), so the higher bytes will be truncated, i.e. `0x102` -> `0x2` – iBug Mar 01 '18 at 15:22
  • 1
    @iBug I can't see anything about the truncating behaving differently in the standard, I don't think that's true. – unwind Mar 01 '18 at 15:25
  • iBug, you cannot say "_It does not initialize a with an array_" At best you can say "_It does not declare `a` as an array_" because `a` is declared here as just an `int.` – Paul Ogilvie Mar 01 '18 at 15:40
  • 1
    @PaulOgilvie *"iBug, you cannot say"* Why not? `{1234}` is not an array. – HolyBlackCat Mar 01 '18 at 15:43
  • @HolyBlackCat, because `a` is an `int` and you cannot "initialize" an int with an array. Confusion is about declaration (`int a[4];`) and initialization (`int a[]={1,2,3,4};`). – Paul Ogilvie Mar 01 '18 at 15:45
  • oh man, just spotted a typo in my OP, should have written int a[] = {}; - sorry about that, now corrected – bph Mar 02 '18 at 14:53
  • @JonathanLeffler 'GCC and C++ have different interpretations' - could you possibly explain the GCC interpretation or point me in the right direction? – bph Mar 02 '18 at 15:33
  • haha - googling 'gcc interpreation of empty braces' brings me back to this question (curse you google) – bph Mar 02 '18 at 15:34