O que exatamente significam “IB” e “UB”?

110

Já vi os termos "IB" e "UB" usados ​​várias vezes, principalmente no contexto de C ++. Eu tentei pesquisá-los, mas aparentemente essas combinações de duas letras são muito úteis. : P

Então, eu pergunto a você ... o que eles querem dizer, quando são ditos como se fossem uma coisa ruim?

cHao
fonte
5
Se você decidir reverter as edições de outra pessoa, certifique-se de que sua ortografia, pontuação e gramática sejam perfeitas. Reverter edições que são uma melhoria substancial em relação ao texto original é inútil.
Robert Harvey

Respostas:

139

IB: Comportamento definido pela implementação. O padrão deixa para o compilador / plataforma particular definir o comportamento preciso, mas requer que seja definido.

Usar o comportamento definido pela implementação pode ser útil, mas torna seu código menos portátil.

UB: Comportamento indefinido. O padrão não especifica como um programa que invoca um comportamento indefinido deve se comportar. Também conhecido como "demônios nasais" porque, teoricamente, poderia fazer os demônios saírem do seu nariz.

Usar um comportamento indefinido quase sempre é uma má ideia. Mesmo que pareça funcionar às vezes, qualquer mudança no ambiente, compilador ou plataforma pode quebrar seu código aleatoriamente.

Thomas
fonte
11
Ainda estou esperando um demônio voando pelo nariz de alguém por causa do uso de comportamento indefinido em C ++. Acho que isso vai acontecer quando os primeiros compiladores estiverem totalmente em conformidade com o novo padrão C ++.
OregonGhost
4
@OregonGhost: Acho que você está certo. Já vi isso acontecer com unicórnios algumas vezes, mas nunca com demônios.
Thomas
33
@OregonGhost - o padrão não especifica quantos chifres um demônio deve ter.
DVK
5
@Michael Burr: Eu prefiro "pegar fogo". É evidentemente catastrófico e tem pelo menos um vago ar de plausibilidade (o hardware do computador às vezes pega fogo, reconhecidamente por razões de hardware e não por falha de software no caso de qualquer sistema no qual você esteja lendo este tópico).
Steve Jessop
1
É engraçado como ninguém que respondeu a essa pergunta tem menos reputação do que 30 mil.
19

Comportamento definido pela implementação e comportamento indefinido

O padrão C ++ é muito específico sobre os efeitos de várias construções e, em particular, você deve estar sempre ciente destas categorias de problemas :

  • Comportamento indefinido significa que não há absolutamente nenhuma garantia dada. O código pode funcionar, ou pode atear fogo ao seu disco rígido ou fazer demônios voarem pelo seu nariz . No que diz respeito à linguagem C ++, absolutamente tudo pode acontecer. Em termos práticos, isso geralmente significa que você tem um bug irrecuperável. Se isso acontecer, você não pode realmente confiar em nada sobre seu aplicativo (porque um dos efeitos desse comportamento indefinido pode ter sido apenas bagunçar a memória usada pelo resto do seu aplicativo). Não é necessário que seja consistente, portanto, executar o programa duas vezes pode gerar resultados diferentes. Pode depender das fases da lua, da cor da camisa que você está vestindo ou de qualquer outra coisa.

  • Comportamento não especificado significa que o programa deve fazer algo lógico e consistente, mas não é necessário documentar isso.

  • O comportamento definido pela implementação é semelhante ao não especificado, mas também deve ser documentado pelos escritores do compilador. Um exemplo disso é o resultado de a reinterpret_cast. normalmente , ele simplesmente muda o tipo de um ponteiro, sem modificar o endereço, mas o mapeamento é realmente definido pela implementação, então um compilador poderia mapear para um endereço completamente diferente, desde que documentasse essa escolha. Outro exemplo é o tamanho de um int. O padrão C ++ não se importa se tem 2, 4 ou 8 bytes, mas deve ser documentado pelo compilador

Mas o comum para todos esses é que é melhor evitá-los. Quando possível, mantenha o comportamento 100% especificado pelo próprio padrão C ++. Dessa forma, você tem portabilidade garantida.

Freqüentemente, você também precisa confiar em algum comportamento definido pela implementação. Pode ser inevitável, mas você ainda deve prestar atenção a isso e estar ciente de que está contando com algo que pode mudar entre diferentes compiladores.

O comportamento indefinido, por outro lado, deve sempre ser evitado. Em geral, você deve apenas assumir que isso faz seu programa explodir de uma forma ou de outra.

Jalf
fonte
1
UB deve ser evitado se você se preocupa com a portabilidade . Uma implementação específica pode definir o que acontece para um comportamento indefinido específico e, em alguns casos (especialmente drivers de dispositivo e sistemas embarcados menores), você precisa usar essas coisas.
Jerry Coffin
3
@Jerry: Não, o UB deve ser evitado se for totalmente indefinido . Se a plataforma / implementação / tempo de execução / compilador oferece mais garantias, você pode confiar no comportamento e perder a portabilidade. Mas então não é mais tão indefinido ... Na maioria das vezes, porém, você não tem tais garantias, e undefined é apenas indefinido e deve ser evitado a todo custo.
jalf
"consistente" pode ser uma descrição enganosa de comportamento não especificado. Deve ser consistente com o contexto geral da operação, por exemplo, se uma expressão tiver "valor não especificado", o resultado deve ser um valor; se você armazená-lo, o valor armazenado deve ser comparado a ele mesmo, e assim por diante. Mas os resultados não especificados não precisam ser consistentes ao longo do tempo (mesma saída para a mesma entrada se você executá-la novamente), ou mesmo determinísticos.
Steve Jessop
"não é mais tão indefinido" - é exatamente tão indefinido pelo padrão , e UB é um significado abreviado indefinido pelo padrão. No seu exemplo, é definido pela implementação. Por falar nisso, você pode confiar em um comportamento que não é definido pelo padrão ou pela implementação, se você verificou o código do objeto e não planeja recompilar nunca mais ;-)
Steve Jessop
"deve, posteriormente, comparar-se a si mesmo". Hmm, a menos que seja um NaN. De qualquer forma, ele deve ter qualquer comportamento exigido de seu tipo.
Steve Jessop
8
  • IB: é o comportamento definido pela implementação - o compilador deve documentar o que ele faz. Executar uma >>operação em um valor negativo é um exemplo.

  • UB: comportamento indefinido - o compilador pode fazer o que quiser, incluindo simplesmente travar ou fornecer resultados imprevisíveis. Desreferenciar um ponteiro nulo se enquadra nesta categoria, mas também em coisas mais sutis, como aritmética de ponteiro, que está fora dos limites de um objeto de matriz.

Outro termo relacionado é 'comportamento não especificado'. Isso é algo entre comportamentos definidos e indefinidos pela implementação. para comportamento não especificado, o compilador deve fazer algo de acordo com o padrão, mas exatamente quais escolhas o padrão fornece depende do compilador e não precisa ser definido (ou mesmo consistente). Coisas como a ordem de avaliação das subexpressões se enquadram nesta categoria. O compilador pode executá-los em qualquer ordem que desejar e pode fazê-lo de maneira diferente em compilações diferentes ou mesmo em execuções diferentes da mesma compilação (improvável, mas permitido).

Michael Burr
fonte
4

A versão curta:

Comportamento definido pela implementação (IB): programado corretamente, mas indeterminado *

Comportamento indefinido (UB): programado incorretamente (ou seja, um bug !)

*) "indeterminado" no que diz respeito ao padrão de linguagem, é claro que será determinado em qualquer plataforma fixa.

Kerrek SB
fonte
Se o padrão indicar que uma ação invoca o comportamento definido pela implementação, as implementações são necessárias para especificar um comportamento consistente resultante dessa ação. Infelizmente, não existe uma categoria de comportamento para o qual uma implementação seria exigida para especificar as possíveis consequências, mas não seria exigida que qualquer consequência particular ocorresse de forma consistente.
supercat