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, src
et dst
ces solutions alternatives auront des valeurs de fin différentes, mais comme strcpy
il 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é?
strcpy
méthode de cette façon (pour les raisons que vous avez déjà mentionnées).int c = 0; c++;
Réponses:
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.
la source
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.-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 à .(void)x
faire 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: /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,
et
... 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
*--x
ou*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 ..." :-) :-)la source
−−i
eti++
en C. Sii
etj
é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. ] "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 :
la source
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: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-1
ou(x+=1)-1
. Toujoursx++
est plus facile à lire.la source
(++x,x-1)
(appel de fonction, peut-être?)x++
dans la plupart des cas (une exception: ilx
s'agit d'un type non signé avec un rang inférieur àint
et la valeur initiale dex
est la valeur maximale de ce type).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:
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.
la source
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.
la source
J’aime l’apéritif postfixé lorsqu’il s’agit de pointeurs (que je les supprime ou non) car
se lit plus naturellement comme "passer à la prochaine place" que l'équivalent
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
analyse comme
et pas
comme certains pourraient s'y attendre?
(Vous pensez que c'est mauvais? Voir Reading C Declarations .)
la source
La localisation et le minimalisme font partie des éléments subtils d'une bonne programmation :
Dans le même esprit,
x++
peut être considéré comme un moyen de localiser une référence à la valeur actuelle dex
tout 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 ellex
estint
ou un pointeur ou itérateur). Avec un peu d’imagination, vous pourriez comparer cela à laisser l’ancienne valeur / position dex
«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»x
peut 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.
la source
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 variablex
est ensuite incrémentée.Le préfixe
++x
ne 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:
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.la source
for
déclaration, diable, mêmewhile
(goto
après tout)?Regardons la justification originale de Kernighan & Ritchie (pages 42 et 43 de l'original):
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()
etstrcat()
) 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,
*--p
et*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.
la source
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++p
est intrinsèquement obscur.la source
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:
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.
la source
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 :
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
if
et danswhile
quelles conditions, et inversement, comment les deuxfor
boucles 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.)
la source
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.)Peut-être devriez-vous aussi penser aux produits cosmétiques.
sans doute "semble mieux" que
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:
"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 (
+0
ou quelque chose comme ça). Nous sommes habitués à voir OBJECT + QUELQUE CHOSEEst-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!la source
i++
renvoie la valeur d'origine dei
et,i+=1
et++i
renvoie la valeur d'origine dei
plus 1. Comme vous utilisez les valeurs de retour pour le contrôle du flux, cela est important.Compte à rebours de 9 à 0 inclus:
Ou l'équivalent de la boucle while.
la source
for (unsigned i = 9; i <= 9; --i)
vous