Em um microcontrolador, para evitar o carregamento de configurações de uma versão anterior do firmware, também armazeno o tempo de compilação, que é verificado no carregamento.
O projeto do microcontrolador é construído com 'mikroC PRO for ARM' da MikroElektronika .
Para ser mais fácil de depurar, programei o código com minGW no meu PC e, após verificar à esquerda e à direita, coloquei-o no microC.
O código que usou essa verificação não funcionou corretamente. Depois de uma noite de depuração frustrante, descobri sizeof("...")
que produzia valores diferentes nas duas plataformas e, como consequência, causava um estouro de buffer.
Mas agora não sei de quem é a culpa.
Para recriar o problema, use o seguinte código:
#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));
No MinGW ele retorna (conforme esperado):
sizeof(#def): 21
sizeof(strA): 21
sizeof(strB): 21
Porém, no 'mikroC PRO for ARM' ele retorna:
sizeof(#def): 20
sizeof(strA): 20
sizeof(strB): 21
Essa diferença causou um estouro de buffer na linha (sobrescrevendo o byte zero de um ponteiro – ai).
21 é a resposta que espero: 20 caracteres e o terminador '\0'.
Esta é uma das coisas que 'depende' em C ou há uma violação do sizeof
comportamento do operador?
Tudo isso é 100% padronizado. C17 6.10.8.1:
" "
(o espaço que você usou para concatenação literal de string) = 111 + 8 + 1 + 1 = 21
Quanto a
sizeof
, uma string literal é uma matriz. Sempre que você passa um array declarado parasizeof
, o array não "decai" em um ponteiro para o primeiro elemento, entãosizeof
reportará o tamanho do array em bytes. No caso de literais de string, isso inclui a terminação nula, C17 6.4.5:(A fase de tradução 6 também é mencionada, que é a fase de concatenação literal de string. Ou seja, é garantido que a concatenação literal de string aconteça antes que a terminação nula seja adicionada.)
Portanto, parece que o mikroC PRO não está em conformidade/bugado. Existem muitos compiladores de sistemas embarcados questionáveis por aí, com certeza.
O comportamento está totalmente definido no padrão C. Abaixo estão as citações relevantes do padrão publicado C99, que eram idênticas, exceto pelos números de seção na versão C90 (ANSI C) e não foram modificadas em essência na versão mais recente até e incluindo a próxima versão C23:
As macros
__DATE__
e__TIME__
são especificadas porDo exposto acima, se o tempo de tradução estiver disponível, a macro
SAVEFILECHECK_COMPILE_DATE
se expande para 3 literais de string para um total de 11+1+8 = 20 caracteres, portanto 21 bytes incluindo o terminador nulo. Se o horário da tradução não estiver disponível, datas e horários válidos definidos pela implementação deverão ser usados, portanto o comportamento deverá ser o mesmo.Daí o fato de que o argumento a
sizeof
ser feito de 3 literais de string adjacentes é irrelevante, todas as ocorrências dosizeof
operador em seus exemplos obtêm um único argumento literal de string na fase 7, entãoPortanto, todas as 3 saídas do seu exemplo devem mostrar 21 bytes. Você encontrou um bug no compilador mikroc : você deve reportá-lo e encontrar uma solução alternativa para seus projetos atuais.
Este é um bug do compilador. Literais de string, quer consistam em uma única sequência entre aspas ou em várias sequências adjacentes entre aspas, são armazenados como matrizes estáticas que sempre contêm um byte nulo final. Isso não está acontecendo aqui, onde deveria.
Isso é especificado na seção 6.4.5p6 do padrão C em relação a literais de string:
Isso significa que
sizeof(SAVEFILECHECK_COMPILE_DATE)
deve contar os caracteres da string e o byte nulo de terminação, mas o compilador, por algum motivo, não está incluindo o byte nulo.Como outros observaram, o comportamento de
sizeof
uma string literal há muito tempo foi padronizado como produzindo um valor maior que o comprimento da string representada por ela, em vez do tamanho da menor matriz de caracteres que poderia ser inicializada usando aquela string literal. Dito isto, se alguém deseja tornar o código compatível mesmo com compiladores que adotam a última interpretação, sugiro usar algo como uma expressão(1-(sizeof "")+(sizeof "stringLiteral of interst"))
que permita que o código opere corretamente com os compiladores peculiares, mas evite sacrificar a compatibilidade com os padrão.Curiosamente, neste caso,
"aa"
não decai para ponteiro, mas atua como array de caracteres. Como a matriz possui 3 elementos (incluindo terminador zero), a saída é 3.Isso define string (matriz de char)
toda vez que você compila é diferente, porque
__DATE__
e__TIME__
.Meu resultado atual é 21, mas pode mudar.
O mesmo é válido para C++.