Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create string in C where first character is string length

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?

like image 882
Rocketmagnet Avatar asked Jun 17 '19 16:06

Rocketmagnet


People also ask

How do you get the first character of a string as a string?

To get the first character of a string, we can call charAt() on the string, passing 0 as an argument. For example, str. charAt(0) returns the first character of str . The String charAt() returns the character of a string at the specified index.

How do you get only the first letter in a string in C?

Use a %s format (or, better, %255s format) for the two scanf() calls. Then pass firstname[0] and lastname to printf() . You might want to think about using tolower() from <ctype. h> on the first letter, and maybe on the last name too.

How do you declare a string without specifying length?

'C' also allows us to initialize a string variable without defining the size of the character array. It can be done in the following way, char first_name[ ] = "NATHAN"; The name of Strings in C acts as a pointer because it is basically an array.


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.

like image 146
PSkocik Avatar answered Oct 03 '22 09:10

PSkocik