Por que temos incremento de postfix?

55

Disclaimer : Conheço perfeitamente a semântica do incremento de prefixo e postfix. Então, por favor, não me explique como eles funcionam.

Lendo perguntas sobre estouro de pilha, não posso deixar de notar que os programadores ficam confusos com o operador de incremento do postfix repetidas vezes. A partir disso, surge a seguinte pergunta: existe algum caso de uso em que o incremento do postfix fornece um benefício real em termos de qualidade do código?

Deixe-me esclarecer minha pergunta com um exemplo. Aqui está uma implementação super concisa de strcpy:

while (*dst++ = *src++);

Mas esse não é exatamente o código mais auto-documentado do meu livro (e produz dois avisos irritantes sobre os compiladores sãos). Então, o que há de errado com a seguinte alternativa?

while (*dst = *src)
{
    ++src;
    ++dst;
}

Podemos então nos livrar da tarefa confusa na condição e obter um código completamente livre de aviso:

while (*src != '\0')
{
    *dst = *src;
    ++src;
    ++dst;
}
*dst = '\0';

(Sim, eu sei, srce dstterá valores finais diferentes nessas soluções alternativas, mas, como strcpyretorna imediatamente após o loop, isso não importa nesse caso.)

Parece que o objetivo do incremento do postfix é tornar o código o mais conciso possível. Simplesmente deixo de ver como isso é algo pelo qual devemos nos esforçar. Se isso era originalmente sobre desempenho, ainda é relevante hoje?

fredoverflow
fonte
7
A variante postfix é a mais usada, por isso, se você quiser argumentar contra ela, é melhor que seja uma argumentação bastante forte. E o seu primeiro exemplo é um homem de palha, já que suspeito que poucas pessoas realmente codificariam o strcpymétodo dessa maneira (por razões que você já mencionou).
Robert Harvey
4
Por que qualquer um?
James McNellis
31
Se removêssemos tudo da linguagem que tivesse o potencial de confundir os programadores, não teríamos muitos recursos. O fato de algo não ser útil para você, ou apenas muito raramente útil, não significa que deve ser cortado. Se não for mais relevante, não o use; o fim.
Cody Gray
4
Sim, mas então você nunca pode fazerint c = 0; c++;
Biff MaGriff
4
@ Cody: Porque algumas coisas são confusas e úteis. Ele não está confuso com o pós-incremento em si, está confuso com a sua utilidade . Não devemos ter coisas inúteis, confusas ou não.
GManNickG

Respostas:

23

Embora tenha tido algumas implicações no desempenho, acho que o verdadeiro motivo é para expressar sua intenção de maneira limpa. A verdadeira questão é se algo while (*d++=*s++);expressa intenção claramente ou não. Na OMI, é verdade, e acho as alternativas que você oferece menos claras - mas isso pode (facilmente) ser o resultado de passar décadas se acostumando com a maneira como as coisas são feitas. Tendo aprendido C de K & R (porque não eram quase nenhum outros livros sobre C no momento), provavelmente também ajuda.

Até certo ponto, é verdade que a dispersão foi avaliada em um grau muito maior no código antigo. Pessoalmente, acho que isso foi em grande parte uma coisa boa - entender algumas linhas de código geralmente é bastante trivial; o difícil é entender grandes pedaços de código. Testes e estudos mostraram repetidamente que o ajuste de todo o código na tela ao mesmo tempo é um fator importante para a compreensão do código. À medida que as telas se expandem, isso parece permanecer verdadeiro, mantendo o código (razoavelmente) conciso permanece valioso.

Claro que é possível exagerar, mas acho que não. Especificamente, acho que está exagerando quando entender uma única linha de código se torna extremamente difícil ou demorado - especificamente, quando entender menos linhas de código consome mais esforço do que entender mais linhas. Isso é frequente no Lisp e no APL, mas não parece (pelo menos para mim) o caso aqui.

Estou menos preocupado com os avisos do compilador - é minha experiência que muitos compiladores emitem avisos absolutamente ridículos regularmente. Embora eu certamente ache que as pessoas devem entender seu código (e quaisquer avisos que ele possa produzir), um código decente que acione um aviso em algum compilador não está necessariamente errado. É certo que os iniciantes nem sempre sabem o que podem ignorar com segurança, mas não permanecemos iniciantes para sempre e não precisamos codificar como somos.

Jerry Coffin
fonte
12
+1 por apontar que a versão concisa é mais fácil de ler para alguns de nós. :-)
11
Se você não gosta de alguns dos avisos do seu compilador (e há mais do que alguns sem os quais eu poderia prescindir - sério, eu quis digitar if(x = y), eu juro!), Você deve ajustar as opções de aviso do seu compilador para não perder acidentalmente o avisos que você faz como.
4
@ Brooks Moses: Estou muito menos entusiasmado com isso. Não gosto de poluir meu código para compensar as deficiências do compilador.
Jerry Coffin
11
@ Jerry, @ Chris: Esta anotação é destinada ao caso em que você acha que o aviso geralmente é uma coisa boa , mas não deseja que ele se aplique a uma linha específica que está fazendo algo incomum. Se você nunca deseja o aviso e o considera uma falha, use-o -Wno-X, mas se você deseja apenas fazer uma exceção a ele e aplicar o aviso em qualquer outro lugar, isso é muito mais compreensível do que usar saltos arcanos como Chris se refere a .
11
@ Brooks: os parênteses duplos e o (void)xsilêncio de avisos não utilizados são expressões bastante conhecidas para silenciar avisos de outra forma úteis. A anotação parece um pouco com pragmas, não portátil na melhor das hipóteses: /
Matthieu M.
32

É uma coisa de hardware


Interessante que você notaria. O incremento do Postfix provavelmente está lá por várias razões perfeitamente boas, mas, como muitas coisas em C, sua popularidade pode ser atribuída à origem do idioma.

Embora o C tenha sido desenvolvido em uma variedade de máquinas antigas e com pouca potência, o C e o Unix foram os primeiros a se destacar nos modelos gerenciados por memória do PDP-11. Estes eram computadores relativamente importantes em seus dias e o Unix era muito melhor - exponencialmente melhor - do que os outros 7 sistemas operacionais ruins disponíveis para o -11.

E acontece que no PDP-11,

*--p

e

*p++

... foram implementados no hardware como modos de endereçamento. (Além disso *p, mas nenhuma outra combinação.) Nessas máquinas antigas, todas com menos de 0,001 GHz, salvar uma instrução ou duas em um loop quase deve ter sido uma espera por segundo ou espera por um minuto ou saída por diferença de almoço. Isso não diz exatamente o pós-incremento, mas um loop com pós-incremento de ponteiro poderia ter sido muito melhor do que indexar na época.

Como conseqüência, os padrões de design são os idiomas C que se tornaram macros C mentais.

É algo como declarar variáveis ​​logo após um {... não desde que o C89 estava atual, isso foi um requisito, mas agora é um padrão de código.

Atualização: Obviamente, o principal motivo *p++está no idioma, porque é exatamente o que se quer fazer com tanta frequência. A popularidade do padrão de código foi reforçada pelo hardware popular que surgiu e correspondia ao padrão já existente de uma linguagem projetada um pouco antes da chegada do PDP-11.

Hoje em dia, não faz diferença qual padrão você usa, ou se você usa a indexação, e geralmente programamos em um nível mais alto de qualquer maneira, mas deve ter sido muito importante nessas máquinas de 0,001 GHz e usar algo diferente *--xou *x++significaria que você não "pegou" o PDP-11, e você pode ter pessoas chegando até você e dizendo "você sabia disso ..." :-) :-)

DigitalRoss
fonte
25
A Wikipedia diz: "Um mito popular (falso) é que a arquitetura do conjunto de instruções do PDP-11 influenciou o uso idiomático da linguagem de programação C. Os modos de endereçamento de incremento e decremento do PDP-11 correspondem aos construtos −−ie i++em C. Se ie jambas eram variáveis ​​de registro, uma expressão que *(−−i) = *(j++)poderia ser compilada em uma única instrução de máquina. [...] Dennis Ritchie contradiz inequivocamente esse mito popular. [O desenvolvimento da linguagem C ] "
fredoverflow
3
Huh (No link fornecido no comentário de FredOverflow): "As pessoas geralmente acham que foram criadas para usar os modos de endereço de incremento automático e decremento automático fornecidos pelo DEC PDP-11 no qual C e Unix se tornaram populares. Isso é historicamente impossível, já que não havia PDP-11 no desenvolvimento de B. O PDP-7, no entanto, possuía algumas células de memória com 'autoincremento', com a propriedade de que uma referência indireta de memória através delas incrementava a célula. sugeriu esses operadores a Thompson; a generalização para torná-los prefixo e postfix era dele. "
3
A DMR apenas disse que o PDP-11 não foi o motivo pelo qual foi adicionado ao C. Eu disse isso também. O que eu disse foi que ele foi usado com vantagem no PDP-11 e se tornou um padrão de design popular por esse motivo exato. Se a Wikipedia contradiz isso , eles estão simplesmente errados, mas eu não leio dessa maneira. Está mal formulado nessa página em W.
DigitalRoss
3
@FredOverflow. Eu acho que você não entendeu exatamente o que é o "mito popular". O mito é que C foi projetado para explorar essas 11 operações, não que elas não existam e que o padrão de código não tenha se tornado popular porque todos sabiam que eles mapeavam o poço 11. Caso não esteja claro: o PDP-11 realmente implementa esses dois modos no hardware para quase todas as instruções como modo de endereçamento, e foi realmente a primeira máquina importante em que C foi executado.
DigitalRoss
2
"deve ter importado muito nessas máquinas de 0,001GHz". Hum, eu odeio dizer isso porque me data, mas eu tinha manuais da Motorola e Intel para minhas CPUs para procurar o tamanho das instruções e quantos ciclos eles levaram. Encontrar os códigos menores, além de poder adicionar mais um recurso a um spooler de impressão e salvar alguns ciclos, significa que uma porta serial pode acompanhar algum protocolo como o recém-inventado MIDI. C aliviou parte da nit-picking, especialmente quando os compiladores melhoraram, mas ainda era importante saber qual era a maneira mais eficiente de escrever código.
the Tin Man
14

O prefixo, o postfix --e os ++operadores foram introduzidos na linguagem B (antecessora de C) por Ken Thompson - e não, não foram inspirados pelo PDP-11, que não existia na época.

Citando "O Desenvolvimento da Linguagem C", de Dennis Ritchie :

Thompson foi um passo além ao inventar o ++e--operadores que aumentam ou diminuem; sua posição de prefixo ou postfix determina se a alteração ocorre antes ou depois da anotação do valor do operando. Eles não estavam nas versões mais antigas de B, mas apareceram ao longo do caminho. As pessoas geralmente acham que foram criadas para usar os modos de endereço de incremento automático e decremento automático fornecidos pelo DEC PDP-11, no qual o C e o Unix se tornaram populares pela primeira vez. Isso é historicamente impossível, pois não havia PDP-11 quando B foi desenvolvido. O PDP-7, no entanto, tinha algumas células de memória com 'auto incremento', com a propriedade que uma referência indireta de memória através delas incrementou a célula. Esse recurso provavelmente sugeriu esses operadores para Thompson; a generalização para torná-los prefixo e postfix era dele. De fato,++xera menor que o de x=x+1.

Keith Thompson
fonte
8
Não, eu não sou parente de Ken Thompson.
21816 Keith Thompson
Obrigado por esta referência muito interessante! Isso confirma minha citação da K&R original que sugeriu o objetivo da compactação, em vez de uma otimização técnica. Eu não sabia, porém, que estes operadores já existia em B.
Christophe
@BenPen Até onde eu sei, não há política de fechar perguntas se houver uma duplicata em um site SE diferente , desde que ambas as perguntas se encaixem nos respectivos sites.
Ordous 26/09/16
Interessante ... O programador não é uma pergunta tão próxima, certamente.
BenPen 26/09/16
@ BenPen: A resposta aceita para a pergunta Stack Overflow já cita a mesma fonte que eu (embora não tanto).
26516 Keith Thompson
6

A razão óbvia para o operador pós-incremento de existir é para que você não tem que expressões escrever como (++x,x-1)ou (x+=1,x-1)todo o lugar ou declarações triviais inutilmente separadas com fácil de entender os efeitos colaterais fora em várias instruções. aqui estão alguns exemplos:

while (*s) x+=*s++-'0';

if (x) *s++='.';

Sempre que ler ou escrever uma sequência na direção direta, pós-incremento, e não pré-incremento, é quase sempre a operação natural. Quase nunca encontrei usos no mundo real para pré-incremento, e o único uso principal que encontrei para o predecrement é converter números em strings (porque os sistemas de escrita humana escrevem números de trás para a frente).

Edit: Na verdade, não seria tão feio; em vez de (++x,x-1)você poderia, é claro, usar ++x-1ou (x+=1)-1. Ainda x++é mais legível.

R ..
fonte
Você precisa ter cuidado com essas expressões, pois está modificando e lendo uma variável entre dois pontos de sequência. A ordem em que essas expressões são avaliadas não é garantida.
@ Snowman: Qual? Não vejo esses problemas na minha resposta.
R ..
Se você tem algo parecido (++x,x-1)(chamada de função, talvez?)
@ Snowman: Isso não é uma chamada de função. É o operador de vírgula C, que é um ponto de sequência, entre parênteses para controlar a precedência. O resultado é o mesmo x++da maioria dos casos (uma exceção: xé um tipo não assinado com classificação menor que inte o valor inicial de xé o valor máximo desse tipo).
R ..
Ah, o complicado operador de vírgula ... quando uma vírgula não é vírgula. O parêntese e a raridade do operador de vírgula me assustaram. Deixa pra lá!
4

O PDP-11 ofereceu operações de pós-incremento e pré-decremento no conjunto de instruções. Agora, essas não eram instruções. Eles foram modificadores de instruções que permitiram especificar que você desejava o valor de um registro antes que ele fosse incrementado ou depois que ele fosse diminuído.

Aqui está uma etapa de cópia de texto no idioma da máquina:

movw (r1)++,(r2)++

que movia uma palavra de um local para outro, apontada pelos registros, e os registros estariam prontos para fazê-lo novamente.

Como o C foi desenvolvido no PDP-11, muitos conceitos úteis chegaram ao C. O objetivo era ser um substituto útil para a linguagem assembler. Pré-incremento e pós-decremento foram adicionados para simetria.

BobDalgleish
fonte
Modificadores brilhantes ... Isso me faz querer aprender a montagem do PDP-11. BTW, você tem uma referência em "para simetria?" Faz sentido, mas isso é história, às vezes fazendo sentido não era a chave. LOL
BenPen 23/09/16
2
Também conhecido como modos de endereçamento . O PDP-11 forneceu pós-incremento e pré-decréscimo, que se combinaram muito bem para operações de pilha.
Erik Eidt
11
O PDP-8 forneceu uma indireção de memória pós-incremento, mas nenhuma operação de pré-decremento correspondente. Com a memória do núcleo magnético, a leitura de uma palavra de memória a apagou (!); Portanto, o processador usou uma função de write-back para restaurar a palavra que acabou de ler. A aplicação de um incremento antes do write-back ocorreu naturalmente, e uma movimentação de bloco mais eficiente tornou-se possível.
Erik Eidt
3
Desculpe, o PDP-11 não inspirou esses operadores. Ainda não existia quando Ken Thompson os inventou. Veja minha resposta .
21816 Keith Thompson
3

Duvido que tenha sido realmente necessário. Tanto quanto eu sei, ele não compila em nada mais compacto na maioria das plataformas do que usar o pré-incremento como você fez, no loop. Só que, no momento em que foi criado, a dispersão do código era mais importante do que a clareza do código.

Isso não quer dizer que a clareza do código não é (ou não era) importante, mas quando você estava digitando em um modem de baixa velocidade de transmissão (qualquer coisa que você mede na velocidade de transmissão é lenta) em que cada pressionamento de tecla precisava o mainframe e, em seguida, receba eco de um byte de cada vez com um único bit usado como verificação de paridade, você não precisa digitar muito.

É como o & e | operador com precedência menor que ==

Ele foi projetado com as melhores intenções (uma versão sem curto-circuito de && e ||), mas agora confunde programadores todos os dias e provavelmente nunca mudará.

De qualquer forma, esta é apenas uma resposta, pois acho que não há uma boa resposta para sua pergunta, mas provavelmente me provarei errado por um programador de guru mais do que eu.

--EDITAR--

Devo observar que acho que ter os dois é incrivelmente útil, apenas estou apontando que não mudará se alguém gosta ou não.


fonte
Isso nem é remotamente verdade. Em nenhum momento a "dispersão do código" foi mais importante que a clareza.
Cody cinzento
Bem, eu argumentaria que era muito mais um ponto de foco. Você está certo, porém, certamente nunca foi tão importante quanto a clareza de código ... para a maioria das pessoas.
6
Bem, a definição de "concisa" é "suavemente elegante: polida" ou "usando poucas palavras: desprovida de superfluidade", as quais geralmente são boas quando se escreve código. "Conciso" não significa "obscuro, difícil de ler e ofuscado", como muitas pessoas pensam.
James McNellis
@ James: Enquanto você estiver correto, acho que a maioria das pessoas quer dizer a segunda definição de "concisa" ao usá-la em um contexto de programação: "usando poucas palavras; desprovida de superfluidade". Sob essa definição, é certamente mais fácil imaginar situações em que exista uma troca entre a brevidade e a expressividade de um pedaço de código específico. Obviamente, a coisa certa a se fazer quando se escreve código é a mesma coisa quando se fala: faça as coisas tão curtas quanto precisam, mas não mais curtas. Avaliar a discrepância sobre a expressividade pode levar a um código com aparência ofuscada, mas certamente não deveria.
Cody cinzento
11
retroceda 40 anos e imagine que você teve que ajustar seu programa naquele tambor que conterá apenas 1000 caracteres ou escrever um compilador só pode funcionar com 4 mil bytes de RAM. Houve momentos em que discrepância versus clareza nem sequer eram um problema. Você tinha que ser conciso, não existia um computador neste planeta que pudesse armazenar, executar ou compilar seu programa não conciso.
nºs
3

Eu gosto do aperitivo postfix ao lidar com ponteiros (independentemente de eu estar desreferenciando-os) porque

p++

lê mais naturalmente como "ir para o próximo local" do que o equivalente

p += 1

o que certamente deve confundir os iniciantes na primeira vez em que o veem usado com um ponteiro em que sizeof (* p)! = 1.

Tem certeza de que está afirmando o problema de confusão corretamente? Não é o que confunde os iniciantes o fato de o operador postfix ++ ter uma precedência mais alta que o operador dereference *, para que

*p++

analisa como

*(p++)

e não

(*p)++

como alguns podem esperar?

(Você acha que isso é ruim? Consulte Ler declarações C ).

Eric Giguere
fonte
2

Entre os elementos sutis da boa programação estão a localização e o minimalismo :

  • colocando variáveis ​​em um escopo mínimo de uso
  • usando const quando o acesso de gravação não é necessário
  • etc.

No mesmo espírito, x++pode ser visto como uma maneira de localizar uma referência ao valor atual x, indicando imediatamente que você - pelo menos por enquanto - terminou com esse valor e deseja ir para o próximo (válido se xé um intou um ponteiro ou iterador). Com um pouco de imaginação, você pode comparar isso a deixar o antigo valor / posição x"fora do escopo" imediatamente depois que ele não é mais necessário, e passar para o novo valor. O ponto exato em que essa transição é possível é destacado pelo postfix++ . A implicação de que esse é provavelmente o uso final do "antigo" xpode ser uma visão valiosa do algoritmo, ajudando o programador a varrer o código ao redor. Claro, o postfix++ pode colocar o programador à procura de usos do novo valor, o que pode ou não ser bom dependendo de quando esse novo valor é realmente necessário. Portanto, é um aspecto da "arte" ou "arte" da programação para determinar o que é mais importante. útil nas circunstâncias.

Embora muitos iniciantes possam ficar confusos com um recurso, vale a pena comparar isso com os benefícios de longo prazo: iniciantes não permanecem iniciantes por muito tempo.

Tony
fonte
2

Uau, muitas respostas não são muito objetivas (se é que posso ser tão ousado) e, por favor, perdoe-me se estou apontando o óbvio - principalmente à luz do seu comentário, para não apontar a semântica, mas o óbvio (da perspectiva de Stroustrup, suponho) ainda não parece ter sido publicado! :)

O Postfix x++produz um temporário que é passado 'para cima' na expressão, enquanto a variável xé posteriormente incrementada.

O prefixo ++xnão produz um objeto temporário, mas incrementa 'x' e passa o resultado para a expressão.

O valor é a conveniência, para quem conhece o operador.

Por exemplo:

int x = 1;
foo(x++); // == foo(1)
// x == 2: true

x = 1;
foo(++x); // == foo(2)
// x == 2: true

Obviamente, os resultados deste exemplo podem ser produzidos a partir de outro código equivalente (e talvez menos oblíquo).

Então, por que temos o operador postfix? Eu acho que porque é um idioma que persiste, apesar da confusão que obviamente cria. É uma construção legada que já foi percebida como tendo valor, embora não tenha certeza de que o valor percebido foi tanto para desempenho quanto para conveniência. Essa conveniência não foi perdida, mas acho que a apreciação da legibilidade aumentou, resultando no questionamento do objetivo do operador.

Precisamos mais do operador postfix? Provavelmente não. Seu resultado é confuso e cria uma barreira à compreensibilidade. Certos bons codificadores certamente saberão imediatamente onde achar que é útil, geralmente em lugares onde ela possui uma beleza peculiarmente "do tipo PERL". O custo dessa beleza é legibilidade.

Concordo que o código explícito traz benefícios sobre a discrepância, legibilidade, compreensão e manutenção. Bons designers e gerentes querem isso.

No entanto, há uma certa beleza que alguns programadores reconhecem que os incentiva a produzir código com itens puramente para simplificar a beleza - como o operador postfix - que código eles nunca teriam pensado - ou acharam intelectualmente estimulantes. Existe algo que só o torna mais bonito, se não mais desejável, apesar da expressividade.

Em outras palavras, algumas pessoas while (*dst++ = *src++);simplesmente consideram uma solução mais bonita, algo que tira o fôlego com sua simplicidade, tanto quanto se fosse um pincel na tela. O fato de você ser forçado a entender o idioma para apreciar a beleza apenas aumenta sua magnificência.

Brian M. Hunt
fonte
3
+1 "algumas pessoas consideram while (* dst ++ = * src ++); simplesmente ser uma solução mais bonita". Definitivamente. As pessoas que não entendem a linguagem assembly realmente não entendem o quão elegante C pode ser, mas essa declaração de código específica é uma estrela brilhante.
the Tin Man
"Nós precisamos mais do operador postfix? Provavelmente não." - Não posso deixar de pensar nas máquinas de Turing aqui. Alguma vez precisamos de variáveis ​​automáticas, a fordeclaração, até mesmo while( gotoafinal, afinal)?
@Bernd: Ha! Imagine fechamentos! Encerramentos! Escandaloso! lol
Brian M. caça
Encerramentos! Que ridículo. Apenas me dê uma fita! Sério, o que eu quis dizer é que eu não acho que / precisar / um recurso deve ser o principal critério de decisão (a menos que você queira criar, digamos, lisp). IME Eu uso (ou melhor, preciso ) os operadores de postfix quase que exclusivamente e raramente precisam do prefixo.
2

Vejamos a justificativa original de Kernighan & Ritchie (páginas 42 e 43 originais da K&R):

Os aspectos incomuns são que ++ e - podem ser usados ​​como prefixo ou como postfix. (...) No contexto em que nenhum valor é desejado (..) escolha o prefixo ou o postfix de acordo com o gosto. Porém, htere são situações em que um ou outro é especificamente solicitado.

O texto continua com alguns exemplos que usam incrementos no índice, com o objetivo explícito de escrever código " mais compacto ". Portanto, a razão por trás desses operadores é a conveniência de um código mais compacto.

Os três exemplos dados ( squeeze(), getline()e strcat()) usam apenas o postfix em expressões usando indexação. Os autores comparam o código com uma versão mais longa que não usa incrementos incorporados. Isso confirma que o foco está na compacidade.

K&R destacam na página 102, o uso desses operadores em combinação com a desreferenciação de ponteiros (por exemplo, *--pe *p--). Nenhum outro exemplo é dado, mas novamente, eles deixam claro que o benefício é a compacidade.

O prefixo é, por exemplo, muito comumente usado ao decrementar um índice ou um ponteiro olhando para o final.

 int i=0; 
 while (i<10) 
     doit (i++);  // calls function for 0 to 9  
                  // i is 10 at this stage
 while (--i >=0) 
     doit (i);    // calls function from 9 to 0 
Christophe
fonte
1

Vou discordar da premissa de que ++p, de p++alguma forma, é difícil de ler ou pouco claro. Um significa "incrementar p e depois ler p", o outro significa "ler p e depois incrementar p". Nos dois casos, o operador no código está exatamente onde está na explicação do código; portanto, se você sabe o que ++significa, sabe o que significa o código resultante.

Você pode criar código ofuscado usando qualquer sintaxe, mas não vejo um caso aqui p++/ que ++pseja inerentemente ofuscante.

philosodad
fonte
1

isto é do ponto de vista de um engenheiro elétrico:

existem muitos processadores que possuem operadores integrados de pós-incremento e pré-decremento com o objetivo de manter uma pilha LIFO (último a entrar, primeiro a sair).

pode ser como:

 float stack[4096];
 int stack_pointer = 0;

 ... 

 #define push_stack(arg) stack[stack_pointer++] = arg;
 #define pop_stack(arg) arg = stack[--stack_pointer];

 ...

Não sei, mas essa é a razão pela qual eu esperaria ver os operadores de incremento de prefixo e postfix.

Robert Bristow-Johnson
fonte
1

Para enfatizar o ponto de vista de Christophe sobre compacidade, quero mostrar a vocês algum código da V6. Isso faz parte do compilador C original, em http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/source/c/c00.c :

/*
 * Look up the identifier in symbuf in the symbol table.
 * If it hashes to the same spot as a keyword, try the keyword table
 * first.  An initial "." is ignored in the hash.
 * Return is a ptr to the symbol table entry.
 */
lookup()
{
    int ihash;
    register struct hshtab *rp;
    register char *sp, *np;

    ihash = 0;
    sp = symbuf;
    if (*sp=='.')
        sp++;
    while (sp<symbuf+ncps)
        ihash =+ *sp++;
    rp = &hshtab[ihash%hshsiz];
    if (rp->hflag&FKEYW)
        if (findkw())
            return(KEYW);
    while (*(np = rp->name)) {
        for (sp=symbuf; sp<symbuf+ncps;)
            if (*np++ != *sp++)
                goto no;
        csym = rp;
        return(NAME);
    no:
        if (++rp >= &hshtab[hshsiz])
            rp = hshtab;
    }
    if(++hshused >= hshsiz) {
        error("Symbol table overflow");
        exit(1);
    }
    rp->hclass = 0;
    rp->htype = 0;
    rp->hoffset = 0;
    rp->dimp = 0;
    rp->hflag =| xdflg;
    sp = symbuf;
    for (np=rp->name; sp<symbuf+ncps;)
        *np++ = *sp++;
    csym = rp;
    return(NAME);
}

Esse é um código que foi divulgado no samizdat como uma educação em bom estilo, e mesmo assim nunca o escreveríamos hoje. Veja quantos efeitos colaterais foram agrupados ife whilecondições e , inversamente, como os dois forloops não têm uma expressão de incremento porque isso é feito no corpo do loop. Veja como os blocos são envoltos em chaves apenas quando absolutamente necessário.

Parte disso foi certamente uma micro-otimização manual do tipo que esperamos que o compilador faça hoje, mas eu também proporia a hipótese de que, quando toda a sua interface com o computador for um único tty de vidro de 80x25, você deseja seu código para ser o mais denso possível, para que você possa ver mais ao mesmo tempo.

(O uso de buffers globais para tudo é provavelmente uma ressaca por cortar os dentes na linguagem assembly.)

zwol
fonte
Manuseie com cuidado: "ihash = + * sp ++;" Em algum momento, C não tinha apenas + = mas também = + operador. Hoje, o = + é um operador de atribuição, seguido de uma vantagem unária que não faz nada; portanto, é equivalente a "ihash = * sp; sp + = 1;"
gnasher729
@ gnasher729 Sim, e mais tarde possui rp->hflag =| xdflg, o que acredito ser um erro de sintaxe em um compilador moderno. "Em algum momento" é precisamente V6, por acaso; a mudança para a +=forma aconteceu na V7. (O compilador V6 única aceite =+, se eu estou lendo este código corretamente - ver o símbolo () no mesmo arquivo.)
Zwol
-1

Talvez você deva pensar também em cosméticos.

while( i++ )

indiscutivelmente "parece melhor" do que

while( i+=1 )

Por alguma razão, o operador pós-incremento parece muito atraente. É curto, doce e aumenta tudo em 1. Compare-o com o prefixo:

while( ++i )

"olha para trás", não é? Você nunca vê um sinal de adição PRIMEIRO em matemática, exceto quando alguém é tolo ao especificar algo positivo ( +0ou algo assim). Estamos acostumados a ver OBJECT + ALGO

Tem algo a ver com a classificação? A, A +, A ++? Posso dizer que fiquei muito chocado no começo que o pessoal do Python removeu operator++de sua linguagem!

bobobobo
fonte
11
Seus exemplos não fazem a mesma coisa. i++retorna o valor original i, e i+=1e ++iretornar o valor original imais 1. Como você está usando os valores de retorno para o fluxo de controle, isso é importante.
David Thornley
Sim, você está certo. Mas estou procurando apenas legibilidade aqui.
bobobobo
-2

Contando para trás 9 a 0, inclusive:

for (unsigned int i=10; i-- > 0;)  {
    cout << i << " ";
}

Ou o equivalente while loop.


fonte
11
Que tal for (unsigned i = 9; i <= 9; --i)?
Fredoverflow