tandis que (1) Vs. for (;;) Y a-t-il une différence de vitesse?


154

Version longue...

Un collègue a affirmé aujourd'hui après avoir vu mon utilisation while (1)dans un script Perl for (;;)plus rapide. J'ai fait valoir qu'ils devraient être les mêmes en espérant que l'interprète optimiserait les différences. J'ai mis en place un script qui exécuterait 1 000 000 000 pour les itérations de boucle et le même nombre de boucles while et enregistrerait le temps entre les deux. Je n'ai trouvé aucune différence appréciable. Mon collègue a dit qu'un professeur lui avait dit que le while (1)faisait une comparaison 1 == 1et que for (;;)non. Nous avons répété le même test avec le 100x le nombre d'itérations avec C ++ et la différence était négligeable. C'était cependant un exemple graphique de la rapidité avec laquelle le code compilé peut être comparé à un langage de script.

Version courte...

Y a-t-il une raison de préférer un while (1)à un for (;;)si vous avez besoin d'une boucle infinie pour sortir?

Remarque: si cela ne ressort pas clairement de la question. C'était purement une discussion académique amusante entre deux amis. Je suis conscient que ce n'est pas un concept très important sur lequel tous les programmeurs devraient se débattre. Merci pour toutes les bonnes réponses que j'ai (et je suis sûr que d'autres) ont appris quelques choses de cette discussion.

Mise à jour: Le collègue susmentionné a pesé avec une réponse ci-dessous.

Cité ici au cas où il serait enterré.

Il est venu d'un programmeur d'assemblage AMD. Il a déclaré que les programmeurs C (les gens) ne réalisent pas que leur code est inefficace. Il a cependant dit aujourd'hui que les compilateurs gcc sont très bons et mettent des gens comme lui à la faillite. Il a dit par exemple, et m'a parlé du while 1contre for(;;). Je l'utilise maintenant par habitude mais gcc et surtout les interprètes feront la même opération (un saut de processeur) pour les deux ces jours, puisqu'ils sont optimisés.


4
Je suis curieux. Pourquoi faut-il une boucle infinie dans un script perl? Vous n'êtes évidemment pas en train de programmer un pilote ou une chose système ... Infini est calme long :-)
Luc M

125
Quelle boucle infinie est la plus rapide? LOL ... "Mon nouvel ordinateur est si rapide, il exécute une boucle infinie en un peu moins d'une heure ..." ;-)
Arjan Einbu

8
Est-ce un professeur de sociologie qui lui a dit cela? À l'ère moderne, le code que vous tapez n'est pas ce que l'ordinateur finit par voir.
brian d foy

5
Je pense que le temps qu'il vous a fallu pour tester cela a été beaucoup plus long que le temps potentiellement gagné en sachant lequel est le plus rapide, le cas échéant. même si vous l'amortissez sur vos deux durées de vie de programmation.
Peter Recore

4
Pourquoi le compilateur générerait-il jamais du code pour effectuer un test dont il sait qu'il n'a pas d'effets secondaires et dont le compilateur connaît déjà le résultat? Ça n'a aucun sens.
David Schwartz

Réponses:


218

En perl, ils aboutissent aux mêmes opcodes:

$ perl -MO=Concise -e 'for(;;) { print "foo\n" }'
a  <@> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 2 -e:1) v ->3
9     <2> leaveloop vK/2 ->a
3        <{> enterloop(next->8 last->9 redo->4) v ->4
-        <@> lineseq vK ->9
4           <;> nextstate(main 1 -e:1) v ->5
7           <@> print vK ->8
5              <0> pushmark s ->6
6              <$> const[PV "foo\n"] s ->7
8           <0> unstack v ->4
-e syntax OK

$ perl -MO=Concise -e 'while(1) { print "foo\n" }'
a  <@> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 2 -e:1) v ->3
9     <2> leaveloop vK/2 ->a
3        <{> enterloop(next->8 last->9 redo->4) v ->4
-        <@> lineseq vK ->9
4           <;> nextstate(main 1 -e:1) v ->5
7           <@> print vK ->8
5              <0> pushmark s ->6
6              <$> const[PV "foo\n"] s ->7
8           <0> unstack v ->4
-e syntax OK

De même dans GCC:

#include <stdio.h>

void t_while() {
    while(1)
        printf("foo\n");
}

void t_for() {
    for(;;)
        printf("foo\n");
}

    .file   "test.c"
    .section    .rodata
.LC0:
    .string "foo"
    .text
.globl t_while
    .type   t_while, @function
t_while:
.LFB2:
    pushq   %rbp
.LCFI0:
    movq    %rsp, %rbp
.LCFI1:
.L2:
    movl    $.LC0, %edi
    call    puts
    jmp .L2
.LFE2:
    .size   t_while, .-t_while
.globl t_for
    .type   t_for, @function
t_for:
.LFB3:
    pushq   %rbp
.LCFI2:
    movq    %rsp, %rbp
.LCFI3:
.L5:
    movl    $.LC0, %edi
    call    puts
    jmp .L5
.LFE3:
    .size   t_for, .-t_for
    .section    .eh_frame,"a",@progbits
.Lframe1:
    .long   .LECIE1-.LSCIE1
.LSCIE1:
    .long   0x0
    .byte   0x1
    .string "zR"
    .uleb128 0x1
    .sleb128 -8
    .byte   0x10
    .uleb128 0x1
    .byte   0x3
    .byte   0xc
    .uleb128 0x7
    .uleb128 0x8
    .byte   0x90
    .uleb128 0x1
    .align 8
.LECIE1:
.LSFDE1:
    .long   .LEFDE1-.LASFDE1
.LASFDE1:
    .long   .LASFDE1-.Lframe1
    .long   .LFB2
    .long   .LFE2-.LFB2
    .uleb128 0x0
    .byte   0x4
    .long   .LCFI0-.LFB2
    .byte   0xe
    .uleb128 0x10
    .byte   0x86
    .uleb128 0x2
    .byte   0x4
    .long   .LCFI1-.LCFI0
    .byte   0xd
    .uleb128 0x6
    .align 8
.LEFDE1:
.LSFDE3:
    .long   .LEFDE3-.LASFDE3
.LASFDE3:
    .long   .LASFDE3-.Lframe1
    .long   .LFB3
    .long   .LFE3-.LFB3
    .uleb128 0x0
    .byte   0x4
    .long   .LCFI2-.LFB3
    .byte   0xe
    .uleb128 0x10
    .byte   0x86
    .uleb128 0x2
    .byte   0x4
    .long   .LCFI3-.LCFI2
    .byte   0xd
    .uleb128 0x6
    .align 8
.LEFDE3:
    .ident  "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
    .section    .note.GNU-stack,"",@progbits

Donc, je suppose que la réponse est, ils sont les mêmes dans de nombreux compilateurs. Bien sûr, pour certains autres compilateurs, ce n'est pas nécessairement le cas, mais il y a de fortes chances que le code à l'intérieur de la boucle soit de toute façon plusieurs milliers de fois plus cher que la boucle elle-même, alors qui s'en soucie?


15
essayez avec B :: Deparse, deparsing une boucle for infinie renvoie une boucle while: P
Kent Fredric

27
"En perl, ils aboutissent aux mêmes opcodes" ... oui, mais lequel est le plus rapide? :-)
the Tin Man

6
J'adore que gcc substitue met () à printf (), car il n'y a qu'un seul argument et donc rien à formater - plus rapide et plus sûr! (gcc vérifie également le formatage des balises par rapport à la liste d'arguments de variable.)
Lee D

@the Tin Man: ils sont équivalents, car l'ordinateur fait exactement les mêmes opérations: P
BlackBear

1
@snap, ce n'est pas «complètement» incorrect, il se concentre uniquement sur les coûts d'exécution. Je ne peux pas imaginer quel genre de situation ferait en sorte que le temps d'analyse des boucles infinies soit le facteur décisif clé de la vitesse d'exécution de votre programme
bdonlan

55

En utilisant GCC, ils semblent tous deux compiler dans le même langage d'assemblage:

L2:
        jmp     L2

20
Utilisation de GCC avec l'option -S (assembler, ne pas lier)
Martin Cote

54

Il n'y a pas beaucoup de raisons de préférer l'un à l'autre. Je pense que while(1)et en particulier while(true)sont plus lisibles que for(;;), mais c'est juste ma préférence.


94
#define EVER ;; car (JAMAIS) j'ai toujours trouvé ça amusant.
Tom

19
Que diriez-vous de #define ever (;;) forever;
Martin Cote

16
Les deux semblent plus lisibles en surface, mais j'essaie de ne pas définir de nouveaux mots-clés pour que mon programmeur de maintenance (généralement moi) se gratte la tête.
Bill the Lizard

13
@Martin cela ne fonctionnera pas, car les # define ne remplacent pas dans un jeton, et foreverc'est son propre jeton.
Lily Chung

2
«J'essaie de ne pas définir de nouveaux mots-clés pour ma maintenance» - si seulement plus de gens adoptaient cette attitude, je ne serais pas accro à toutes ces manigances stupides et magiques à chaque fois que je me retournerais!
tchrist

31

Il n'y a pas de différence selon la norme. 6.5.3 / 1 a:

La déclaration for

for ( for-init-statement ; conditionopt ; expressionopt ) statement

est équivalent à

{
  for-init-statement
  while ( condition ) {
    statement
    expression ;
  }
}

Et le 6.5.3 / 2 a:

La condition et l'expression, ou les deux, peuvent être omises. Une condition manquante rend la clause while implicite équivalente à while (true).

Donc, selon la norme C ++, le code:

for (;;);

est exactement le même que:

{
  while (true) {
    ;
    ;
  }
}

4
Cela ne concerne pas du tout le code généré ou les performances. La norme définit uniquement la fonctionnalité. Bien sûr, les performances seront les mêmes.
Potatoswatter

1
Je ne crois pas que ce soit vrai qu'une différence de performance enfreigne la règle du «comme si». Si tel était le cas, les compilateurs ne seraient pas autorisés à accélérer votre code selon la règle du «comme si», par exemple en réordonnant les instructions indépendantes. Les compilateurs font exactement cela. Mais ma copie de la norme est bien en haut.
Steve Jessop le

28

Le compilateur Visual C ++ utilisé pour émettre un avertissement pour

while (1) 

(expression constante) mais pas pour

for (;;)

J'ai continué à préférer for (;;)pour cette raison, mais je ne sais pas si le compilateur le fait encore de nos jours.


l'avertissement est probablement dû au fait que vous avez utilisé while (1) au lieu de while (true)
jrharshath

16
vrai est une constante. tandis que (vrai) est une expression constante. Pour toute personne intéressée, l'avertissement C4127 est documenté ici: msdn.microsoft.com/en-us/library/6t66728h(VS.80).aspx
sean e

Oui, l'avertissement est toujours présent pour 1 et true. C'est la raison pour laquelle j'utilise toujours pour (;;)
Elviss Strazdins

26

for(;;) c'est un caractère de moins à taper si l'on veut aller dans cette direction pour optimiser les choses.


21
Bon à savoir pour le golf. Sinon, une mauvaise raison de choisir une syntaxe.
Adam Bellaire

@AdamBellaire Terseness augmente souvent la lisibilité, au-dessus d'un certain seuil de compétence.
Vector Gorgoth

20

Turbo C avec ces anciens compilateurs for(;;)entraîne alors un code plus rapide while(1).

Aujourd'hui gcc, les compilateurs Visual C (je pense que presque tous) optimisent bien, et les processeurs avec 4,7 MHz sont rarement utilisés.

À cette époque, a for( i=10; i; i-- )était plus rapide que for( i=1; i <=10; i++ ), car comparer iest égal à 0, il en résulte un saut conditionnel CPU-Zero-Flag. Et le Zero-Flag a été modifié avec la dernière opération de décrémentation ( i-- ), aucune opération cmp supplémentaire n'est nécessaire.

    call    __printf_chk
    decl    %ebx          %ebx=iterator i 
    jnz     .L2
    movl    -4(%ebp), %ebx
    leave

et ici avec for(i=1; i<=10; i++)avec cmpl supplémentaire:

    call    __printf_chk
    incl    %ebx
    cmpl    $11, %ebx
    jne     .L2
    movl    -4(%ebp), %ebx
    leave

13

Pour tous ceux qui se disputent, vous ne devriez pas utiliser des boucles while indéfinies, et suggérer des trucs stupides comme utiliser des goto ouverts (sérieusement, aïe)

while (1) {
     last if( condition1 );
     code();
     more_code(); 
     last if( condition2 ); 
     even_more_code(); 
}

Je ne peux pas vraiment être représenté d'une autre manière. Non sans créer une variable de sortie et faire de la magie noire pour la maintenir synchronisée.

Si vous avez un penchant pour la syntaxe plus goto-esque, utilisez quelque chose de sensé qui limite la portée.

flow: { 

   if ( condition ){ 
      redo flow;
   }
   if ( othercondition ){ 
       redo flow;
   }
   if ( earlyexit ){ 
       last flow;
   }
   something(); # doesn't execute when earlyexit is true 
}

En fin de compte, la vitesse n'est pas si importante

S'inquiéter de l'efficacité des différentes constructions de boucle en termes de vitesse est une perte de temps énorme. Optimisation prématurée de bout en bout. Je ne peux penser à aucune situation que j'ai jamais vue où le code de profilage a trouvé des goulots d'étranglement dans mon choix de construction de boucle.

Généralement, c'est le comment de la boucle et le quoi de la boucle.

Vous devez «optimiser» la lisibilité et la concision, et écrire ce qui est le mieux pour expliquer le problème au prochain pauvre suceur qui trouve votre code.

Si vous utilisez le truc "goto LABEL" que quelqu'un a mentionné, et que je dois utiliser votre code, soyez prêt à dormir avec un œil ouvert, surtout si vous le faites plus d'une fois, car ce genre de choses crée horriblement code spaghetti.

Ce n'est pas parce que vous pouvez créer du code spaghetti que vous devriez


9

De Stroustrup, TC ++ PL (3e édition), §6.1.1:

La notation curieuse for (;;)est la manière standard de spécifier une boucle infinie; vous pouvez le prononcer "pour toujours". [...] while (true)est une alternative.

Je préfère for (;;).


9

Si le compilateur ne fait aucune optimisation, for(;;)serait toujours plus rapide quewhile(true) . En effet, while-statement évalue la condition à chaque fois, mais for-statement est un saut inconditionnel. Mais si le compilateur optimise le flux de contrôle, il peut générer des opcodes. Vous pouvez lire le code de démontage très facilement.

PS vous pourriez écrire une boucle infinie comme ceci:

#define EVER ;;
  //...
  for (EVER) {
    //...
  }

De nos jours et à notre époque, il ne faut JAMAIS remplacer par EVS (teenage speak)! Sérieusement, j'utilise simplement for (;;) {}. J'ai lu en ligne il y a longtemps sur les différences entre les deux (quand j'étais plus jeune et que je ne savais pas vraiment qu'ils sont identiques) et je suis resté fidèle à ce que je lisais.
Bja

8

J'en ai entendu parler une fois.

Il est venu d'un programmeur d'assemblage AMD. Il a déclaré que les programmeurs C (les gens) ne réalisent pas que leur code est inefficace. Il a cependant dit aujourd'hui que les compilateurs gcc sont très bons et mettent des gens comme lui à la faillite. Il a dit par exemple, et m'a parlé du while 1contre for(;;). Je l'utilise maintenant par habitude mais gcc et surtout les interprètes feront la même opération (un saut de processeur) pour les deux ces jours, puisqu'ils sont optimisés.


5

Dans une version optimisée d'un langage compilé, il ne devrait y avoir aucune différence appréciable entre les deux. Ni l'un ni l'autre ne devraient finir par effectuer des comparaisons au moment de l'exécution, ils exécuteront simplement le code de la boucle jusqu'à ce que vous quittiez manuellement la boucle (par exemple avec a break).


3

Je suis surpris que personne ne teste correctement for (;;)par rapport while (1)à perl!

Parce que perl est un langage interprété, le temps d'exécution d'un script perl ne comprend pas seulement la phase d'exécution (qui dans ce cas est la même) mais aussi la phase d'interprétation avant l'exécution. Ces deux phases doivent être prises en compte lors de la comparaison de vitesse.

Heureusement, perl dispose d'un module Benchmark pratique que nous pouvons utiliser pour implémenter un benchmark tel que:

#!/usr/bin/perl -w

use Benchmark qw( cmpthese );

sub t_for   { eval 'die; for (;;) { }'; }
sub t_for2  { eval 'die; for (;;)  { }'; }
sub t_while { eval 'die; while (1) { }'; }

cmpthese(-60, { for => \&t_for, for2 => \&t_for2, while => \&t_while });

Notez que je teste deux versions différentes de la boucle for infinie: une qui est plus courte que la boucle while et une autre qui a un espace supplémentaire pour lui donner la même longueur que la boucle while.

Sur Ubuntu 11.04 x86_64 avec perl 5.10.1, j'obtiens les résultats suivants:

          Évaluer pendant 2 fois
pour 100588 / s - -0% -2%
pour2 100937 / s 0% - -1%
pendant 102147 / s 2% 1% -

La boucle while est clairement la gagnante sur cette plateforme.

Sur FreeBSD 8.2 x86_64 avec perl 5.14.1:

         Évaluer pendant 2 fois
pour 53453 / s - -0% -2%
pour2 53552 / s 0% - -2%
tandis que 54564 / s 2% 2% -

Alors que la boucle est le gagnant ici aussi.

Sur FreeBSD 8.2 i386 avec perl 5.14.1:

         Évaluer pendant pour for2
tandis que 24311 / s - -1% -1%
pour 24481 / s 1% - -1%
pour2 24637 / s 1% 1% -

Étonnamment, la boucle for avec un espace supplémentaire est le choix le plus rapide ici!

Ma conclusion est que la boucle while devrait être utilisée sur la plate-forme x86_64 si le programmeur optimise la vitesse. De toute évidence, une boucle for doit être utilisée lors de l'optimisation de l'espace. Mes résultats ne sont malheureusement pas concluants par rapport aux autres plateformes.


9
La conclusion est manifestement erronée. Benchmarka ses limites et ne peut pas être utilisé pour distinguer rapide de lent si les résultats sont à moins de 7% l'un de l'autre. De plus, vous n'avez pas testé la différence entre les boucles foret whilecar chaque sous-marin le fera dieavant d'atteindre les boucles eux-mêmes. Et depuis quand la quantité d'espaces blancs importait-elle pour l'interpréteur Perl? Désolé, mais l'analyse est extrêmement imparfaite.
Zaid du

2
@Zaid, merci pour vos commentaires! Pourriez-vous publier votre propre réponse afin que tout le monde puisse en tirer des leçons? :) Le dieest là dans mon code car mon intention est de tester uniquement le décalage horaire de compilation . Comme d'autres l'ont déjà souligné, l'octet-code résultant est identique, il est donc inutile de le tester. Étonnamment, la quantité d'espace blanc semble faire une petite différence dans ce cas dans mes environnements de test. Cela pourrait avoir quelque chose à voir avec la façon dont les personnages finissent par s'aligner dans la mémoire ou quelque chose de similaire ...
claquer

4
Je n'ai pas besoin de poster une réponse car ce que je dirais a déjà été mentionné par bdonlan. Et même si vous comparez les temps de compilation, les chiffres ne Benchmarksont pas concluants. Ne vous fiez pas du tout à cette différence de 1%!
Zaid du

Seulement 60 itérations? Exécutez des tests pendant environ 5 minutes afin d'obtenir des temps relatifs plus précis.
Mooing Duck

-60exécute le test pendant 60 secondes.
snap

2

En théorie, un compilateur complètement naïf pourrait stocker le littéral '1' dans le binaire (gaspillage d'espace) et vérifier si 1 == 0 à chaque itération (perte de temps et plus d'espace).

En réalité, cependant, même sans optimisations, les compilateurs réduiront toujours les deux au même niveau. Ils peuvent également émettre des avertissements car cela pourrait indiquer une erreur logique. Par exemple, l'argument de whilepourrait être défini ailleurs et vous ne réalisez pas qu'il est constant.


2

Je suis surpris que personne n'ait proposé la forme la plus directe, correspondant à l'assemblage souhaité:

forever:
     do stuff;
     goto forever;

Dose qui ne finit pas avec le même code machine que while 1 ou pour (;;) dans disons c?
Copas

1
Un autre inconvénient de cette approche: elle viole l'encapsulation en ne renfermant pas la boucle dans un bloc - ainsi toutes les variables déclarées dans la boucle sont disponibles en dehors de la boucle. (Bien sûr, vous pourriez { forever: do stuff; goto forever; })
Roy Tinker

2

while(1) est un idiome pour for(;;) reconnu par la plupart des compilateurs.

J'étais heureux de voir que perl reconnaît until(0)aussi.


Dans quelle situation jusqu'à (0) serait-il utile?
Copas

3
jusqu'à ce que () est l'opposé de while () tout comme sauf () est l'opposé de if (). Comme suggéré ailleurs dans ce fil, on pourrait écrire: faire {quelque chose ...} while (! Condition) Une alternative pourrait être jusqu'à (condition) {quelque chose}
JMD

2

Pour résumer le débat for (;;)vs while (1), il est évident que le premier était plus rapide à l'époque des anciens compilateurs non optimisateurs, c'est pourquoi vous avez tendance à le voir dans des bases de code plus anciennes telles que les commentaires de code source Lions Unix, mais à l'ère de l'optimisation badass les compilateurs ces gains sont optimisés loin du couplage qu'avec le fait que ce dernier est plus facile à comprendre que le premier, je pense qu'il serait plus préférable.


2

Je viens de tomber sur ce fil (bien que quelques années de retard).

Je pense avoir trouvé la vraie raison pour laquelle "for (;;)" est meilleur que "while (1)".

selon la "norme de codage barr 2018"

Kernighan & Ritchie long ago recommended for (;;) , which has the additional benefit
of insuring against the visually-confusing defect of a while (l); referencing a variable l’.

fondamentalement, ce n'est pas un problème de vitesse mais un problème de lisibilité. Selon la police / impression du code, le chiffre un (1) peut parfois ressembler à une lettre minuscule l.

soit 1 vs l. (dans certaines polices, elles semblent identiques).

Ainsi, while (1) peut ressembler à une boucle while dépendante de la lettre variable L.

while (true) peut également fonctionner mais dans certains cas plus anciens en C et en C incorporé, true / false ne sont pas encore définis à moins que stdbool.h ne soit inclus.


2
Je dirais que le problème dans votre code serait que vous ayez une variable nommée l, pas cela 1et qu'elle lpuisse ressembler.
mjuopperi

D'accord, je sais que la norme de codage Barr dit également ailleurs que les variables doivent comporter au moins 3 caractères, même dans les boucles for. c'est-à-dire pas de i ++ etc. dans une boucle for. J'ai tendance à penser que c'est peut-être un peu trop. En tapant, je remarque également que ce n'est pas seulement la lettre L qui ressemble à un 1. La lettre i qui est couramment utilisée comme variable peut également causer des problèmes.
Nick Law

-3

Je pense que les deux sont les mêmes en termes de performances. Mais je préférerais while (1) pour la lisibilité, mais je me demande pourquoi vous avez besoin d'une boucle infinie.


-14

Ce sont les mêmes. Il y a des questions beaucoup plus importantes à se poser.


Mon point qui était implicite mais pas explicitement fait ci-dessus, est qu'un compilateur décent générerait exactement le même code pour les deux formes de boucle. Le point le plus important est que la construction en boucle est une partie mineure du temps d'exécution de tout algorithme, et vous devez d'abord vous assurer que vous avez optimisé l'algorithme et tout ce qui y est lié. L'optimisation de votre construction de boucle doit absolument être au bas de votre liste de priorités.


22
Aucun lien ni explication. Inutile, subjectif et un peu condescendant.
cdmckay

1
enfin aucune preuve mais il a raison. Ils appellent tous les deux l'opcode pour sauter lorsqu'il est faux. (ce qui en ferait la même chose que goto mais personne n'aime les gotos)
Matthew Whited

3
J'ignorais que seules les questions importantes devaient être posées, mon erreur était ma première question.
Copas

3
Oui, j'avoue que c'est condescendant. Mais sérieusement, même sans aucune preuve, il est évident qu'ils vont être dans le même sens de la vitesse; si la question portait sur le style, il y aurait quelque chose à discuter. J'essayais de faire valoir que sur la liste des choses à craindre, cela devrait vraiment être au bas de la liste.
Mark Ransom

8
Je n'essayais pas d'être un imbécile. J'essayais de faire valoir un point. Quand je l'ai posté, j'essayais une sorte d'humour noir, et il est évident que j'ai échoué; pour cela, je m'excuse.
Mark Ransom
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.