Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the lifetime of compound literals passed as arguments?

This compiles without warnings using clang.

typedef struct {
  int option;
  int value;
} someType;

someType *init(someType *ptr) {
  *ptr = (someType) {
    .option = ptr->option | ANOTHEROPT,
    .value = 1
  };

  return ptr;
}

int main()
{
  someType *typePtr = init( &(someType) {
    .option = SOMEOPT
  });
  // do something else with typePtr
}
  1. Is this even valid C?

  2. If so: What is the lifetime of the compound literal?

like image 799
vestol Avatar asked Feb 19 '14 13:02

vestol


2 Answers

It's valid C in C99 or above.

C99 §6.5.2.5 Compound literals

The value of the compound literal is that of an unnamed object initialized by the initializer list. If the compound literal occurs outside the body of a function, the object has static storage duration; otherwise, it has automatic storage duration associated with the enclosing block.

In your example, the compound literal has automatic storage, which means, its lifetime is within its block, i.e, the main() function that it's in.

Recommended reading from @Shafik Yaghmour:

  1. The New C: Compound Literals
  2. GCC Manual: 6.25 Compound Literals
like image 171
Yu Hao Avatar answered Oct 05 '22 22:10

Yu Hao


Yu Hao has answered with the standard, now some vulgarization.

Whenever you see a compound literal like:

struct S *s;
s = &(struct S){1};

you can replace it with:

struct S *s;
struct S __HIDDEN_NAME__ = {1};
s = &__HIDDEN_NAME__;

So:

main.c

#include <assert.h>

struct S {int i;};
/* static: lives for the entire program. */
struct S *s1 = &(struct S){1};
struct S *s2;
struct S *s3;
struct S *s4;

int f(struct S *s) {
    return s->i + 1;
}

int main() {
    /* Undefined behaviour: not defined yet.
     * GCC 10 -fsanitize=undefined -ggdb3 -O0 -std=c99 gives at runtime:
     * runtime error: member access within null pointer of type 'struct S' */
#if 0
    assert(f(s2) == 1);
#endif

    /* Lives inside main, and any function called from main. */
    s2 = &(struct S){1};
    /* Fine because now instantiated. */
    assert(f(s2) == 2);

    /* Only lives in this block. */
    {
        s3 = &(struct S){1};
        /* Fine. */
        assert(f(s3) == 2);
    }
    {
        /* On GCC 10 -O0, this replaces s3 above due to UB */
        s4 = &(struct S){2};
    }
    /* Undefined Behavior: lifetime has ended in previous block.
     * On GCC 10, ubsan does not notice it, and the assert fails
     * due to the s4 overwrite.*/
#if 0
    assert(s3->i == 1);
#endif
}

Full compilation command:

gcc -fsanitize=undefined -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c