Create string in C where first character is string length

Viewed 132

This question is related to Concatenate string literal with char literal, but is slightly more complex.

I would like to create a string literal, where the first character of the string is the length of the string, and the second character is a constant. This is how it is being done currently:

const char myString[] = 
{
    0x08,
    SOME_8_BIT_CONSTANT,
    'H',
    'e',
    'l',
    'l',
    'o',
    0x00
};

Ideally, I would like to replace it with something like:

const char myString[] = BUILD_STRING(0xAA, "Hello");

I tried implementing it like this:

#define STR2(S) #S
#define STR(S) STR2(S)
#define BUILD_STRING(C, S)  {(sizeof(S)+2), C, S}

const char myString[] = BUILD_STRING(0xAA, "Hello");

but it expands to:

const char myString[] = {(sizeof("Hello")+2), 0xAA, "Hello"};

and the compiler doesn't seem to like mixing numbers and strings.

Is there any way to do this?

1 Answers

You could in-place define a structure to hold the prefix and the rest, conveniently initialize it, and then treat the whole struct as a char array (not a strict-aliasing violation because standard C lets you treat any object as a char array).

Technically, you're not guaranteed that the compiler won't insert padding between the prefix and the rest, but in practice you can count on it.

#define BUILD_STRING(C, S)  \
      ((char const*)&(struct{ char const p[2]; char const s[sizeof(S)]; })\
                            { {(sizeof(S)+2), C}, S})

const char *myString = BUILD_STRING(0xAA, "Hello");

#include <stdio.h>
int main()
{
    printf("%d, %#hhX, '%s'\n", myString[0], myString[1], myString+2);
    //PRINTS: 8, 0XAA, 'Hello'
}

Edit:

If you're paranoid about the possibility of padding, here's one way to statically assert none is inserted:

#define BUILD_STRING__(S)  \
      char const p[2]; char const s[sizeof(S)]
#define BUILD_STRING(C, S)  \
      ((char const*)&(struct{BUILD_STRING__(S); \
            _Static_assert(sizeof(S)+2== \
                    sizeof(struct{BUILD_STRING__(S);_Static_assert(1,"");}),""); \
        }){ {sizeof(S)+2, C}, S})

Alternatively, using the first version with the (nonstandard) __attribute((__packed__)) should do the same.

Related