Considerar:
typedef union
{
int intval;
const int const_intval;
}myUnion_t;
int main(void)
{
myUnion_t x = {0};
x.intval = 5;
printf("intval = %d\n", x.intval);
printf("const_intval = %d\n", x.const_intval);
}
- O próprio UB é
myUnion_t
definido como é aqui? - Existe alguma atribuição da
intval
UB?
Nenhum dos compiladores testados emite aviso (compilados também com compiladores C++)
EDITAR
A duplicata está relacionada apenas à pergunta 2
, não à principal.
Não.
O especificador de união fornecido na questão está em conformidade com a gramática C, conforme descrito em C23 6.7.3.2/1
Não há restrição contra membros serem
const
qualificados ou haver uma mistura deconst
e não-const
membros (C23 6.7.3.2/2-6). As restrições são onde encontraríamos limitações colocadas sobre a gramática, como no uso deconst
qualificação em particular onde tipos qualificados são permitidos em geral.C permite explicitamente que
(C23 6.7.3.2/11)
const
sendo um qualificador de tipo, ele faz parte dos tipos de quaisquer membros assim qualificados. Ou seja,const int
é um tipo diferente deint
, e está entre os "quaisquer tipos completos" que são permitidos para membros de união.A única coisa a ser questionada é o nome do tipo que termina em
_t
. C não tem nada em particular a dizer sobre isso, mas se POSIX é importante para você, então você deve estar ciente de que ele reserva nomes dessa forma.Uma resposta a uma pergunta semelhante diz o mesmo, com base na versão C11 desta disposição C23 no parágrafo 6.4.7.1/7:
Eu tendo a concordar , mas não acho que a aplicação dessa provisão seja totalmente clara. As uniões consistem em membros logicamente distintos com armazenamento sobreposto (C23 6.2.5/25) e, teoricamente, uma união contém um valor para apenas um membro por vez (nota 38 do C23). Há uma questão, então, se atribuir a
x.intval
deve ser interpretado como modificarx.const_intval
. Em que nível(is) a provisão acima deve ser aplicada? De fato, a resposta pode ser diferente para C++, que faz uma distinção mais forte entre membros de união ao rejeitar o comportamento definido para trocadilhos de tipo baseados em união -- mas lá, mesmo se a atribuição tivesse comportamento definido, a segundaprintf()
chamada, em quex.const_intval
é acessada, não teria.Um caminho diferente passaria pelas especificações para operações de atribuição — especificamente que operadores de atribuição devem ter lvalues modificáveis como seus operandos esquerdos (C23 6.5.17.1/2). Então, por C23 6.3.3.1/1,
Também temos que quando um membro é acessado por meio de uma estrutura de host ou união pelo uso do operador
.
or->
, o tipo do lvalue resultante herda os qualificadores do lvalue que designa a estrutura de host ou união. Isso significa que sex
fossem declarados,const
então atribuir ax.intval
teria um comportamento indefinido. Mas isso também não nos leva à conclusão esperada, porquex.intval
herda qualificadores do tipo dex
, não modificabilidade diretamente.x
em si não é um lvalue modificável, masx.intval
parece satisfazer a definição da especificação de "lvalue modificável".Acho que é uma falha na especificação.
A interpretação mais segura é que a atribuição para
x.intval
produz UB. Duplamente sex.const_intval
for acessado subsequentemente, e triplamente sex.const_intval
tiver sido inicializado. Como uma questão prática, mesmo se você preferir interpretar a atribuição parax.intval
como tendo comportamento definido, há um risco significativo de que sua implementação C não tenha, de modo que a atribuição provoque comportamento inesperado e indesejado de seu programa na prática.