web-dev-qa-db-fra.com

Exécution de printf () et défaut de segmentation

#include<stdio.h>

int main()
{
    char *name = "Vikram";
    printf("%s",name);
    name[1]='s';
    printf("%s",name);
    return 0;
}

Il n'y a aucune sortie imprimée sur le terminal et obtenez simplement un défaut de segmentation. Mais quand je l'exécute dans GDB, je reçois -

Program received signal SIGSEGV, Segmentation fault.
0x0000000000400525 in main () at seg2.c:7
7       name[1]='s';
(gdb) 

Cela signifie que le programme reçoit un défaut SEG sur la 7ème ligne (évidemment, je ne peux pas écrire sur un tableau de caractères constant). Alors pourquoi printf () de la ligne numéro 6 n'est pas exécuté?

22
Vikram

Cela est dû à la mise en mémoire tampon des flux de stdout. À moins que vous ne fassiez fflush(stdout) ou que vous imprimiez une nouvelle ligne "\n" la sortie peut être mise en mémoire tampon.

Dans ce cas, il s'agit d'un défaut de segmentation avant que le tampon ne soit vidé et imprimé.

Vous pouvez essayer ceci à la place:

printf("%s",name);
fflush(stdout);        //  Flush the stream.
name[1]='s';           //  Segfault here (undefined behavior)

ou:

printf("%s\n",name);   //  Flush the stream with '\n'
name[1]='s';           //  Segfault here (undefined behavior)
45
Mysticial

Vous devez d'abord terminer vos printfs par "\ n" (ou au moins le dernier). Mais cela n'est pas lié à la faute de segmentation.

Lorsque le compilateur compile votre code, il divise le binaire en plusieurs sections. Certains sont en lecture seule, tandis que d'autres sont accessibles en écriture. L'écriture dans une section en lecture seule peut provoquer une erreur de segmentation. Les littéraux de chaîne sont généralement placés dans une section en lecture seule (gcc devrait le mettre dans ".rodata"). Le nom du pointeur pointe vers cette section ro. Vous devez donc utiliser

const char *name = "Vikram";

Dans ma réponse, j'ai utilisé quelques "peut" "devrait". Le comportement dépend de votre système d'exploitation, du compilateur et des paramètres de compilation (le script de l'éditeur de liens définit les sections).

Ajouter

-Wa,-ahlms=myfile.lst

à la ligne de commande de gcc produit un fichier appelé myfile.lst avec le code assembleur généré. En haut, vous pouvez voir

    .section .rodata
.LC0:
    .string "Vikram"

Ce qui montre que la chaîne est en Vikram.

Le même code utilisant (Doit être de portée globale, sinon gcc peut le stocker sur la pile, notez qu'il s'agit d'un tableau et non d'un pointeur)

char name[] = "Vikram";

produit

    .data
    .type name, @object
    .size name, 7
name:
    .string "Vikram"

La syntaxe est un peu différente, mais voyez comment elle est maintenant dans la section .data, qui est en lecture-écriture. Au fait, cet exemple fonctionne.

10
paul

La raison pour laquelle vous obtenez une erreur de segmentation est que les littéraux de chaîne C sont en lecture seule conformément à la norme C et que vous essayez d'écrire "sur" sur le deuxième élément du tableau littéral "Vikram".

La raison pour laquelle vous n'obtenez aucune sortie est que votre programme met en mémoire tampon sa sortie et se bloque avant d'avoir la possibilité de vider sa mémoire tampon. Le but de la bibliothèque stdio, en plus de fournir des fonctions de formatage conviviales comme printf (3), est de réduire la surcharge des opérations d'E/S en tamponnant les données dans des tampons en mémoire et en ne vidant la sortie que si nécessaire, et en n'effectuant des entrées que de temps en temps. au lieu de constamment. L'entrée et la sortie réelles ne se produiront pas, dans le cas général, au moment où vous appelez la fonction stdio, mais uniquement lorsque le tampon de sortie est plein (ou que le tampon d'entrée est vide).

Les choses sont légèrement différentes si un objet FILE a été défini de manière à ce qu'il vienne constamment (comme stderr), mais en général, c'est l'essentiel.

Si vous déboguez, il est préférable de fprintf vers stderr pour vous assurer que vos impressions de débogage seront vidées avant un plantage.

5
Perry

Par défaut, lorsque stdout est connecté à un terminal, le flux est mis en mémoire tampon de ligne. En pratique, dans votre exemple, l'absence de '\n' (ou d'un vidage de flux explicite) est la raison pour laquelle les caractères ne sont pas imprimés.

Mais en théorie, le comportement indéfini n'est pas limité (à partir de la norme "comportement [...] pour lequel la présente Norme internationale n'impose aucune exigence") et la faute de segmentation peut se produire avant même que le comportement indéfini ne se produise, par exemple avant le premier printf appel!

1
ouah