J'essaie de compiler et d'exécuter le programme C ci-dessous sur mes machines Ubuntu et Windows avec GCC et VC9. Cependant, je suis confronté à des problèmes ci-dessous:
Sur la machine Ubuntu:
GCC compile très bien, mais lors de l'exécution, cette invite s'affiche:
Segmentation Fault (Core Dump).
Sur une machine Windows:
VC9 Compile et fonctionne bien. GCC compile bien, mais le processus se termine lorsque le programme est exécuté.
Besoin de votre assistance experte ici. Voici mon code:
#include <string.h>
#include <stdio.h>
int calc_slope(int input1,int input2)
{
int sum=0;
int start=input1;
int end=input2;
int curr=start;
//some validation:
if (input1>input2)
return -1;
while(curr<=end)
{
if (curr>100)
{
char *s="";
int length;
int left;
int right;
int cent;
sprintf(s,"%d",curr);
length=strlen(s);
s++;
do
{
//printf("curr=%d char=%c pointer=%d length=%d \n",curr,*s,s,length);
left = *(s-1) - '0';
cent = *s - '0';
right = *(s+1) - '0';
//printf("curr=%d l=%d c=%d r=%d\n",curr,left,cent,right);
if ( (cent>left && cent>right) || (cent<left && cent<right) )
{
sum+=1; //we have either a maxima or a minima.
}
s++;
} while (*(s+1)!='\0');
}
curr++;
}
return sum;
}
int main()
{
printf("%d",calc_slope(1,150));
return 0;
}
Mise à jour:
Le crédit va à Eliah non seulement me aider suivre l'erreur, mais aussi me présenter à gdb
et son outil de sauvegarde de traçage ( bt
) qui sont si utiles dans le débogage d' un programme compilé gcc. Voici la version modifiée, j'ai travaillé après quelques essais et erreurs:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int calc_slope(int input1,int input2)
{
int sum=0;
int start=input1;
int end=input2;
int curr=start;
//some validation:
if (input1>input2)
return -1;
while(curr<=end)
{
if (curr>100)
{
int size=10;
char *s=(char*)malloc((size+1) * sizeof(char));
int left;
int right;
int cent;
sprintf(s,"%d",curr);
s++;
do
{
left = *(s-1) - '0';
cent = *s - '0';
right = *(s+1) - '0';
if ( (cent>left && cent>right) || (cent<left && cent<right) )
{
sum+=1; //we have either a maxima or a minima.
}
s++;
} while (*(s+1)!='\0');
}
curr++;
}
return sum;
}
int main()
{
printf("%d",calc_slope(1,150));
return 0;
}
Réponses:
Une erreur de segmentation se produit lorsqu'un programme tente d'accéder à la mémoire en dehors de la zone qui lui a été allouée.
Dans ce cas, un programmeur C expérimenté peut voir que le problème se produit dans la ligne où
sprintf
est appelé. Mais si vous ne pouvez pas dire où votre erreur de segmentation se produit, ou si vous ne voulez pas vous embêter à lire le code pour essayer de le comprendre, vous pouvez créer votre programme avec des symboles de débogage (avecgcc
, le-g
drapeau fait cela ), puis exécutez-le via un débogueur.J'ai copié votre code source et l'ai collé dans un fichier que j'ai nommé
slope.c
. Ensuite, je l'ai construit comme ceci:(Le
-Wall
est facultatif. Il sert simplement à lui faire générer des avertissements pour plus de situations. Cela peut également aider à déterminer ce qui pourrait ne pas fonctionner.)Ensuite, j'ai exécuté le programme dans le débogueur
gdb
en exécutant d'abordgdb ./slope
pour démarrergdb
avec le programme, puis, une fois dans le débogueur, en donnant larun
commande au débogueur:(Ne vous inquiétez pas pour mon
you have broken Linux kernel i386 NX
...support
message; cela n'empêche pasgdb
d'être utilisé efficacement pour déboguer ce programme.)Ces informations sont hautement cryptées ... et si vous n'avez pas de symboles de débogage installés pour libc, alors vous obtiendrez un message encore plus cryptique qui a une adresse hexadécimale au lieu du nom de la fonction symbolique
_IO_default_xsputn
. Heureusement, cela n'a pas d'importance, car ce que nous voulons vraiment savoir, c'est où se produit le problème dans votre programme .Donc, la solution est de regarder en arrière, pour voir quels appels de fonction ont eu lieu menant à cet appel de fonction particulier dans une bibliothèque système où le
SIGSEGV
signal a finalement été déclenché.gdb
(et tout débogueur) a cette fonctionnalité intégrée: elle s'appelle une trace de pile ou une trace arrière . J'utilise labt
commande debugger pour générer une trace dansgdb
:Vous pouvez voir que votre
main
fonction appelle lacalc_slope
fonction (que vous vouliez), puiscalc_slope
appellesprintf
, qui est (sur ce système) implémentée avec des appels à quelques autres fonctions de bibliothèque associées.Ce qui vous intéresse généralement, c'est l'appel de fonction dans votre programme qui appelle une fonction en dehors de votre programme . Sauf s'il y a un bogue dans la bibliothèque / les bibliothèques elles-mêmes que vous utilisez (dans ce cas, la bibliothèque C standard
libc
fournie par le fichier de bibliothèquelibc.so.6
), le bogue qui provoque le crash est dans votre programme et sera souvent à ou près de la dernier appel dans votre programme.Dans ce cas, c'est:
C'est là que votre programme appelle
sprintf
. Nous le savons parce quesprintf
c'est la prochaine étape. Mais même sans qu'il l'indique, vous le savez parce que c'est ce qui se passe à la ligne 26 , et il dit:Dans votre programme, la ligne 26 contient:
(Vous devez toujours utiliser un éditeur de texte qui affiche automatiquement les numéros de ligne, au moins pour la ligne sur laquelle vous êtes actuellement. Cela est très utile pour interpréter à la fois les erreurs de compilation et les problèmes d'exécution révélés lors de l'utilisation d'un débogueur.)
Comme discuté dans la réponse de Dennis Kaarsemaker ,
s
est un tableau à un octet. (Pas zéro, car la valeur que vous lui avez attribuée""
, est longue d'un octet, c'est-à-dire qu'elle est égale à{ '\0' }
, de la même manière qu'elle"Hello, world!\n"
est égale à{ 'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n', '\0' }
.)Alors, pourquoi cela pourrait- il toujours fonctionner sur une plate-forme (et apparemment, cela est compilé avec VC9 pour Windows)?
Les gens disent souvent que lorsque vous allouez de la mémoire, puis essayez d'accéder à la mémoire en dehors, cela produit une erreur. Mais ce n'est pas vraiment vrai. Selon les normes techniques C et C ++, cela produit vraiment un comportement indéfini.
En d'autres termes, tout peut arriver!
Pourtant, certaines choses sont plus probables que d'autres. Pourquoi un petit tableau sur la pile semble-t-il, sur certaines implémentations, fonctionner comme un plus grand tableau sur la pile?
Cela revient à la façon dont l'allocation de pile est mise en œuvre, qui peut varier d'une plateforme à l'autre. Votre exécutable peut allouer plus de mémoire à sa pile que ce qui est réellement destiné à être utilisé à tout moment. Parfois, cela peut vous permettre d'écrire dans des emplacements de mémoire que vous n'avez pas explicitement revendiqués dans votre code. Il est très probable que c'est ce qui se passe lorsque vous créez votre programme dans VC9.
Cependant, vous ne devez pas compter sur ce comportement même dans VC9. Cela peut potentiellement dépendre de différentes versions de bibliothèques pouvant exister sur différents systèmes Windows. Mais le problème est encore plus probable que l'espace de pile supplémentaire est alloué avec l'intention qu'il sera réellement utilisé, et donc qu'il peut réellement être utilisé.Ensuite, vous vivez le cauchemar complet d'un «comportement indéfini», où, dans ce cas, plus d'une variable pourrait finir par être stockée au même endroit, où l'écriture de l'une écrase l'autre ... mais pas toujours, car parfois elle écrit dans des variables sont mis en cache dans les registres et ne sont pas réellement exécutés immédiatement (ou les lectures des variables peuvent être mises en cache, ou une variable peut être supposée être la même qu'avant car la mémoire qui lui est allouée est connue du compilateur comme n'ayant pas été écrite via la variable elle-même).
Et cela m'amène à l'autre possibilité probable pour laquelle le programme a fonctionné lorsqu'il est construit avec VC9. Il est possible, et quelque peu probable, qu'un tableau ou une autre variable ait été réellement alloué par votre programme (ce qui peut inclure l'allocation par une bibliothèque que votre programme utilise) pour utiliser l'espace après le tableau d'un octet
s
. Ainsi, le fait de traiters
comme un tableau de plus d'un octet aurait pour effet d'accéder au contenu de ces / ces variables / tableaux, ce qui pourrait également être mauvais.En conclusion, lorsque vous avez une erreur comme celle-ci, vous avez de la chance d'obtenir une erreur comme "Erreur de segmentation" ou "Erreur de protection générale". Lorsque vous ne l' avez pas , vous ne découvrirez peut-être pas avant qu'il ne soit trop tard que votre programme ait un comportement indéfini.
la source
Bonjour débordement de tampon!
Vous allouez un octet à une chaîne de la pile, puis vous y écrivez plusieurs octets. Et pour couronner le tout, vous lisez au-delà de la fin de ce tableau. Veuillez lire un manuel C et en particulier la section sur les chaînes et leur allouer de la mémoire.
la source