
Estou tentando compilar e executar o programa C abaixo em minhas máquinas Ubuntu e Windows com GCC e VC9. No entanto, estou enfrentando os problemas abaixo:
Na máquina Ubuntu:
O GCC compila bem, mas quando executado, aparece este prompt:
Segmentation Fault (Core Dump).
Na máquina Windows:
VC9 compila e funciona bem. O GCC compila bem, mas o processo termina quando o programa é executado.
Precisa de sua ajuda especializada aqui. Aqui está o meu código:
#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;
}
Atualizar:
O crédito vai paraEliaspor não apenas me ajudar a rastrear o erro, mas também por me apresentar gdb
sua ferramenta de back-tracing ( bt
), que é muito útil na depuração de um programa compilado pelo gcc. Aqui está a versão modificada, que desenvolvi após algumas tentativas e erros:
#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;
}
Responder1
Afalha de segmentaçãoocorre quando um programa tenta acessar a memória fora da área que foi alocada para ele.
Neste caso, um programador C experiente pode perceber que o problema está acontecendo na linha onde sprintf
é chamado. Mas se você não souber onde está ocorrendo a falha de segmentação ou se não quiser se preocupar em ler o código paratentarpara descobrir isso, você pode criar seu programa com símbolos de depuração (com gcc
, o -g
sinalizador faz isso) e executá-lo por meio de um depurador.
Copiei seu código-fonte e colei em um arquivo que chamei de slope.c
. Então eu construí assim:
gcc -Wall -g -o slope slope.c
(O -Wall
é opcional. Serve apenas para produzir avisos para mais situações. Isso também pode ajudar a descobrir o que pode estar errado.)
Em seguida, executei o programa no depurador gdb
, primeiro executando gdb ./slope
para iniciar gdb
o programa e, em seguida, uma vez no depurador, dando o run
comando ao depurador:
ek@Kip:~/source$ gdb ./slope
GNU gdb (GDB) 7.5-ubuntu
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/ek/source/slope...done.
(gdb) run
Starting program: /home/ek/source/slope
warning: Cannot call inferior functions, you have broken Linux kernel i386 NX (non-executable pages) support!
Program received signal SIGSEGV, Segmentation fault.
0x001a64cc in _IO_default_xsputn () from /lib/i386-linux-gnu/libc.so.6
(Não se preocupe com minha you have broken Linux kernel i386 NX
... support
mensagem; ela não impede gdb
que seja usada de forma eficaz para depurar este programa.)
Essa informação é altamente enigmática... e se você não tiver símbolos de depuração instalados para libc, você receberá uma mensagem ainda mais enigmática que possui um endereço hexadecimal em vez da função simbólica name _IO_default_xsputn
. Felizmente, isso não importa, porque o que realmente queremos saber éonde no seu programao problema está acontecendo.
Portanto, a solução é olhar para trás, para ver quais chamadas de função ocorreram antes daquela chamada de função específica em uma biblioteca do sistema onde o SIGSEGV
sinal foi finalmente acionado.
gdb
(e qualquer depurador) possui esse recurso integrado: é chamado derastreamento de pilhaourastreamento de retorno. Eu uso o bt
comando debugger para gerar um backtrace em gdb
:
(gdb) bt
#0 0x001a64cc in _IO_default_xsputn () from /lib/i386-linux-gnu/libc.so.6
#1 0x00178e04 in vfprintf () from /lib/i386-linux-gnu/libc.so.6
#2 0x0019b234 in vsprintf () from /lib/i386-linux-gnu/libc.so.6
#3 0x0017ff7b in sprintf () from /lib/i386-linux-gnu/libc.so.6
#4 0x080484cc in calc_slope (input1=1, input2=150) at slope.c:26
#5 0x08048578 in main () at slope.c:52
(gdb)
Você pode ver que sua main
função chama a calc_slope
função (que você pretendia) e depois calc_slope
chama sprintf
, que é (neste sistema) implementada com chamadas para algumas outras funções de biblioteca relacionadas.
O que geralmente lhe interessa é a chamada de funçãono seu programaque chama uma funçãofora do seu programa. A menos que haja um bug nas próprias bibliotecas que você está usando (neste caso, a biblioteca C padrão libc
fornecida pela biblioteca file libc.so.6
), o bug que causa a falha está no seu programa emuitas vezesestará na última chamada ou perto delaem seu programa.
Neste caso, é:
#4 0x080484cc in calc_slope (input1=1, input2=150) at slope.c:26
É aí que seu programa chama sprintf
. Sabemos disso porque sprintf
é o próximo passo. Mas mesmo sem afirmar isso, você sabe disso porqueisso é o que acontece na linha 26, e diz:
... at slope.c:26
No seu programa, a linha 26 contém:
sprintf(s,"%d",curr);
(Você deve sempre usar um editor de texto que mostre automaticamente os números das linhas, pelo menos para a linha em que você está atualmente. Isso é muito útil na interpretação de erros em tempo de compilação e problemas em tempo de execução revelados ao usar um depurador.)
Como discutido emResposta de Dennis Kaarsemaker, s
é uma matriz de um byte. (Não é zero, porque o valor que você atribuiu, ""
, tem um byte de comprimento, ou seja, é igual a { '\0' }
, da mesma forma que "Hello, world!\n"
é igual a { 'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n', '\0' }
.)
Então por quepoderiaisso ainda funciona em algumas plataformas (e aparentemente funciona quando compilado com VC9 para Windows)?
As pessoas costumam dizer que quando você aloca memória e tenta acessar a memória fora dela, isso produz um erro. Mas isso não é verdade. De acordo com os padrões técnicos C e C++, o que isso realmente produz écomportamento indefinido.
Em outras palavras, tudo pode acontecer!
Ainda assim, algumas coisas são mais prováveis do que outras. Por que é que um pequeno array na pilha, em algumas implementações, parecerá funcionar como um array maior na pilha?
Isso se resume à forma como a alocação de pilha é implementada, que pode variar de plataforma para plataforma. Seu executável pode alocar mais memória para sua pilha do que realmente deveria ser usado a qualquer momento. Às vezes isso pode permitir que você grave em locais de memória que você nãoexplicitamentereivindicado em seu código. É muito provável que seja isso que esteja acontecendo quando você cria seu programa no VC9.
No entanto,você não deve confiar nesse comportamento mesmo no VC9.Poderia depender potencialmente de diferentes versões de bibliotecas que poderiam existir em diferentes sistemas Windows. Masainda mais provávelé o problema de que o espaço extra da pilha é alocado com a intenção de ser realmente usado e, portanto,pode realmente ser usado.Então você experimenta o pesadelo completo do "comportamento indefinido", onde, neste caso, mais de uma variável pode acabar armazenada no mesmo lugar, onde escrever em uma substitui a outra... mas nem sempre, porque às vezes escreve em variáveis são armazenados em cache em registradores e não são realmente executados imediatamente (ou leituras de variáveis podem ser armazenadas em cache, ou uma variável pode ser assumida como a mesma que era antes porque a memória alocada para ela é conhecida pelo compilador como não tendo sido gravada através a própria variável).
E isso me leva à outra possibilidade provável de por que o programa funcionou quando construído com o VC9. É possível, e um tanto provável, quealgum array ou outra variávelfoi realmente alocado pelo seu programa (o que pode incluir ser alocado por uma biblioteca que seu programa está usando) para usar o espaço após o array de um byte s
. Então, tratar s
como um array maior que um byte teria o efeito de acessar o conteúdo daquelas variáveis/matrizes, o que também poderia ser ruim.
Concluindo, quando você comete um erro como esse, ésortudopara obter um erro como "Falha de segmentação" ou "Falha de proteção geral". Quando vocênãotiver isso, você pode não descobrir, até que seja tarde demais, que seu programacomportamento indefinido.
Responder2
Olá, estouro de buffer!
char *s="";
sprintf(s,"%d",curr);
length=strlen(s);
Você aloca um byte para uma string na pilha e então escreve mais de um byte nela. E ainda por cima, você lê além do final dessa matriz. Por favor, leia um manual C e especialmente a seção sobre strings e alocação de memória para elas.