2009-06-10 4 views
3

Comme beaucoup de jeunes programmeurs, j'ai appris l'utilité d'insérer de nombreuses instructions d'impression-console de "here1", "here2", et ainsi de suite à différents points du code pour comprendre quand mes programmes vont mal. Cette technique de débogage de force brute m'a sauvé beaucoup, plusieurs fois tout au long de mes études CS. Cependant, quand j'ai commencé à programmer en C, je suis tombé sur un problème intéressant. Si je devais essayer de courirC Programmation: erreurs de segmentation, printf et dépendances

void* test; 

printf("hello world"); 
test[5] = 234; 

Bien sûr, je reçois une erreur de segmentation pour ne pas malloc'ing mémoire pour testChar. Cependant, vous penseriez logiquement que "bonjour monde" serait imprimé avant que la faute seg, car c'est le flux du code, mais dans mon expérience, il est toujours le cas que la faute seg se produit d'abord, et "salut monde "n'est jamais imprimé sur la console. (Je n'ai pas pu tester cet exemple exact, mais j'ai souvent rencontré ce genre de situation en utilisant gcc sur une machine linux.) Je suppose que cela a à voir avec le fait que le compilateur réorganise certaines choses et/ou printf en utilisant une sorte de tampon qui est vidé de façon asynchrone et qui n'est donc pas immédiat. C'est tout à fait de la spéculation de ma part parce que je ne sais vraiment pas pourquoi cela arrive. Dans toute autre langue que j'ai utilisée, quel que soit le problème causé par la ligne "testChar = ...", le "hello world" serait toujours imprimé, et ainsi je pourrais déterminer où est le problème.

Ma question est la suivante: pourquoi cela se produit-il lorsque je programme C? Pourquoi le monde des salutations n'est-il pas imprimé en premier? Et deuxièmement, y at-il une meilleure technique de débogage de programmation C que celle qui accomplit la même chose de base? Comme dans, un moyen facile/intuitif de trouver la ligne de code qui pose problème?

Editer: J'ai donné un exemple de travail par accident haha. Ce que j'ai maintenant devrait provoquer une erreur de segmentation. C'est drôle comment d'habitude quand je ne veux pas un je veux en obtenir un, et maintenant quand j'en ai voulu un, j'ai écrit du code légal!

Répondre

9

Le code que vous avez posté est parfaitement légal et ne devrait pas provoquer de segfault - il n'est pas nécessaire de faire quoi que ce soit. Votre problème doit se situer ailleurs - s'il vous plaît poster le plus petit exemple de code qui provoque le problème.

Editer: Vous avez maintenant édité le code pour avoir une signification totalement différente. Cependant, la raison pour laquelle "hello world" n'est pas affiché est que le tampon de sortie n'a pas été vidé. Essayez addinig

fflush(stdout); 

après la printf.

En ce qui concerne la localisation de la source du problème que vous avez deux choix:

  • arrosent généreusement printfs par votre code en utilisant les __FILE__ et __LINE__ macros C
  • à utiliser votre débogueur - si vos supports de plate-forme core dumps, vous pouvez utiliser l'image de base pour trouver l'emplacement de l'erreur.
+0

Je pensais ça.* gratte la tête * bien que je devienne mental. –

+0

Désolé pour ce lol, j'ai corrigé mon code pour que ce soit un vrai problème. J'avais besoin de quoi que ce soit qui causerait une erreur de segmentation. – JoeCool

+0

"oh merde c'est mauvais" sera stocké dans la zone des symboles (?) Et le pointeur est stocké dans testChar ... vous pouvez le voir avec le débogueur. – stefanB

1

Ce code ne devrait pas segfault. Vous affectez simplement un pointeur à une chaîne littérale à une variable pointeur. Les choses seraient différentes si vous étiez par exemple. en utilisant strcpy pour copier des éléments avec un pointeur non valide.

Le message qui n'apparaît pas peut être dû aux E/S mises en mémoire tampon. Imprimez un caractère de nouvelle ligne \n ou appelez fflush pour vider le tampon de sortie.

+0

Ajouter \ n ne vide pas le tampon. Vous devez appeler fflush manuellement pour le faire. –

0

Vous avez deux problèmes. Le premier est que votre code (original) ne segmentera pas. Il est parfaitement possible d'affecter cette chaîne à un pointeur char. Mais laissons cela de côté pour l'instant et prétendons que vous avez mis quelque chose là-bas que serait segfault.

Ensuite, il s'agit généralement de tampons, celui de la bibliothèque d'exécution C et celui de l'OS lui-même. Vous devez les vider.

La meilleure façon de le faire était (sous UNIX, pas tout à fait certain de la fsync sous Linux, mais vous devez être garanti que ce wil arriver à terme à moins que le système lui-même descend):

printf ("DEBUG point 72\n"); fflush (stdout); fsync (fileno (stdout)); 

Je Cela est souvent fait sous UNIX et cela garantit que les bibliothèques d'exécution C sont vidées sous UNIX (fflush) et que les tampons UNIX sont synchronisés sur le disque (fsync), ce qui est utile si stdout n'est pas un terminal ou si vous le faites pour un handle de fichier différent.

5

printf écrit dans stdout, qui est mis en tampon. Parfois, ce tampon n'est pas vidé avant que votre programme se bloque, vous ne voyez jamais la sortie. Deux façons d'éviter cela:

  1. utilisez fprintf(stderr, "error string"); puisque stderr n'est pas mis en mémoire tampon. Pour ajouter un appel à fflush(stdout); après l'appel printf.

Comme Neil et d'autres l'ont dit, le code tel qu'il est écrit est très bien. C'est-à-dire, jusqu'à ce que vous commenciez à modifier le tampon que testChar pointe vers.

+0

Pourquoi n'ai-je pas besoin de malloc pour le char poitner? Dans un sens, le compilateur met-il automatiquement en mémoire la mémoire de la chaîne "oh crap this is bad" et lui indique testChar? – JoeCool

+0

Fondamentalement, oui, sauf que la mémoire allouée pour la chaîne est contenue dans le programme lui-même et est en lecture seule. Si vous essayez de le modifier, votre application va planter. –

2

La sortie est mise en mémoire tampon par défaut, le segfault se produit avant que la sortie ne soit réellement écrite sur stdout. Essayez:

(stderr est unbuffered par défaut.)

3

"Comme, un moyen facile/intuitive pour trouver la ligne de code qui est un problème?"

Utilisez gdb (ou tout autre débogueur).

Pour trouver où vous le compiler les défauts de votre programme avec l'option -g (pour inclure les symboles de débogage) exécuter votre application à partir gdb, il arrêtera sur la faute seg.

Vous pouvez ensuite regarder backtrace avec la commande bt pour voir à quel point vous avez le défaut seg.

exemple:

> gdb ./x 
(gdb) r 
Starting program: /proj/cpp/arr/x 
Program received signal EXC_BAD_ACCESS, Could not access memory. 
Reason: KERN_PROTECTION_FAILURE at address: 0x00000000 
0x000019a9 in willfail() at main.cpp:22 
22   *a = 3; 
(gdb) bt 
#0 0x000019a9 in willfail() at main.cpp:22 
#1 0x00001e32 in main() at main.cpp:49 
(gdb) 
0
void* test; 

printf("hello world"); 
test[5] = 234; 

Il est probable que « Bonjour tout le monde » est tamponnées par le système quelque part et n'est pas immédiatement imprimé à l'écran. Son attente stockée pour une chance pour n'importe quel processus/thread/tout ce qui est en charge de l'écriture d'écran peut avoir une chance de le traiter. Et pendant qu'il attend (et peut-être tamponner d'autres données à la sortie), vous êtes en train de terminer la fonction. Il vient sur l'accès illégal et segfaults.