Updates:
- 2020-04-30 – Added missing semicolon in code and updated example.
NOTE: This article was originally written a few years ago, so some references may be out of date.
I have been enjoying working on the SirSound project the past month, as well as some fun challenges at my day job. Every now and then I run into something I’d like to do that is not doable in C, or doable but not proper to do in C, or … maybe doable. It’s sometimes difficult for me to find answers when I do not know how to ask the question.
With that said, I have a C question for anyone who might be able to answer it.
Using my multi-track music sequencer as an example, consider representing data like this:
[sequence] [track] [note /] [note /] [note /] [/track] [track] [note /] [note /] [note /] [/track] [/sequence]
It’s easy to create a sequence like this in C. Here’s some pseudo code:
track1 = { C, C, C, C }; track2 = { E, E, E, E }; sequence1 = { track1, track 2};
I thought there might be a clever way to do all of this with one initializer. If I treat the data like nested XML (like the first example), I thought it might be possible to do something like this:
typedef struct SentenceStruct SentenceStruct; struct SentenceStruct { char *Word; SentenceStruct *Next; };
Something like this allows me to represent that tree of data very easily, and I find many examples of building things like this in C code:
int main() { SentenceStruct Word1; SentenceStruct Word2; SentenceStruct Word3; Word1.Word = "sequence"; Word1.Next = &Word2; Word2.Word = "track"; Word2.Next = &Word3; Word3.Word = "note1"; Word3.Next = NULL; SentenceStruct *Word; Word = &Word1; while( Word != NULL ) { printf("%s ", Word->Word); if (Word->Next == NULL) break; Word = Word->Next; } printf("\n"); return EXIT_SUCCESS; }
This creates a single linked list of words, then prints them out.
I thought it might be possible to initialize the whole thing, like this:
SentenceStruct sentence = { { "kitchen", { "light", { "ceiling", { "on", NULL }}}}; }
…though the address of “light” is quite different than the address of a structure which contains a pointer that should point to “light”.
I was going to make each one a pointer to an array of words, so I could have a tree of words like the earlier example (with kitchen/light and kitchen/fan):
typedef struct SentenceStruct SentenceStruct; struct SentenceStruct { char *Word; SentenceStruct *Next[]; }
Does anyone know how (or if) this can be done in C?
Thoughts?
Looks like in C11 you can use a “compound literal”. I’ve yet to mess with them, though I will, so I can keep up, and because some of the stuff there is neat, e.g. you can write
int foo[200] = {[24] = 1, [108] = 6};
and not have to explicitly initialize the elements 0-108.
Check out https://stackoverflow.com/questions/60022421/best-way-to-statically-initialize-a-linked-list-in-c
Oh, I love that. Does it say if it zeros out the rest?
The code below seems to work. The macro makes the initializer a bit less cluttered. Of course you do need to add or remove a closing brace on the last line for each word added or removed.
typedef struct SentenceStruct SentenceStruct;
struct SentenceStruct
{
char *Word;
SentenceStruct *Next;
};
#define WORD(s) &(SentenceStruct){s
SentenceStruct *sentence =
WORD(“kitchen”), WORD(“light”), WORD(“ceiling”), WORD(“on”), NULL
}}}};
Very interesting. Let me give that a shot.
That’s really cool. I was looking at a way to do this for an Arduino music sequencer. I also looked into it when I was implementing CoAP protocol. It works like URL paths. Maybe one device had a few lights and a fan and a temperature sensor on it:
I could POST to that device “/light/0/on” or “/light/2/off” to have it happen. I could GET “/thermometer/0” and get back a temperature.
To create a list of endpoints without redundancy I was trying to do them like:
Now that you’ve shown me how to string things together, I’m going to see if I can use that to define it like that.