61

I hit some code in Linux kernel:

static struct ctl_table ip_ct_sysctl_table[] = {
    {
        .procname   = "ip_conntrack_max",
        .maxlen     = sizeof(int),
        .mode       = 0644,
        .proc_handler   = proc_dointvec,
    },
    // ...
    {
        .procname   = "ip_conntrack_log_invalid",
        .maxlen     = sizeof(unsigned int),
        .mode       = 0644,
        .proc_handler   = proc_dointvec_minmax,
        .extra1     = &log_invalid_proto_min,
        .extra2     = &log_invalid_proto_max,
    },
    { }
};

Here an array of structs ends with { }. For what purpose was it added?
By the way, a bit above this code there is another array of structs, but without empty braces at the end.

When should I use empty braces at the end of an array of structs?

red0ct
  • 4,840
  • 3
  • 17
  • 44
NK-cell
  • 1,145
  • 6
  • 19
  • 1
    Hmm what if it added to signal the end of the array like 0 signal the end of the string? Just guessing. – Eraklon Mar 02 '20 at 13:07
  • 4
    This is some non-standard GCC extension. And as such, it very likely comes with little or no documentation... I just read all the docs and I can't find anything about empty struct initializer lists. Yet it compiles, unless you force strict ISO with `-pedantic`. – Lundin Mar 02 '20 at 13:35
  • 9
    Anyway, it is a "sentinel" value, an item with everything set to zero/NULL to mark the end of the array. – Lundin Mar 02 '20 at 13:40
  • Sentinels are also common in [CPython extension modules](https://docs.python.org/3.7/extending/extending.html#the-module-s-method-table-and-initialization-function). – MaxPowers Mar 02 '20 at 22:06

4 Answers4

39

This particular change was part of the sysctl net: Remove unused binary sysctl code commit by Eric W. Biederman, changing the initialization of the last element of the ip_ct_sysctl_table array from {0} to {} (and performs similar changes to many other array initializations).

The {0} pattern seems to have been around for much longer though, and both {0} or {} final element-initialization is commonly (in the Linux source code) explicitly referred to as Terminating entry, so it is likely a pattern present to allow consuming these arrays without knowing their lengths, terminating consumption when hitting the zero-initialized terminating entry. E.g. for the similar arrays in sound/aoa/fabrics/snd-aoa-fabric-layout.c the intent of the zero-initialization is even explicitly mentioned in a comment, e.g.:

static struct codec_connection toonie_connections[] = {
  {
      .connected = CC_SPEAKERS | CC_HEADPHONE,
      .codec_bit = 0,
  },
  {} /* terminate array by .connected == 0 */
};
dfrib
  • 70,367
  • 12
  • 127
  • 192
  • 11
    It would be interesting to know their rationale for dropping standard C in favour of a GCC extension that is 100% equivalent in terms of functionality. All it does is preventing the code from compiling on standard C compilers. That is, supposedly 100% equivalent because gcc doesn't seem to document this feature... This is _not_ a zero-length array, it's an empty initializer list. – Lundin Mar 02 '20 at 15:32
  • @Lundin Would not `int arr[] = {}` (given that we are using the GNU empty initializer extension) result in an empty array; i.e., the size of `arr` being `0`? – dfrib Mar 02 '20 at 16:02
  • @dfri: It can't in this case because the type of object being initialized is given rather than inferred. – supercat Mar 02 '20 at 21:51
  • @Lundin: I've seen some compilers (MSVC compiling in C mode several years ago, never looked on gcc) that will insist on individual initialization of the single explicitly initialized field followed by a `memset` to zero of the remaining fields, even though it could have zero-initialized the whole structure with a single `memset`. Not saying that's the rationale here, but it's possible it's trying to tell the compiler "zero-init everything in bulk" rather than "set first attribute to `0`, then zero-init the rest in bulk". – ShadowRanger Mar 03 '20 at 05:04
  • @Lundin On the flip side, I don't see why the C standards committee doesn't legalize `{}` for member-wise initialization to 0. `{0}` has the disadvantage of being less general since it requires that the first member not be an aggregate. – jamesdlin Mar 03 '20 at 06:38
  • @ShadowRanger The behavior you describe would not be conformant (and strictly speaking, `memset` and member-wise initialization to 0 are not the same). – jamesdlin Mar 03 '20 at 06:40
  • @dfri No, an initializer list cannot be empty in C. – Lundin Mar 03 '20 at 07:32
  • @ShadowRanger Such compilers would be quite broken. MSVC has a very poor reputation in C mode, so looking at it for reference of how C compilers should behave will do you no good. – Lundin Mar 03 '20 at 07:34
  • @jamesdlin: `{0}` works even for a subaggregate (or union); see 6.7.8p20. – dave_thompson_085 Mar 03 '20 at 07:38
  • This [cppreference](https://en.cppreference.com/w/c/language/struct_initialization) page describes initialization rules well and in detail. This particular chapter of the standard isn't easy reading. – Lundin Mar 03 '20 at 07:47
  • 1
    @Lundin: The cppreference page is however in conflict with the wording of ISO/IEC 9899:2011, which allows for that (§6.7.9 (21)). No initializers are undoubtly "fewer" than members of the aggregate. So that's not a queer compiler extension, but legitimate C. – Damon Mar 03 '20 at 12:19
  • 2
    @Damon It is not valid C and it's well-known... compile with gcc -pedantic-errors. To understand why, you need to read the actual syntax for an initializer list, top of 6.7.9. There must be at least one initializer. Explained here: https://stackoverflow.com/questions/17589533/is-an-empty-initializer-list-valid-c-code. Specifically `{ initializer-list }` then initializer list: `designation(opt) initializer` or `initializer-list , designation(opt) initializer` – Lundin Mar 03 '20 at 12:43
  • 2
    @Lundin In this specific instance, no idea. But gcc extensions are used extensively in the linux kernel. – bobsburner Mar 03 '20 at 13:31
21

You're probably familiar with zero-terminated strings. ctl_table ip_ct_sysctl_table[] is a zero-terminated array, i.e. the last array entry has all-zero members.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • 1
    So going through the array, you know you have reached the end when e.g. `procname` is null, or `maxlen` is zero. – Paul Ogilvie Mar 02 '20 at 13:24
  • 1
    @PaulOgilvie: Well, the example is incomplete. `procname` could be a `char[100]` in which case it's `""`, not null. But otherwise yes. – MSalters Mar 02 '20 at 13:57
13

What is the need of empty braces '{ }' at the end of array of structs?

To be clear: the "empty braces '{ }' at the end of array of structs" is not needed to satisfy C syntax requirements.

When should I use empty braces at the end of an array of structs?

When code wants a sentinel value.

It is sometimes useful for the program to have a final array element of all zeros - certainly to detect the end. The need comes from the application's use of array ctl_table ip_ct_sysctl_table[], not from a C language need.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
9

It's one zero initialized element at the end of the array in order to increase the number of elements of the array by one.

Consider this small demo:

#include <stdio.h>

struct Test
{
  int x;
  int y;
} arr[] =
{
    {1,2},
    {3,4},
//  {}
};

int main(void) {
    printf("%zu\n", sizeof(arr) / sizeof(arr[0]));
    return 0;
}

The size of the arr array will change if you uncomment the {} at the end of the array initialisation list.

Outputs:

With // {} (array has 2 elements)

2

With {} (array has 3 elements)

3

Further explanation:

The ip_ct_sysctl_table array is only used at one place, that is here:

in->ctl_table = kmemdup(ip_ct_sysctl_table,
                sizeof(ip_ct_sysctl_table),
                GFP_KERNEL);

The extra {} increases the total size ip_ct_sysctl_table.

Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
  • 1
    That is not "in order to increase the number of elements of the array" but to signal the end of the array. – Paul Ogilvie Mar 02 '20 at 13:25
  • 6
    LOL, no. The idea is that no-one so far has been able to explain it completely, with absolute certainty. The closest statement of certainty is simply that the `{ }` is an initializer. But _the why_ is still unclear. Thus, for now anyway, the word _probably_ is probably a good idea. :) – ryyker Mar 02 '20 at 13:26