Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 16/09/2019, à 17:49

chris7522

pre-incrementation , post-incrementation (C)

Bonjour a toutes et a tous ,
J'ai cru comprendre que pour l'affectation par exemple d'une variable :
variable=i++;
variable=++i;
Que ce genre d'instruction pouvait avoir un comportement inattendu , que l'on etait pas sur si , entre 2 sequences , l'incrementation va bien se faire au bon moment  .
Ma question est donc dois-je me prendre la tete a comprendre ce genre de truc qui me pose des problemes de comprehension :

#include <stdio.h>
int main(){
 int i,j,n;
 i=0;n=i++;
 printf("A:i=%d n=%d\n",i,n);
 i=10;n=++i;
 printf("B:i=%d n=%d\n",i,n);
 i=20;j=5;n=i++*++j;
 printf("C:i=%d j=%d n=%d\n",i,j,n);
 i=15;n=i+=3;
 printf("D:i=%d n=%d\n",i,n);
 i=3;j=5;i*= --j;
 printf("E:i=%d j=%d n=%d\n",i,n);
return 0;
}

Ce code est tiré du bouquin de C. delannoy "exercices en language C (2003)et n'est peut etre plus trop d'actualité .
Merci

Hors ligne

#2 Le 16/09/2019, à 18:04

pingouinux

Re : pre-incrementation , post-incrementation (C)

Bonjour,

j=expression(++i);

Tu incrémentes d'abord i, puis tu calcules l'expression avec la nouvelle valeur de i.

j=expression(i++);

Tu calcules l'expression avec la valeur initiale de i, puis tu incrémentes i.

Hors ligne

#3 Le 16/09/2019, à 18:19

kamaris

Re : pre-incrementation , post-incrementation (C)

chris7522 a écrit :

Que ce genre d'instruction pouvait avoir un comportement inattendu , que l'on etait pas sur si , entre 2 sequences , l'incrementation va bien se faire au bon moment  .

Que veux-tu dire par « entre deux séquences » ? Il n'y a pas, à ma connaissance, d'aspect aléatoire ici.
C'est une question de précédence d'opérateur. Par exemple, dans ton troisième cas, la pré-incrémentation de j précède la multiplication, qui précède l'affectation de n, qui précède la post-incrémentation de i.

chris7522 a écrit :

Ma question est donc dois-je me prendre la tete a comprendre ce genre de truc qui me pose des problemes de comprehension

Oui ! Accessoirement, il y a une erreur dans ton dernier printf : n est en trop dans le premier argument, et doit être remplacé par j dans le troisième.

Dernière modification par kamaris (Le 16/09/2019, à 18:51)

Hors ligne

#4 Le 16/09/2019, à 18:40

Zakhar

Re : pre-incrementation , post-incrementation (C)

Non, il n'y a aucun piège.
D'un point de vue lisibilité et maintenance, il est bon d'éviter les "astuces". Ça rend les choses horriblement compliquées quand on veut ensuite faire corriger des bugs, car il faut se remettre en tête les astuces.

Donc tu peux très bien écrire :

 n = i;
 i++;

Au lieu de

n = i++;

Ainsi tu constateras que tu n'as jamais besoin du pré-incrément, car cela devient alors :

 i++;
 n = i;

au lieu de

n = ++i;

Ou plutôt, tu peux utiliser indifféremment l'un ou l'autre puisque tu as séparé les instructions.
En deux lignes c'est bien plus clair :
=> (1) J'affecte la valeur de i à n, puis (2) j'incrémente i [eqv post incrément]
=> (1) Je commence par incrémenter i, puis (2) j'affecte la nouvelle valeur à n [eqv pré incrément]

Ainsi le code est plus lisible, et donc plus maintenable sans créer des erreurs.
Pour les compilateurs modernes, et à part si tu compiles en mode "debug", ça ne changera pas le code produit qui sera aussi efficace dans tous les cas.

Aussi, l'incrément est très utilisé dans les boucles, mais encore un fois, pré/post n'a en général pas d'importance, et alors préférer post
Exemple :

for (i = 0; i < MAX_ITER; i++)

Exception (sur une variable que l'on sait strictement positive pour passer au moins une fois dans une construction do - while):

assert( i > 0 );
do { /* something */ } while (0 != --i)

La seule "difficulté", mais elle n'est pas liée au seul incrément/décrément pré/post, c'est si tu venais à faire cela dans un programme multitâche sur une même variable (compteur global par exemple). A cause du parallélisme, cet incrément tout seul n'est pas bon, il faudrait l'entourer de verrous, ou le transformer en opération atomique.... mais ça c'est le cours de C avancé ++++ lol ... comprendre les notions de acquire et release sur la mémoire est un autre défi que le pré/post incrément !


Si "piège" il y a... c'est que l'incrément n'est pas toujours de 1.

long long ll[2], *pl;
 pl = &ll[0];
 *pl = 1LL;
 pl ++;
 *pl = 2LL;

Dans ce code, on a un pointeur sur un long long que l'on initialise à la première case du tableau ll.
On pose une valeur dans la case via le pointeur.
Ensuite on incrémente le pointeur.
Il se passe ce qu'on imagine, le pointeur va pointer sur la deuxième case du tableau, c'est à dire être incrémenté de 8 (taille d'un long long)
Ce n'est en réalité pas vraiment un "piège", parce que le code fait bien ce qu'on attend qu'il fasse. Le fait qu'un pointeur pointe sur un truc à N octets ne doit pas être la préoccupation majeure du programmeur (sauf cas particulier), et il faut laisser le compilateur se débrouiller avec ça. En plus ça entraîne des notions de padding et d'alignement qui sont hors de considération dans un programme "normal" !
C'est ce qu'on appelle l'arithmétique des pointeurs. En réalité, une addition sur un pointeur est toujours faite par rapport à la taille de l'objet pointé, et donc la quantité incrémentée/décrémentée, est multipliée par cette taille.

Dernière modification par Zakhar (Le 16/09/2019, à 19:11)


"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)

Hors ligne

#5 Le 16/09/2019, à 20:56

chris7522

Re : pre-incrementation , post-incrementation (C)

Merci a tous , je suis toujours surpris de la longueur et de la precision de vos reponses , ca fait plaisir lorsqu'on debute !

Zakhar a écrit :

On peut ecrire :
n = i;
i++; 
au lieu de : n = i++;

Cette décompositon de l'instruction m'a beaucoup aidé a comprendre .
Par contre , je bute encore sur la derniere a savoir :

i=3;j=5;i*= --j;

Pourrai tu me la decomposer de la mm facon , afin que je puisse comprendre ?
merci

Hors ligne

#6 Le 16/09/2019, à 21:37

Zakhar

Re : pre-incrementation , post-incrementation (C)

Oui

Etape 1, on écrit une instruction par ligne (séparation par ; ) c'est plus lisible.

C'est d'ailleurs une bonne pratique, sauf quand c'est totalement évident, de toujours mettre une seule instruction par ligne pour la lisibilité. Bien sûr, on l'inverse on pourrait écrire un programme C sur une seule ligne, le compilateur s'en fiche totalement, mais ça devient ridiculement illisible.
Exemple, on pourrait écrire:

#include <stdio.h>
void main(){printf("Hello, world!\n");}

Mais c'est quand même moche sans espaces, sans sauts de ligne... et pourtant ça compile et le résultat est bon. Donc même si tout est possible, "aérer" un peu permet de mieux comprendre un programme : maintenabilité, etc...

i = 3;
j = 5;
i *= --j;

Ensuite on remplace le pré-incrément et le égal-multiplié par la même chose en deux lignes. Comme c'est une "pré" opération sur j, il faut mettre l'opération sur j avant l'affectation.

i = 3;
j = 5;
j--;
i *= j;

Comme remarqué plus haut, puisqu'on commence par décrémenter j (pré-décrément), et qu'on a isolé l'opération sur une ligne seule, ça n'a aucune importance d'écrire j-- ou --j car on n'a que faire du résultat.

Et ensuite, on voit que c'est idiot d'affecter une constante à j pour ensuite la décrémenter et on peut donc optimiser. Remarquez que si on ne fait pas, le compilateur le fera de lui-même en pratique (sauf mode "debug").

i = 3;
j = 4;
i *= j;

En pratique on n'aurait jamais un tel code. On pourrait par exemple avoir, pour calculer la surface d'un rectangle d'une certaine longueur, et de la largeur spécifiée rognée de 1. Dans ce cas, laisser le w-- ou écrire w = WIDTH - 1; est une question de goûts et couleurs !

#define LENGTH  3
#define WIDTH  5

s = LENGTH;
w = WIDTH;
w--;
s *= w;

Maintenant si le *= te gène, il peut s'écrire aussi

i = 3;
j = 4;
i = i * j;

Cependant, cette écriture là qui est équivalente, va au contraire géner les "matheux", qui déduirons que la seule solution à l'équation est j = 1, par "simplification" de la multiplication à droite et à gauche du signe = !.. Donc là, c'est une question de goûts et de couleurs, tu trouveras le conseil de condenser et son inverse.
Et si c'est un vrai code, comme l'exemple du rectangle, on peut le rendre explicite ainsi :

#define LENGTH  3
#define WIDTH  5

l = LENGTH;
w = WIDTH;
w--;
surface = l * w;

Ainsi tout le monde est d'accord, les matheux aussi, puisqu'on a utilisé des variables "propres" pour la longueur, largeur et surface, et par "bricolé" en commençant par mettre la longueur dans la surface pour ensuite le multiplier par la largeur. On a aussi évité le *= qui est un condensé par toujours simple à comprendre.

Bref, écrire ce qu'on fait le plus naturellement possible, sans "astuce" (in-maintenables par la suite), et en aérant suffisamment pour la compréhension.

Dernière modification par Zakhar (Le 16/09/2019, à 22:02)


"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)

Hors ligne

#7 Le 17/09/2019, à 07:17

chris7522

Re : pre-incrementation , post-incrementation (C)

La fatigue visuelle etant passé par la , je  me suis trompé d'instruction hier soir . la derniere etait ecrite de la maniere suisvante :

i=3; j=5; n=i *= --j;
printf("D:i=%d j=%d n=%d\n", i, n);

Je dois etre un bourin et je ferai mieux de me mettre au tricot car mes resultats ne corresponde pas a la reponse . J'ai donc fait :
i=3;
j=5;
--j;   soit finalement  j=4;
n = i *= --j;   soit :
n =i = 3x 4;
pour moi , n et i valent 12 , et j = 4

Hors ligne

#8 Le 17/09/2019, à 07:32

Zakhar

Re : pre-incrementation , post-incrementation (C)

Oui,

n = i *= --j;

Séparation des affectations (toujours de droite à gauche dans l'ordre) :

i *= --j;
n = i;

Séparation du décrément (placé avant l'affectation car pré-décrément) :

j--;
i *= j;
n = i;

Et ensuite tu appliques numériquement avec les valeurs qui servent à initialiser i et j.

Dernière modification par Zakhar (Le 17/09/2019, à 07:34)


"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)

Hors ligne

#9 Le 17/09/2019, à 09:57

chris7522

Re : pre-incrementation , post-incrementation (C)

mon resultat est donc exact ?
La reponse du livre indique : 
i = 12  j = 12  n = 6

Hors ligne

#10 Le 17/09/2019, à 10:06

NicoApi73

Re : pre-incrementation , post-incrementation (C)

Bonjour,

Quel livre as tu?

Hors ligne

#11 Le 17/09/2019, à 10:26

chris7522

Re : pre-incrementation , post-incrementation (C)

claude Delannoy  "Exercices en language C" (2003)
J'ai lu apres l'achat du livre , un commentaire en particulier parlant d'un certain nombre d'erreur et d'oubli , je ne sais pas trop ce qu'il en est .

Hors ligne

#12 Le 17/09/2019, à 10:37

claudius01

Re : pre-incrementation , post-incrementation (C)

Bonjour,

Déjà, le code du post #7; à savoir:

printf("D:i=%d j=%d n=%d\n", i, n);

est incorrect pour une raison évidente (regarder notamment le warning à la compilation ;-)

Sinon, il existe maintenant des solutions de compilation / exécution en ligne comme OnlineGDB qui évitent de se prendre la tête, de parler de la même chose entre nous et surtout de lever le doute...

Hors ligne

#13 Le 17/09/2019, à 11:01

NicoApi73

Re : pre-incrementation , post-incrementation (C)

chris7522 a écrit :

claude Delannoy  "Exercices en language C" (2003)
J'ai lu apres l'achat du livre , un commentaire en particulier parlant d'un certain nombre d'erreur et d'oubli , je ne sais pas trop ce qu'il en est .

Tu trouveras toujours des détracteurs et des défenseurs. Toutes les personnes qui mettent les commentaires négatifs que j'ai pu lire n'ont pas lu le livre (ou en tout cas juste par échantillonnage), dixit ces mêmes personnes.

J'ai déjà vu quelques erreurs de typo, comme d'en autres bouquins... Aucune qui ne modifiait le sens du message.

Voici un lien qui peut compléter ce que tu es entrain de faire, en montrant un autre point de vue : https://openclassrooms.com/fr/courses/1 … ammer-en-c

Hors ligne