struct {
char str[4];
char sc1;
char str2[3];
char sc2;
} foo __attribute__((packed));
snprintf(foo.str, 5, "%04X", data);
foo.sc1 = ';';
snprintf(foo.str2, 4, "%03X", otherdata);
foo.sc2 = ';';
Yes, I know that both snprintf() calls in the above snippet will overflow their immediate buffer. Yet this code is safe; the network protocol for which this code is written does not actually need nor expect NUL bytes; instead, it wants semicolons. I could of course use a "char foo[9]" rather than a struct as above, but I find this to be slightly more convenient than to count offsets.
However, this code does not work with glibc, because the buffer overflow detection kicks in.
Solution:
char buf[5]; snprintf(buf, 5, "%04X", data); memcpy(foo.str, buf, 4);
In other words: add a stupid and useless memcpy, because someone thinks they're smarter than me. Stupid morons.
char foo[9];
char *p = foo;
p += snprintf(p, 5, "%04X", data);
*p++ = ';';
p += snprintf(p, 4, "%03X", otherdata);
*p++ = ';';
Why packed? Why not let the compiler pad for you?
Also, this is an example. The real, actual struct is similar, but different.
union {
struct {
char str[4];
char sc1;
}
char for_sprintf[5];
}
char foo[10];snprintf(foo, 10, "%04X;%03X;", data, otherdata);
struct {
char str[5];
char str2[4];
} foo __attribute__((packed));
If you still need access to sc1 and sc2 independently, use a union or whatever.
union
{
struct {
char str[5];
char str[4];
} str;
struct {
char str[4];
char sc1;
char str2[3];
char sc2;
} chars;
} foo __attribute__((packed));
snprintf(foo.str.str, 5, "%04X", data);
foo.chars.sc1 = ';';
snprintf(foo.str.str2, 4, "%03X", otherdata);
foo.chars.sc2 = ';';
yes, not ideal, but that allows you to keep on using glibc's excellent overflow checking help.
void main() {
struct {
char str[4];
char sc1;
char str2[3];
char sc2;
} __attribute__((__packed__)) foo;
int data, otherdata;
data = 5;
otherdata = 42;
snprintf(foo.str, 5, "%04X", data);
foo.sc1 = ';';
snprintf(foo.str2, 4, "%03X", otherdata);
foo.sc2 = ';';
}
snprintf(foo, sizeof(foo), "%04X;%03X", data, otherdata);
foo[8] = ';';
#include
#include
struct {
char str[4];
char sc1;
char str2[3];
char sc2;
} foo __attribute__((packed));
int main(int argc, char** argv){
snprintf(foo.str, 5, "%04X", 1000000);
foo.sc1 = ';';
snprintf(foo.str2, 4, "%03X", 100000);
foo.sc2 = ';';
return 0;
}
test.c:9: warning: ‘packed’ attribute ignored
The code above is a reworked example of the actual code that I'm using. This is because there's no good reason to dump a whole privately-used network protocol on the public internetz, and the format of the struct wasn't essential to the discussion at hand (why else do you think I'd be using "foo" as a variable name?)
The actual code did not have the declaration and use of the struct in the same line of code, so apparently I messed up there; but it was a correct and working packed struct that hit this problem.