Pourquoi avons-nous incrémenté postfix?


55

Disclaimer : Je connais parfaitement la sémantique de l'incrément de préfixe et de postfix. Alors s'il te plait, ne m'explique pas comment ils fonctionnent.

En lisant les questions sur le dépassement de pile, je ne peux m'empêcher de remarquer que les programmeurs sont confus par l'opérateur d'incrément de postfix, encore et encore. La question suivante se pose: existe-t-il un cas d'utilisation où l'incrémentation postfixée offre un réel avantage en termes de qualité du code?

Permettez-moi de clarifier ma question avec un exemple. Voici une implémentation super-concise de strcpy:

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

Mais ce n'est pas exactement le code le plus auto-documenté de mon livre (et il produit deux avertissements ennuyeux sur des compilateurs sains). Alors, quel est le problème avec l'alternative suivante?

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

Nous pouvons ensuite supprimer la tâche confuse dans la condition et obtenir un code totalement sans avertissement:

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

(Oui, je sais, srcet dstces solutions alternatives auront des valeurs de fin différentes, mais comme strcpyil revient immédiatement après la boucle, cela n'a pas d'importance dans ce cas.)

Il semble que le but de l’incrément postfix soit de rendre le code le plus concis possible. Je ne vois tout simplement pas à quel point c'est quelque chose que nous devrions nous efforcer. S'il s'agissait à l'origine de performances, est-il toujours d'actualité?


7
La variante postfixe est la version la plus utilisée, aussi si vous voulez plaider contre elle, il vaudrait mieux qu'elle soit solide. Et votre premier exemple est un peu un homme de paille, car je suppose que peu de personnes coderaient réellement la strcpyméthode de cette façon (pour les raisons que vous avez déjà mentionnées).
Robert Harvey

4
Pourquoi avoir du tout?
James McNellis

31
Si nous supprimions tous les éléments du langage susceptibles de dérouter les programmeurs, nous n'aurions pas beaucoup de fonctionnalités. Le fait que quelque chose ne vous soit pas utile, ou seulement très rarement , ne signifie pas qu'il faut le couper. Si ce n'est plus pertinent, ne l'utilisez pas; la fin.
Cody Grey

4
Oui, mais alors vous ne pouvez jamais le faireint c = 0; c++;
Biff MaGriff

4
@Cody: Parce que certaines choses sont à la fois déroutantes et utiles. Il n'est pas déconcerté par l'incrémentation elle-même, il est confus quant à son utilité . Nous ne devrions pas avoir des choses inutiles, déroutantes ou non.
GManNickG

Réponses:


23

Bien que cela ait déjà eu des conséquences sur les performances, je pense que la vraie raison est d’exprimer votre intention de manière claire. La vraie question est de savoir si quelque chose while (*d++=*s++);exprime l'intention clairement ou non. OMI, oui, et je trouve les alternatives que vous proposez moins claires - mais cela peut (facilement) être le résultat de décennies d’habitude habituées à la façon dont les choses se passent. Avoir appris le langage C de K & R (car il n’y avait pratiquement aucun autre livre sur le C à ce moment-là) est probablement utile aussi.

Dans une certaine mesure, il est vrai que la nuance était beaucoup plus valorisée dans les anciens codes. Personnellement, je pense que c'était en grande partie une bonne chose - comprendre quelques lignes de code est généralement assez trivial; ce qui est difficile, c’est de comprendre de gros morceaux de code. Des tests et des études ont montré à maintes reprises que l'ajustement simultané de tout le code à l'écran est un facteur essentiel pour la compréhension du code. Au fur et à mesure que les écrans se développent, cela semble rester vrai, donc garder le code (raisonnablement) laconique reste précieux.

Bien sûr, il est possible d'aller trop loin, mais je ne pense pas que ce soit le cas. Plus précisément, je pense que nous allons trop loin lorsque la compréhension d'une seule ligne de code devient extrêmement difficile ou prend beaucoup de temps - en particulier, lorsque comprendre moins de lignes de code consomme plus d'effort que de comprendre plus de lignes. C'est fréquent dans Lisp et APL, mais cela ne semble pas (du moins pour moi) être le cas ici.

Les avertissements du compilateur m'inquiètent moins. D'après mon expérience, de nombreux compilateurs émettent des avertissements totalement ridicules de manière assez régulière. Bien que je pense certainement que les gens devraient comprendre leur code (et tous les avertissements qu’il pourrait générer), un code correct qui déclenche un avertissement dans certains compilateurs n’est pas nécessairement mauvais. Certes, les débutants ne savent pas toujours ce qu’ils peuvent ignorer en toute sécurité, mais nous ne restons pas pour toujours débutants et n’avons pas besoin de coder comme nous le sommes.


12
+1 pour signaler que la version abrégée est plus facile à lire pour certains d'entre nous. :-)

1
Si vous n'aimez pas certains des avertissements de votre compilateur (et il y en a plus que certains dont je pourrais me passer - sérieusement, je voulais taper if(x = y), je jure!), Vous devez ajuster les options d'avertissement de votre compilateur pour ne pas rater accidentellement les avertissements vous font comme.

4
@ Brooks Moses: Je suis beaucoup moins enthousiaste à ce sujet. Je n'aime pas polluer mon code pour compenser les lacunes du compilateur.
Jerry Coffin le

1
@ Jerry, @Chris: cette annotation est destinée au cas où vous pensez que l'avertissement est généralement une bonne chose, mais que vous ne voulez pas qu'il s'applique à une ligne particulière qui fait quelque chose d'inhabituel. Si vous ne voulez jamais l'avertissement et le considérez comme une lacune, utilisez-le -Wno-X, mais si vous ne voulez faire qu'une exception et que vous souhaitez que l'avertissement s'applique partout, cela est beaucoup plus compréhensible que d'utiliser un arcane hoop-jump comme Chris se réfère à .

1
@Brooks: les doubles parenthèses et le fait de (void)xfaire taire les avertissements non utilisés sont des idiomes assez connus pour faire taire les avertissements utiles par ailleurs. L'annotation ressemble un peu à pragmas, pas portable au mieux: /
Matthieu M.

32

C'est, euh, c'était une chose matérielle


Intéressant que vous remarqueriez. L'incrément de Postfix est probablement là pour un certain nombre de raisons parfaitement bonnes, mais comme beaucoup de choses en C, sa popularité peut être attribuée à l'origine de la langue.

Bien que C ait été développé sur une variété de machines anciennes et sous-alimentées, C et Unix ont tout d’abord connu un grand succès avec les modèles de gestion de la mémoire du PDP-11. C'étaient des ordinateurs relativement importants à leur époque et Unix était de loin meilleur - de manière exponentielle - que les 7 autres systèmes d'exploitation minables disponibles pour le -11.

Et, il se trouve que sur le PDP-11,

*--p

et

*p++

... ont été implémentés dans le matériel en tant que modes d'adressage. (Également *p, mais pas d'autre combinaison.) Sur les premières machines, toutes inférieures à 0,001 GHz, enregistrer une instruction ou deux dans une boucle devait être presque une attente ou une minute ou une fin. différence de déjeuner. Cela ne parle pas précisément de postincrement, mais une boucle avec postincrement de pointeur aurait pu être bien meilleure que l’indexation à l’époque.

En conséquence, les modèles de conception sont dus aux expressions C qui sont devenues des macros C mental.

C'est un peu comme déclarer des variables juste après un {... pas depuis que C89 était courant, c'est une exigence, mais c'est maintenant un modèle de code.

Mise à jour: Évidemment, la raison principale en *p++est la langue parce que c'est exactement ce que l'on veut si souvent faire. La popularité du modèle de code a été renforcée par le matériel populaire, qui est apparu et correspond au modèle déjà existant d’un langage conçu un peu avant l’arrivée du PDP-11.

Ces jours -ci, il ne fait aucune différence que le modèle que vous utilisez, ou si vous utilisez l' indexation, et nous programme habituellement à un niveau plus élevé de toute façon, mais il doit avoir compté beaucoup sur ces machines à 0.001GHz et utiliser autre chose que *--xou *x++aurait signifié que vous N'a pas "reçu" le PDP-11, et vous pourriez avoir des gens qui s'approchent de vous et vous disent "le saviez-vous ..." :-) :-)


25
Wikipedia dit: "Un (faux) mythe folklorique est que l'architecture des jeux d'instructions du PDP-11 a influencé l'utilisation idiomatique du langage de programmation C. Les modes d'adressage par incrémentation et par décrémentation du PDP-11 correspondent aux constructions −−iet i++en C. Si iet jétaient des variables de registre, une expression telle qu’elle *(−−i) = *(j++)pourrait être compilée en une seule instruction machine. [...] Dennis Ritchie contredit sans ambiguïté ce mythe populaire. [Le développement du langage C. ] "
fredoverflow

3
Huh (D'après le lien fourni dans le commentaire de FredOverflow): "Les gens pensent souvent qu'ils ont été créés pour utiliser les modes d'adresse auto-incrémentés et auto-décrémentés fournis par le DEC PDP-11 sur lequel C et Unix sont devenus populaires. C'est historiquement impossible, car il n'y avait pas de PDP-11 au moment du développement de B. Le PDP-7, toutefois, disposait de quelques cellules de mémoire `auto-incrémentées ', avec la propriété qu'une référence de mémoire indirecte incrémentait la cellule. suggéré de tels opérateurs à Thompson; la généralisation de les faire préfixe et postfix était la sienne ".

3
DMR a seulement dit que le PDP-11 n'était pas la raison pour laquelle il avait été ajouté à C. J'ai également dit cela. Ce que j’ai dit, c’est que le PDP-11 était utilisé à son avantage et est devenu un modèle de conception populaire pour cette raison. Si Wikipedia contredit cela, ils ont tout simplement tort, mais je ne le lis pas de cette façon. Il est mal formulé sur cette page.
DigitalRoss

3
@FredOverflow. Je pense que vous avez mal compris exactement ce qu'est le "mythe populaire". Le mythe est que C a été conçu pour exploiter ces 11 opérations, non pas qu’elles n’existent pas et que le modèle de code ne soit pas devenu populaire parce que tout le monde savait qu’il était mappé sur le puits 11. Au cas où ce ne serait pas clair: le PDP-11 implémenterait réellement ces deux modes dans le matériel pour presque toutes les instructions en tant que mode d'adressage, et ce fut vraiment la première machine importante sur laquelle le C a fonctionné.
DigitalRoss

2
"Cela doit avoir beaucoup d'importance sur ces machines à 0,001 GHz". Euh, je n'aime pas le dire parce que ça date de moi, mais j'avais des manuels de Motorola et d'Intel pour mes processeurs qui cherchaient la taille des instructions et le nombre de cycles qu'elles prenaient. Trouver les plus petits codes, ajouter une fonctionnalité supplémentaire à un spouleur d'impression et enregistrer quelques cycles signifiait qu'un port série pouvait suivre un protocole comme celui nouvellement inventé par MIDI. C a soulagé une partie des erreurs, en particulier à mesure que les compilateurs s’amélioraient, mais il était toujours important de savoir quel était le moyen le plus efficace d’écrire du code.
le Tin Man

14

Le préfixe et postfix --et les ++opérateurs ont été introduits dans la langue B (prédécesseur de C) par Ken Thompson - et non, ils ne sont pas inspirés par le PDP-11, qui n'existait pas à l'époque.

Citant "Le développement du langage C" de Dennis Ritchie :

Thompson est allé plus loin en inventant le ++et--opérateurs, qui augmentent ou diminuent; leur préfixe ou leur position postfixe détermine si la modification a lieu avant ou après avoir noté la valeur de l'opérande. Ils n'étaient pas dans les premières versions de B, mais sont apparus le long du chemin. Les gens croient souvent qu'ils ont été créés pour utiliser les modes d'adresse auto-incrémentés et auto-décrémentés fournis par le DEC PDP-11 sur lequel C et Unix sont devenus populaires. Ceci est historiquement impossible, puisqu'il n'y avait pas de PDP-11 au moment du développement de B. Cependant, le PDP-7 avait quelques cellules de mémoire «auto-incrémentées», avec la propriété qu'une référence de mémoire indirecte par leur intermédiaire incrémentait la cellule. Cette fonctionnalité a probablement suggéré de tels opérateurs à Thompson; la généralisation pour les faire préfixe et postfix était la sienne. En effet,++xétait plus petit que celui de x=x+1.


8
Non, je n'ai aucun lien de parenté avec Ken Thompson.
Keith Thompson

Merci pour cette référence très intéressante! Ceci confirme ma citation de K & R originale qui suggérait un objectif de compacité plutôt qu'une optimisation technique. Je ne savais pas cependant que ces opérateurs existaient déjà en B.
Christophe

@BenPen Autant que je sache, il n'y a pas de politique de fermeture des questions s'il y a un doublon sur un site différent , à condition que les deux questions correspondent à leurs sites respectifs.
Ordous

Intéressant ... Le programmeur n'est pas une question aussi étroite certainement.
BenPen

@BenPen: La réponse acceptée à la question du débordement de pile cite déjà la même source que je l’ai déjà faite (mais pas autant).
Keith Thompson

6

La raison évidente pour laquelle l'opérateur postincrément existe existe est que vous n'avez pas à écrire des expressions similaires (++x,x-1)ou omniprésentes (x+=1,x-1)ni à séparer inutilement des déclarations triviales avec des effets secondaires faciles à comprendre en plusieurs déclarations. Voici quelques exemples:

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

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

Chaque fois que vous lisez ou écrivez une chaîne dans le sens avant, postincrement, et non préincrément, est presque toujours l'opération naturelle. Je n'ai presque jamais rencontré d'utilisations réelles de la pré-incrémentation, et la seule utilisation majeure que j'ai trouvée pour la prédécrémentation est la conversion de nombres en chaînes (car les systèmes d'écriture humains écrivent des nombres à l'envers).

Edit: En fait, ce ne serait pas si moche; au lieu de (++x,x-1)vous pourriez bien sûr utiliser ++x-1ou (x+=1)-1. Toujours x++est plus facile à lire.


Vous devez faire attention à ces expressions car vous modifiez et lisez une variable entre deux points de séquence. L'ordre dans lequel ces expressions sont évaluées n'est pas garanti.

@Snowman: Lequel? Je ne vois pas de tels problèmes dans ma réponse.
R ..

si vous avez quelque chose comme (++x,x-1)(appel de fonction, peut-être?)

@Snowman: Ce n'est pas un appel de fonction. Il s'agit de l'opérateur de virgule C, qui est un point de séquence, entre parenthèses pour contrôler la priorité. Le résultat est le même que x++dans la plupart des cas (une exception: il xs'agit d'un type non signé avec un rang inférieur à intet la valeur initiale de xest la valeur maximale de ce type).
R ..

Ah, l'opérateur de virgule difficile ... quand une virgule n'est pas une virgule. La parenthèse et la rareté de l'opérateur de virgule m'ont jeté. Ça ne fait rien!

4

Le PDP-11 offrait des opérations de post-incrémentation et de pré-décrémentation dans le jeu d'instructions. Maintenant, ce ne sont pas des instructions. C'étaient des modificateurs d'instruction qui vous permettaient de spécifier que vous vouliez la valeur d'un registre avant qu'il ne soit incrémenté ou après qu'il ait été décrémenté.

Voici une étape de copie de mot dans le langage machine:

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

qui déplaçait un mot d'un endroit à un autre, indiqué par des registres, et les registres seraient prêts à le refaire.

Comme C a été construit sur et pour le PDP-11, de nombreux concepts utiles ont été introduits dans C. Le C était censé remplacer utilement le langage assembleur. Le pré-incrément et le post-décrément ont été ajoutés pour la symétrie.


Ce sont des modificateurs brillants ... Cela me donne envie d'apprendre l'assemblage du PDP-11. BTW, avez-vous une référence sur "pour la symétrie?" Cela a du sens, mais c’est de l’histoire, parfois le fait de comprendre n’était pas la clé. LOL
BenPen

2
Aussi appelé modes d'adressage . Le PDP-11 fournissait des fonctions de post-incrémentation et de pré-décrémentation qui se combinaient parfaitement pour les opérations de pile.
Erik Eidt

1
Le PDP-8 a fourni une indirection de mémoire post-incrémentée, mais aucune opération de pré-décrémentation correspondante. Avec la mémoire à noyau magnétique, la lecture d’un mot de la mémoire l’effaçait (!), Le processeur a donc utilisé une fonction de réécriture pour restaurer le mot qui vient d’être lu. L'application d'un incrément avant l'écriture a été faite assez naturellement et un mouvement de bloc plus efficace est devenu possible.
Erik Eidt

3
Désolé, le PDP-11 n'a pas inspiré ces opérateurs. Cela n'existait pas encore quand Ken Thompson les a inventés. Voir ma réponse .
Keith Thompson

3

Je doute que cela ait été vraiment nécessaire. Autant que je sache, il ne compile en quelque chose de plus compact sur la plupart des plateformes que d'utiliser le pré-incrémentation comme vous l'avez fait auparavant. À l'époque, le code était plus important que la clarté du code.

Cela ne veut pas dire que la clarté du code n’est pas (ou n’était pas) importante, mais lorsque vous tapez sur un modem à faible débit (tout ce que vous mesurez en baud est lent), chaque frappe doit y parvenir. l’ordinateur central, puis renvoyé un octet à la fois avec un seul bit utilisé comme contrôle de parité, vous ne souhaitiez pas trop taper.

C'est un peu comme le & et | opérateur ayant une priorité inférieure à ==

Il a été conçu avec les meilleures intentions (une version de && et || non-court-circuitante), mais à présent, il déroute les programmeurs tous les jours et ne changera probablement jamais.

Quoi qu'il en soit, ce n'est qu'une réponse dans la mesure où je pense qu'il n'y a pas de bonne réponse à votre question, mais je vais probablement me tromper de la part d'un codeur plus gourou que moi.

--MODIFIER--

Je dois noter que je trouve les deux incroyablement utiles. Je souligne simplement que cela ne changera pas, que cela plaise ou non.


Ce n'est même pas vraiment vrai. À aucun moment, la "concision du code" n'a été plus importante que la clarté.
Cody Grey

Eh bien, je dirais que c'était beaucoup plus un point de focalisation. Vous avez raison cependant, il est certainement jamais été plus important que la clarté du code ... pour la plupart des gens.

6
Eh bien, la définition de "terse" est "sans à-coup d'élégance: poli" ou "en quelques mots: dépourvue de superflu," les deux sont généralement une bonne chose pour l'écriture de code. "Terse" ne signifie pas "obscur, difficile à lire, et obscurci" comme beaucoup de gens le pensent.
James McNellis

@ James: Bien que vous ayez raison, je pense que la plupart des gens entendent par la deuxième définition du terme "laconique" lorsqu’on l’utilise dans un contexte de programmation: "utiliser peu de mots; sans superflu". Selon cette définition, il est certainement plus facile d'imaginer des situations où il y a un compromis entre la brièveté et l'expressivité d'un code particulier. Évidemment, la bonne chose à faire pour écrire du code est la même chose que pour parler: réduisez les choses au minimum, mais pas plus court. Valoriser la concision sur l'expressivité peut conduire à un code d'aspect obfusqué, mais cela ne devrait certainement pas.
Cody Grey

1
rembobinez 40 ans et imaginez que vous deviez adapter votre programme à une batterie qui ne contiendrait que 1000 caractères environ, ou écrire un compilateur ne peut fonctionner qu'avec 4 000 octets de mémoire vive. Il y avait des moments où la concision vs la clarté n'était même pas un problème. Vous deviez être concis, il n’existait pas d’ordinateur sur cette planète capable de stocker, d’exécuter ou de compiler votre programme non concis.
nos

3

J’aime l’apéritif postfixé lorsqu’il s’agit de pointeurs (que je les supprime ou non) car

p++

se lit plus naturellement comme "passer à la prochaine place" que l'équivalent

p += 1

qui doit sûrement confondre les débutants la première fois qu’ils le voient utilisée avec un pointeur où sizeof (* p)! = 1.

Êtes-vous sûr que vous énoncez correctement le problème de confusion? N’est-ce pas ce qui confond les débutants avec le fait que l’opérateur postfix ++ a une priorité plus élevée que l’opérateur dereference * de sorte que

*p++

analyse comme

*(p++)

et pas

(*p)++

comme certains pourraient s'y attendre?

(Vous pensez que c'est mauvais? Voir Reading C Declarations .)


2

La localisation et le minimalisme font partie des éléments subtils d'une bonne programmation :

  • mettre des variables dans un domaine d'utilisation minimal
  • en utilisant const quand l'accès en écriture n'est pas nécessaire
  • etc.

Dans le même esprit, x++peut être considéré comme un moyen de localiser une référence à la valeur actuelle de xtout en indiquant immédiatement que vous avez terminé, du moins pour le moment, avec cette valeur et que vous souhaitez passer à la suivante (valide si elle xest intou un pointeur ou itérateur). Avec un peu d’imagination, vous pourriez comparer cela à laisser l’ancienne valeur / position de x«sortir de la portée» immédiatement après qu’elle n’est plus nécessaire et passer à la nouvelle valeur. Le point précis où cette transition est possible est mis en évidence par le suffixe++ . L’implication selon laquelle il s’agit probablement de l’utilisation finale de «l’ancien» xpeut constituer un bon aperçu de l’algorithme, en aidant le programmeur à parcourir le code environnant. Bien sûr, le postfix++ peut mettre le programmeur à l’affût des utilisations de la nouvelle valeur, ce qui peut être ou ne pas être bon en fonction du moment où cette nouvelle valeur est réellement nécessaire; utile dans les circonstances.

Bien que de nombreux débutants puissent être déroutés par une fonctionnalité, il est bon de comparer ces avantages aux avantages à long terme: les débutants ne restent pas longtemps débutants.


2

Wow, beaucoup de réponses pas assez sur le point (si je peux être si audacieux), et s'il vous plaît pardonnez-moi si je souligne l'évidence - en particulier à la lumière de votre commentaire de ne pas souligner la sémantique, mais l'évident (du point de vue de Stroustrup, je suppose) ne semble pas encore avoir été posté! :)

Postfix x++produit un temporaire qui est passé "en haut" dans l'expression, tandis que la variable xest ensuite incrémentée.

Le préfixe ++xne produit pas d'objet temporaire, mais incrémente 'x' et transmet le résultat à l'expression.

La valeur est la commodité, pour ceux qui connaissent l'opérateur.

Par exemple:

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

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

Bien entendu, les résultats de cet exemple peuvent être générés à partir d'autres codes équivalents (et peut-être moins obliques).

Alors pourquoi avons-nous l'opérateur postfix? Je suppose que parce que c'est un idiome qui persiste, malgré la confusion qu'il crée évidemment. C'est une construction héritée qui était perçue autrefois comme ayant une valeur, bien que je ne sois pas sûr que la valeur perçue soit autant pour la performance que pour la commodité. Cette commodité n'a pas été perdue, mais je pense que la lisibilité a augmenté, ce qui a entraîné une remise en cause du but de l'opérateur.

Avons-nous besoin de l'opérateur postfix plus? Probablement pas. Son résultat est déroutant et crée un obstacle à la compréhensibilité. Certains bons codeurs sauront certainement immédiatement où le trouver utile, souvent dans des endroits où il a une beauté "PERL-esque" particulière. Le coût de cette beauté est la lisibilité.

Je conviens que le code explicite a des avantages sur la concision, la lisibilité, la compréhensibilité et la maintenabilité. Les bons designers et les gestionnaires le veulent.

Cependant, certains programmeurs reconnaissent qu'il y a une certaine beauté qui les encourage à produire du code dans un but purement esthétique, comme l'opérateur postfixé, code auquel ils n'auraient jamais pensé autrement ou qu'il trouvait stimulant intellectuellement. Il y a un certain quelque chose qui le rend juste plus beau, sinon plus désirable, en dépit de la rigidité.

En d'autres termes, certaines personnes trouvent while (*dst++ = *src++);tout simplement une solution plus belle, quelque chose qui vous coupe le souffle avec sa simplicité, tout autant que s'il s'agissait d'un pinceau à la toile. Le fait que vous soyez obligé de comprendre la langue pour apprécier la beauté ne fait qu'ajouter à sa magnificence.


3
+1 "certaines personnes trouvent tout (* dst ++ = * src ++); simplement une solution plus belle". Absolument. Les personnes qui ne comprennent pas le langage d'assemblage ne comprennent pas vraiment à quel point le C peut être élégant, mais cette déclaration de code est une étoile brillante.
le Tin Man

"Avons-nous besoin de l'opérateur postfix plus? Probablement pas." - Je ne peux pas m'empêcher de penser aux machines de Turing ici. Avons-nous déjà eu besoin de variables automatiques, de la fordéclaration, diable, même while( gotoaprès tout)?

@ Bernd: Ha! Imaginez des fermetures! Fermetures! Scandaleux! lol
Brian M. Hunt

Fermetures! C'est ridicule! Donne moi juste une cassette! Sérieusement, ce que je voulais dire, c'est que je ne pense pas que / besoin / d'une fonctionnalité devrait être le critère de décision principal (sauf si vous voulez concevoir, disons, Lisp). IME, j'utilise (non, besoin ) les opérateurs postfixés presque exclusivement, et il est rare que le préfixe soit nécessaire.

2

Regardons la justification originale de Kernighan & Ritchie (pages 42 et 43 de l'original):

Les aspects inhabituels sont que ++ et - peuvent être utilisés soit comme préfixe, soit comme postfixe. (...) Dans le contexte où aucune valeur n'est souhaitée (..), choisissez préfixe ou postfixe selon vos goûts. Mais il existe des situations où l’un ou l’autre est spécifiquement appelé.

Le texte continue avec quelques exemples qui utilisent des incréments dans index, dans le but explicite d'écrire du code " plus compact ". La raison derrière ces opérateurs est donc la commodité d’un code plus compact.

Les trois exemples donnés ( squeeze(), getline()et strcat()) utilisent uniquement postfix dans les expressions utilisant l'indexation. Les auteurs comparent le code avec une version plus longue qui n'utilise pas d'incréments incorporés. Cela confirme que l’accent est mis sur la compacité.

K & R met en évidence à la page 102 l'utilisation de ces opérateurs en combinaison avec la déréférencement de pointeur (par exemple, *--pet *p--). Aucun autre exemple n'est donné, mais encore une fois, ils indiquent clairement que l'avantage est la compacité.

Le préfixe est par exemple très couramment utilisé lors de la décrémentation d'un index ou d'un pointeur à partir de la fin.

 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 

1

Je vais être en désaccord avec le principe selon lequel ++p, p++est assez difficile à lire ou peu claires. Un signifie "incrémente puis lise p", l'autre signifie "lit p et puis incrémente p". Dans les deux cas, l'opérateur du code se trouve exactement là où il se trouve dans l'explication du code. Par conséquent, si vous savez ce que ++signifie, vous savez ce que le code obtenu signifie.

Vous pouvez créer du code obscurci en utilisant n'importe quelle syntaxe, mais je ne vois pas ici de cas p++/ qui ++pest intrinsèquement obscur.


1

cela provient du point de vue d'un ingénieur électricien:

de nombreux processeurs sont dotés d'opérateurs de post-incrémentation et de pré-décrémentation intégrés afin de maintenir une pile dernier entré premier sorti (LIFO).

ça pourrait être comme:

 float stack[4096];
 int stack_pointer = 0;

 ... 

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

 ...

Je ne sais pas, mais c'est la raison pour laquelle je m'attendrais à voir les opérateurs d'incrémentation préfixe et postfixe.


1

Pour souligner le point de vue de Christophe sur la compacité, je voudrais vous montrer du code de la V6. Cela fait partie du compilateur C original, à partir de 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);
}

C'est un code qui a été transmis dans le samizdat en tant qu'éducation au bon style, et pourtant nous ne l'écririons jamais de cette façon aujourd'hui. Regardez combien d'effets secondaires ont été regroupés ifet dans whilequelles conditions, et inversement, comment les deux forboucles ne possèdent pas d'expression d'incrémentation, car cela se fait dans le corps de la boucle. Regardez comment les blocs ne sont entourés d'accolades qu'en cas d'absolue nécessité.

Une partie de ceci était certainement une micro-optimisation manuelle du type que nous attendons du compilateur pour nous aujourd'hui, mais je voudrais également émettre l'hypothèse que lorsque toute l'interface de votre ordinateur est un tty de verre 80x25, vous voulez que votre code être aussi dense que possible pour pouvoir en voir plus en même temps.

(L'utilisation de tampons globaux pour tout est probablement une gueule de bois due au langage d'assemblage.)


Manipuler avec précaution: "ihash = + * sp ++;" À un moment donné, C avait non seulement l'opérateur + = mais également l'opérateur = +. Aujourd'hui, = = est un opérateur d'assignation, suivi d'un plus unaire qui ne fait rien. Cela équivaut donc à "ihash = * sp; sp + = 1;"
gnasher729

@ gnasher729 Oui, et plus tard rp->hflag =| xdflg, ce sera une erreur de syntaxe sur un compilateur moderne. "À un moment donné" est précisément V6, comme cela arrive; le passage à la +=forme s'est passé dans la V7. (Le compilateur V6 n'est accepté que=+ si je lis correctement ce code - voir le symbole () dans le même fichier.)
zwol

-1

Peut-être devriez-vous aussi penser aux produits cosmétiques.

while( i++ )

sans doute "semble mieux" que

while( i+=1 )

Pour une raison quelconque, l'opérateur post-incrémentation semble très attrayant. C'est court, c'est doux, et tout augmente de 1. Comparez-le avec le préfixe:

while( ++i )

"regarde en arrière" n'est-ce pas? Vous ne voyez jamais un signe plus PREMIER en mathématiques, sauf lorsque quelqu'un est stupide de spécifier quelque chose de positif ( +0ou quelque chose comme ça). Nous sommes habitués à voir OBJECT + QUELQUE CHOSE

Est-ce quelque chose à voir avec le classement? A, A +, A ++? Je peux vous dire que j’étais assez chiché au début que le peuple Python a retiré operator++son langage!


1
Vos exemples ne font pas la même chose. i++renvoie la valeur d'origine de iet, i+=1et ++irenvoie la valeur d'origine de iplus 1. Comme vous utilisez les valeurs de retour pour le contrôle du flux, cela est important.
David Thornley

Oui tu as raison. Mais je ne regarde que la lisibilité ici.
Bobobobo

-2

Compte à rebours de 9 à 0 inclus:

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

Ou l'équivalent de la boucle while.


1
Que diriez- for (unsigned i = 9; i <= 9; --i)vous
fredoverflow
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.