100

I was snooping through my MSP430 microcontroller's header files, and I ran into this in <setjmp.h>:

/* r3 does not have to be saved */
typedef struct
{
    uint32_t __j_pc; /* return address */
    uint32_t __j_sp; /* r1 stack pointer */
    uint32_t __j_sr; /* r2 status register */
    uint32_t __j_r4;
    uint32_t __j_r5;
    uint32_t __j_r6;
    uint32_t __j_r7;
    uint32_t __j_r8;
    uint32_t __j_r9;
    uint32_t __j_r10;
    uint32_t __j_r11;
} jmp_buf[1]; /* size = 20 bytes */

I understand that it declares an anonymous struct and typedef's it to jmp_buf, but I can't figure out what the [1] is for. I know it declares jmp_buf to be an array with one member (of this anonymous struct), but I can't imagine what it's used for. Any ideas?

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
Alexander
  • 59,041
  • 12
  • 98
  • 151

1 Answers1

122

This is a common trick to make a "reference type" in C, where using it as a function argument causes the single element array to degrade to a pointer to its first element without the programmer needing to explicitly use the & operator to get its address. Where declared, it's a real stack type (no dynamic allocation needed), but when passed as an argument, the called function receives a pointer to it, not a copy, so it's passed cheaply (and can be mutated by the called function if not const).

GMP uses the same trick with its mpz_t type, and it's critical there, because the structure manages a pointer to dynamically allocated memory; the mpz_init function relies on getting a pointer to the structure, not a copy of it, or it couldn't initialize it at all. Similarly, many operations can resize the dynamically allocated memory, and that wouldn't work if they couldn't mutate the caller's struct.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
  • 13
    It also prevents copying via `=`. – melpomene Nov 02 '17 at 23:57
  • 12
    That's gross. I'll accept this answer once the min time elapses. Thanks for your help! – Alexander Nov 02 '17 at 23:57
  • 3
    @Alexander: It's not quite so gross when encapsulated via a `typedef` like this. Yeah, doing this ad-hoc would be kinda terrible, but if you have a faintly opaque type, where the API user never needs to think about reference vs. non-reference semantics (it should *always* pass by reference), it's a reasonable way of adding automatic reference semantics to a language that otherwise lacks it. It even works if the user writes their own APIs that receive the type, because in C, declaring you accept an array as an argument really means you accept a pointer; everything "just works". – ShadowRanger Nov 03 '17 at 00:07
  • 4
    @ShadowRanger It's a clever trick, but `... otherwise lacks it` is what's gross about it. The limitations of C, not the workaround itself – Alexander Nov 03 '17 at 00:12
  • 36
    IMO it's gross. The first time working with GMP I couldn't understand how it worked since the numbers were apparently passed by value. I had to dig into the GMP headers to puzzle it out. It just flies in the face of people who actually do know C already. Then you have to keep mental track of which parameters are passed by value and which are reference instead of just looking for a `*` in the code. – M.M Nov 03 '17 at 00:37
  • 2
    @M.M: See, I was puzzled in the same way, and investigated as well, but my response to it was to appreciate the cleverness. For GMP functions, *all* of the GMP type arguments are pass by reference, so there's not a lot of mental overhead. Beyond automatic pass-by-reference, blocking assignment and pass-by-value prevents accidents (where two `mpz_t`s end up containing a pointer to the same memory, which might get `free`-ed or `realloc`-ed on only one, leaving garbage pointers behind). Even newbies can't accidentally do `mpz_t mpz2 = mpz2;` and break everything. – ShadowRanger Nov 03 '17 at 23:19
  • 1
    One more thing that should be noted for the specific example of `jmp_buf` is that this _grossness_ is actually demanded by the C standard: "The type declared is `jmp_buf` which is an array type suitable for holding the information needed to restore a calling environment." (C99, 7.13). A simple struct would not comply with the standard! – stefanct May 03 '21 at 21:32