... The overhead is annoying, so I'm not sure if
I really want to take this route, but it's nice to know there is at
least one really portable way to do this. Thanks!
The assumption I had here was that you are trying to have a 'char[]'
where the first element indicates the number of subsequent elements.
...
The code example below attempts to adjust for the padding you were
worried about. If you have:
struct {
unsigned char size;
char string[sizeof "foo"];
} foo = {
sizeof "foo",
string = "foo",
};
then I agree with you that it's possible that there might be padding
between the 'size' member and the 'string' member.
So we can look at the offset of the 'string' member and consider
something like this:
struct {
unsigned char size[XXX];
char string[sizeof "foo"];
} foo = {
{ [XXX - 1] = sizeof "foo" },
string = "foo",
};
where 'XXX' was the offset of 'string' we noted and the position 'XXX -
1' ought to designate the 'size' element that comes immediately before
the 'string' member. That'll probably do the trick. (Some insane
implementation might add further padding even though we "adjusted" for
padding. If we used struct tags, we could actually get guarantees,
instead!)
But if we are going to point a 'char *' to the last element of the
'size' member and we pass that pointer to a function which attempts to
read 'char' elements beyond, we could run out-of-bounds.
So we can "protect" the whole thing by wrapping with a union that has
the above struct as a member but also has a 'char[]' member that matches
the whole size of the struct. We can then point to where the last
element of 'size' would be if it were in that member. The bounds should
be sufficient.
If you need more portable guarantees for the tag-less struct business,
one could potentially make use of '__LINE__'.
#define AUTOTAG__(line) autotag_ ## line ## __
#define AUTOTAG_(line) AUTOTAG__(line)
#define AUTOTAG AUTOTAG_(__LINE__)
Anyway, the code is also available, with nice colours, at:
http://codepad.org/qYUfXcmR
Here's the code:
#include <stddef.h>
#include <stdio.h>
typedef unsigned char byte__;
/* Derived from Chris M. Thommason */
#define STRING_ALIGNMENT(str) ( \
offsetof( \
struct { \
byte__ b; \
char ca[sizeof (str)]; \
}, \
ca \
) \
)
#define STRING_STRUCT(str) \
struct { \
byte__ pre[STRING_ALIGNMENT(str)]; \
char ca[sizeof (str)]; \
}
#define STRING_STRUCT_INIT(str) { \
.pre = { \
[STRING_ALIGNMENT(str) - 1] = \
sizeof (str), \
}, \
.ca = str, \
}
#define STRING_UNION(str) \
union { \
STRING_STRUCT(str) s; \
byte__ ba[sizeof (STRING_STRUCT(str))]; \
}
#define STRING(str) ( \
(char *) ( \
(STRING_UNION(str)) { \
STRING_STRUCT_INIT(str), \
}.ba + \
STRING_ALIGNMENT(str) - \
1 \
) \
)
static char * foo = STRING("foo");
static char * bar = STRING("hum a few bars");
static char * baz = STRING("very, very baz");
static void dump_string(char * string) {
printf("size: %d, chars: { ", *string);
while (*++string)
printf("'%c', ", *string);
printf("'\\0' }\n");
return;
}
int main(void) {
dump_string(foo);
dump_string(bar);
dump_string(baz);
return 0;
}