11

Am I allowed to do this in C99?

typedef struct dlNode {
    dlNode* next, prev;
    void* datum;
} dlNode;

const static dlNode head={
    .next = &tail,
    .prev = NULL,
    .datum = NULL
};

const static dlNode tail={
    .next = NULL,
    .prev = &head,
    .datum = NULL
};

I can make my program work without this. It'd just be convenient.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mr. Minty Fresh
  • 229
  • 1
  • 6

2 Answers2

15

You can. You just have to forward declare tail to get it to work:

typedef struct dlNode {
    struct dlNode* next;
    struct dlNode* prev;
    void* datum;
} dlNode;

const static dlNode tail;

const static dlNode head={
    .next = &tail,
    .prev = NULL,
    .datum = NULL
};

const static dlNode tail={
    .next = NULL,
    .prev = &head,
    .datum = NULL
};
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ldav1s
  • 15,885
  • 2
  • 53
  • 56
  • Thanks, I didn't know that you could (effectively) prototype variables & structs. – Mr. Minty Fresh Sep 18 '17 at 23:44
  • 7
    Note that this is called "tentative definitions" and does not work in C++. – o11c Sep 19 '17 at 00:02
  • @o11c That pattern is easily replaceable by a class. – bunyaCloven Sep 19 '17 at 07:00
  • @bunyaCloven doesn't mean it's not worth mentioning that tentative definitions specifically don't work in C++ – Pierre Arlaud Sep 19 '17 at 07:54
  • 1
    Specifically, in C++, slapping a `class foo{ } foo_instance;` around head/tail (and removing `static`) will get it to compile to read-only data in static storage, even without optimization. https://godbolt.org/g/T9Mavc. – Peter Cordes Sep 19 '17 at 08:30
  • Ooh, I never managed to get any way to work, I ended up making it `extern`. Adding an artificial class never occurred to me, I wouldn't call it "easy". – o11c Sep 19 '17 at 16:32
5

You are absolutely allowed to do it: add a forward declaration of tail, and C will merge it with a later definition:

typedef struct dlNode {
    const struct dlNode* next, *prev;
    void* datum;
} dlNode;

const static dlNode tail; // <<== C treats this as a forward declaration

const static dlNode head={
    .next=&tail,
    .prev=NULL,
    .datum=NULL
};

const static dlNode tail={ // This becomes the actual definition
    .next=NULL,
    .prev=&head,
    .datum=NULL
};

Note that you should fix your struct declaration to make next and prev constant, otherwise your definition would discard constant qualifiers.

Demo.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523