O que faz o ??!??! operador fazer em C?

1990

Eu vi uma linha de C que fica assim:

!ErrorHasOccured() ??!??! HandleError();

Compilou corretamente e parece funcionar bem. Parece que está verificando se ocorreu um erro e, se houver, ele lida com isso. Mas não tenho muita certeza do que está realmente fazendo ou como está fazendo. Parece que o programador está tentando expressar seus sentimentos sobre erros.

Eu nunca vi o ??!??!antes em nenhuma linguagem de programação e não consigo encontrar documentação para isso em nenhum lugar. (O Google não ajuda em termos de pesquisa como ??!??!). O que faz e como o exemplo de código funciona?

Peter Olson
fonte
44
@ PeterOlson, como você espera !ErrorHasOccurred() ??!???! HandleError();compilar? É isso ??! ??? !. Prova o ponto?
um CVn
31
Eu sugiro que você leia sobre o código limpo. ErrorHasOccured () deve ser refatorado para ErrorHasNotOccured (), limpando assim o ponto de exclamação ... quem tem tempo para entender todos esses operadores ??!
KadekM
17
Eu prefiro a ErrorHasOccured() && HandleError()mim mesma. É também assim que Lua faz.
Hugo Zink
76
@KadekM, mover a negação para o nome da função não resulta em código limpo, mas o oposto.
marcelm
14
Uma observação para qualquer um que acabou aqui depois de uma luta mortal com o mecanismo de pesquisa: o SymbolHound pode ajudar com pesquisas simbólicas.
Jakob

Respostas:

1579

??!é um trigrafo que se traduz em |. Então diz:

!ErrorHasOccured() || HandleError();

que, devido a curto-circuito, é equivalente a:

if (ErrorHasOccured())
    HandleError();

Guru da semana (lida com C ++, mas relevante aqui), onde eu peguei isso.

Possível origem de trigramas ou como @DwB aponta nos comentários, é mais provável que o EBCDIC seja difícil (novamente). Essa discussão no quadro IBM developerWorks parece apoiar essa teoria.

Da ISO / IEC 9899: 1999 §5.2.1.1, nota de rodapé 12 (h / t @ Random832):

As sequências trigraficas permitem a entrada de caracteres que não são definidos no conjunto de códigos invariantes, conforme descrito na ISO / IEC 646, que é um subconjunto do conjunto de códigos ASCII dos EUA de sete bits.

user786653
fonte
378
Originalmente, eram necessários trígrafos, caso o teclado não tivesse, por exemplo, um '|' símbolo. Aqui está o programador sendo deliberadamente irritante ou algum recurso "bizarro" do editor
Martin Beckett
36
Sim, é equivalente a if (ErrorHasOccured()) HandleError(). Felizmente, você normalmente só encontra esse idioma no código perl.
user786653
22
Não é necessariamente EBCDIC - o conjunto de caracteres que requer trigramas corresponde quase exatamente ao conjunto de caracteres que não são invariantes na ISO-646 (ou seja, os antigos padrões 'national ascii').
Random832
52
Uma alternativa perfeitamente legível seria ErrorHasOccurred() && HandleError();Ou seja, se você está acostumado a fazer shell scripts. :)
Yam Marcovic 24/10
18
Leia-o como "No ErrorHasOcurred ou você deve HandleError", @SparkyRobinson.
Omar Antolín-Camarena
453

Bem, por que isso existe em geral é provavelmente diferente do que existe no seu exemplo.

Tudo começou meio século atrás com o redirecionamento dos terminais de comunicação impressa como interfaces de usuário do computador. Na era inicial do Unix e C, era o teletipo ASR-33.

Esse dispositivo era lento (10 cps), barulhento e feio, e sua exibição do conjunto de caracteres ASCII terminava em 0x5f; portanto, tinha (observe atentamente a foto) nenhuma das teclas:

{ | } ~ 

Os trigramas foram definidos para corrigir um problema específico. A idéia era que os programas em C pudessem usar o subconjunto ASCII encontrado no ASR-33 e em outros ambientes sem os altos valores ASCII.

Seu exemplo é na verdade dois de ??!, cada significado |, então o resultado é ||.

No entanto, as pessoas que escreviam o código C quase por definição possuíam equipamentos modernos, 1 pelo que acho: alguém se exibindo ou se divertindo, deixando uma espécie de ovo de Páscoa no código para você encontrar.

Com certeza funcionou, levou a uma pergunta SO muito popular.

Teletipo ASR-33

                                            Teletipo ASR-33


1. Por esse motivo, os trigrafs foram inventados pelo comitê ANSI, que se reuniu pela primeira vez após C se tornar um grande sucesso, de modo que nenhum código ou codificador C original os teria usado.

DigitalRoss
fonte
18
Não é o único caso de caracteres ausentes, no teclado e no conjunto de caracteres. É provável que o Commodore 64 seja mais familiar para muitas pessoas com mais de 30 anos ou mais - os conjuntos de caracteres exibidos não possuem chaves (e provavelmente a barra e o til) - nesse caso, porque o "ASCII" não era ASCII . No ECMA-6 (quase sempre chamado ASCII, mas não US-ASCII), havia 18 códigos específicos de região, mas não sei quais eram. A única coisa que posso dizer com certeza - no britânico "ASCII", #foi substituída por £. Em outras regiões, talvez "ASCII" não tenha chaves etc.
Steve314 20/10
7
O conjunto semelhante de caracteres ATASCII para computadores Atari de 8 bits também não possuía {}, bem como ~ e `.
dan04
42
Veja estes dois artigos da Wikipedia. Tenho idade suficiente para me lembrar da era dos charsets nacionais de 7 bits (embora tenha certeza de que eles ainda permaneçam em alguns cantos escuros e não varridos), e o livro que aprendi C primeiro achou necessário advertir sobre o possibilidade de if (x || y) { a[i] = '\0'; }parecer com if (x öö y) ä aÄiÅ = 'Ö0'; åo conjunto de caracteres errado.
Ilmari Karonen
9
Outra nota histórica interessante é que o Unix (que foi a grande plataforma em que C entrou) pode ter sido o primeiro sistema de qualquer significado (e talvez o primeiro geral) a padronizar os valores alfabéticos para letras minúsculas e não maiúsculas. Embora eu não tenha visto com meus próprios olhos muitos sistemas contemporâneos, acho que isso foi um verdadeiro sinal de sofisticação. Além de ser realmente o único sistema operacional decente, o Unix também converteu sua letra maiúscula para menor, em vez de vice-versa. Aqueles caras eram muito legais.
DigitalRoss
16
História engraçada, tenho que contar ... o compilador XL Fortran da estação de trabalho IBM RS / 6000 foi desenvolvido a partir do compilador XL C. Nos primeiros lançamentos, eles foram acidentalmente deixados no processamento do trigraph, portanto houve algumas seqüências legítimas de caracteres do Fortran (em uma seqüência literal, IIRC) que foram mal interpretadas como trigraphs C, levando a alguns erros interessantes!
21413 Phil
166

É um C trigraph . ??!é |, assim ??!??!como o operador||

Joel Falcou
fonte
5
O trigraph vem de um período em que algum teclado não tinha todas as teclas que possui agora. Também ajuda quando algum editor de texto reserva caracteres especiais para coisas especiais. É principalmente uma relíquia do passado e um facilitador quizz;)
Joel Falcou
5
Porque alguns teclados aparentemente não possuem "|" portanto, algumas pessoas não têm outra opção a não ser pressionar o teclado repetidamente até que um trigrafo ocorra, fornecendo os símbolos de que precisam.
Owl
E depois há o <iso646.h>arquivo de cabeçalho.
precisa
149

Como já foi dito, ??!??!são essencialmente dois trigramas ( ??!e ??!novamente) misturados que são substituídos - traduzidos para ||, ou seja, o OR lógico , pelo pré-processador.

A tabela a seguir, contendo todos os trigramas, deve ajudar a desambiguar combinações alternativas de trigramas:

Trigraph   Replaces

??(        [
??)        ]
??<        {
??>        }
??/        \
??'        ^
??=        #
??!        |
??-        ~

Fonte: C: Um Manual de Referência 5ª Edição

Assim, um trigrama que pareça ??(??)eventualmente será mapeado para [], ??(??)??(??)será substituído [][]e assim por diante, você entenderá.

Como os trigrafs são substituídos durante o pré-processamento, você pode usar cpppara obter uma visão da saída, usando um trigr.cprograma bobo :

void main(){ const char *s = "??!??!"; } 

e processando-o com:

cpp -trigraphs trigr.c 

Você obterá uma saída do console de

void main(){ const char *s = "||"; }

Como você pode perceber, a opção -trigraphsdeve ser especificada ou então cppemitirá um aviso; isso indica como os trigramas são coisa do passado e não têm valor moderno além de confundir pessoas que possam esbarrar neles .


Quanto à justificativa por trás da introdução dos trigramas, é melhor compreendida ao examinar a seção de histórico da ISO / IEC 646 :

A ISO / IEC 646 e seu antecessor ASCII (ANSI X3.4) endossaram amplamente as práticas existentes em relação a codificações de caracteres no setor de telecomunicações.

Como o ASCII não forneceu um número de caracteres necessários para outros idiomas além do inglês, foram feitas várias variantes nacionais que substituíram alguns caracteres menos utilizados pelos necessários .

(ênfase minha)

Portanto, em essência, alguns caracteres necessários (aqueles para os quais existe um trigrama) foram substituídos em certas variantes nacionais. Isso leva à representação alternativa usando trigramas compostos por caracteres que outras variantes ainda possuíam.

Dimitris Fasarakis Hilliard
fonte