Pourquoi printf ne videra-t-il pas après l’appel à moins qu’une nouvelle ligne soit dans la chaîne de format?

Pourquoi printf ne printf t-il pas après l’appel à moins qu’une nouvelle ligne soit dans la chaîne de format? Est ce comportement POSIX? Comment pourrais-je printf immédiatement à chaque fois?

Le stream stdout est mis en mémoire tampon, il affichera donc uniquement ce qui se trouve dans le tampon une fois qu’il a atteint une nouvelle ligne (ou à quel moment). Vous avez quelques options pour imprimer immédiatement:

Imprimez dans stderr à la place en utilisant fprintf :

 fprintf(stderr, "I will be printed immediately"); 

Rincez stdout chaque fois que vous en avez besoin pour utiliser fflush :

 printf("Buffered, will be flushed"); fflush(stdout); // Will now print everything in the stdout buffer 

Edit : D’après le commentaire d’Andy Ross ci-dessous, vous pouvez également désactiver la mise en mémoire tampon sur stdout en utilisant setbuf :

 setbuf(stdout, NULL); 

Non, ce n’est pas le comportement de POSIX, c’est le comportement ISO (bon, c’est le comportement de POSIX mais seulement dans la mesure où ils sont conformes à la norme ISO).

La sortie standard est mise en tampon de ligne si elle peut être détectée comme faisant référence à un périphérique interactif, sinon elle est entièrement mise en mémoire tampon. Il y a donc des situations où printf ne printf pas, même si une nouvelle ligne doit être envoyée, par exemple:

 myprog >myfile.txt 

Cela a du sens pour l’efficacité, car si vous interagissez avec un utilisateur, ils veulent probablement voir chaque ligne. Si vous envoyez la sortie vers un fichier, il est fort probable qu’il n’y ait pas d’utilisateur à l’autre bout (bien qu’il ne soit pas impossible, ils pourraient être en train de modifier le fichier). Vous pouvez maintenant prétendre que l’utilisateur veut voir tous les caractères, mais cela pose deux problèmes.

La première est que ce n’est pas très efficace. La seconde est que le mandat initial de l’ANSI C consistait principalement à codifier les comportements existants , plutôt qu’à inventer de nouveaux comportements, et ces décisions de conception ont été sockets bien avant le lancement du processus par ANSI. Même ISO, de nos jours, fait très attention lors de la modification des règles existantes dans les normes.

En ce qui concerne la manière de gérer cela, si vous fflush (stdout) après chaque appel de sortie que vous voulez voir immédiatement, cela résoudra le problème.

Alternativement, vous pouvez utiliser setvbuf avant de travailler sur stdout , pour le régler sur sans tampon et vous n’aurez pas à vous soucier d’append toutes ces lignes fflush à votre code:

 setvbuf (stdout, NULL, _IONBF, BUFSIZ); 

Gardez simplement à l’esprit que cela peut affecter un peu les performances si vous envoyez la sortie dans un fichier. Gardez également à l’esprit que la prise en charge pour cela est définie par la mise en œuvre, non garantie par la norme.

Le paragraphe 7.19.3/3 ISO C99 est le bit pertinent:

Lorsqu’un stream est sans tampon , les caractères sont destinés à apparaître depuis la source ou à la destination dès que possible. Sinon, les caractères peuvent être accumulés et transmis vers ou depuis l’environnement hôte en tant que bloc.

Lorsqu’un stream est entièrement mis en mémoire tampon , les caractères sont destinés à être transmis à ou depuis l’environnement hôte en tant que bloc lorsqu’un tampon est rempli.

Lorsqu’un stream est mis en tampon de ligne , les caractères sont destinés à être transmis à ou depuis l’environnement hôte en tant que bloc lorsqu’un caractère de nouvelle ligne est rencontré.

De plus, les caractères sont destinés à être transmis en tant que bloc à l’environnement hôte lorsqu’un tampon est rempli, lorsqu’une entrée est demandée sur un stream sans tampon ou lorsque l’entrée est demandée sur un stream en ligne qui nécessite la transmission de caractères de l’environnement hôte. .

La prise en charge de ces caractéristiques est définie par la mise en œuvre et peut être affectée via les fonctions setbuf et setvbuf .

C’est probablement comme ça grâce à l’efficacité et parce que si vous avez plusieurs programmes qui écrivent sur un seul TTY, vous n’obtenez pas de caractères sur une ligne entrelacée. Donc, si les programmes A et B sortent, vous obtiendrez généralement:

 program A output program B output program B output program A output program B output 

Cela pue, mais c’est mieux que

 proprogrgraam m AB ououtputputt prproogrgram amB A ououtputtput program B output 

Notez qu’il n’est même pas garanti de vider sur une nouvelle ligne, vous devriez donc vider explicitement si le vidage compte pour vous.

Pour vider immédiatement l’appel fflush(stdout) ou fflush(NULL) ( NULL signifie tout vider).

Remarque: les bibliothèques d’exécution Microsoft ne prennent pas en charge la mise en mémoire tampon des lignes, donc printf("will print immediatelly to terminal") :

http://msdn.microsoft.com/en-us/library/86cebhfs.aspx

stdout est mis en mémoire tampon, ne sera donc produit qu’après l’impression d’une nouvelle ligne.

Pour obtenir une sortie immédiate, soit:

  1. Imprimer à stderr.
  2. Faites en sorte que stdout ne soit pas tamponné.

par défaut, stdout est mis en tampon de ligne, stderr n’est pas mis en mémoire tampon et le fichier est complètement mis en tampon.

Vous pouvez utiliser fprintf pour stderr, qui est non tamponné. Ou vous pouvez vider stdout quand vous le souhaitez. Ou vous pouvez définir stdout sur non tamponné.

Utilisez setbuf(stdout, NULL); pour désactiver la mise en mémoire tampon.