On a microcontroller, in order to avoid loading settings from a previous firmware build, I also store the compilation time, which is checked at loading.
The microcontroller project is build with 'mikroC PRO for ARM' from MikroElektronika.
Being easier to debug, I programmed the code with minGW on my PC and, after checking it left and right put, it into microC.
The code using that check failed to work properly. After an evening of frustrating debugging I, found sizeof("...")
yielding different values on the two platforms and causing a buffer overflow as a consequence.
But now I don't know whose fault is it.
To re-create the problem, use following code:
#define SAVEFILECHECK_COMPILE_DATE __DATE__ " " __TIME__
char strA[sizeof(SAVEFILECHECK_COMPILE_DATE)];
char strB[] = SAVEFILECHECK_COMPILE_DATE;
printf("sizeof(#def): %d\n", (int)sizeof(SAVEFILECHECK_COMPILE_DATE));
printf("sizeof(strA): %d\n", (int)sizeof(strA));
printf("sizeof(strB): %d\n", (int)sizeof(strB));
On MinGW it returns (as expected):
sizeof(#def): 21
sizeof(strA): 21
sizeof(strB): 21
However, on 'mikroC PRO for ARM' it returns:
sizeof(#def): 20
sizeof(strA): 20
sizeof(strB): 21
This difference caused a buffer overflow down the line (overwriting byte zero of a pointer – ouch).
21 is the answer I expect: 20 chars and the '\0' terminator.
Is this one of the 'it depends' things in C or is there a violation of the sizeof
operator behavior?
__DATE__
and__TIME__
are not well defined size strings. Result may depend on current time/data and preferred system locale. Just print contents of those strings to see what actually happen. – Fafnir"%zu"
,"%zd"
is forssize_t
– Cantharides\0
byte at the end of the string literals: such a bug would have been caught immediately. It’s probably simply not accounted for bysizeof
. – Protection\0
is not accounted for bysizeof
, isn't that a compiler bug? – Icebergsizeof("string")
(incorrectly) produce 6 on this microC compiler? – Sapwoodlanguage-lawyer
question (if that tag were added). The C Standard(s) that I have looked at specify that thenul
terminator is added to string literals in Translation Phase 7, immediately before they are used to initialize the corresponding arrays. However, I can't see any explicit mention in the Standard(s) about when thesizeof
operator is evaluated. I'm guessing that the string literal expression must be treated as an array but I can't find that written anywhere ... – Gillanchar strB[] = SAVEFILECHECK_COMPILE_DATE;
line. – Gillanchar strC[sizeof(SAVEFILECHECK_COMPILE_DATE)] = SAVEFILECHECK_COMPILE_DATE;
works without warning in minGW but in microC Errors with codes 388 & 325 bothToo many initializers
– Acanthopterygiansizeof(SAVEFILECHECK_COMPILE_DATE)
is being evaluated before thenul
terminator is added and the literal is converted to a static array. But I'm not 100% certain that the Standard actually specifies that is incorrect behaviour. – Gillan__DATE__
and__TIME__
are well-defined by the C standard. – Wormeatensizeof expr
, theexpr
is not evaluated (unless it's a VLA). So, for an 'unevaluated string literal', does the Standard require adding anul
suffix? – Gillansizeof
is part of the compilation, after pre-processing is done. So the string literal will be concatenated and null terminated before that, even if the operand ofsizeof
is not evaluated. – Wormeatennul
is done in Phase 7, as is the evaluation of anysizeof
operator. Where is the order of determination specified? – Gillansizeof
operator is done in translation phase 7? – Wormeatensizeof
processing, because up to that point the string literal is just a character sequence and a "sequence" isn't a meaningful operand forsizeof
. After NUL processing, there's an array, which is a valid operand forsizeof
. – Enthrall"\0"
to the string? – Antic