OI pessoal, gostaria que vocês em ajudassem a identificar SEGMENTATION FAULT no programa, no qual o código compila sem erros. Porém, gostaria que não resolvessem o código, mas me ajudassem na maneira de pensar para corrigir o erro.
#include <stdio.h>
#include <stdlib.h>
char* return_buffer(int len)
{
char* buf;
buf = (char*) malloc(len * sizeof(char));
if (buf == NULL) exit(1);
for (int i = 0; i < len; i++)
buf[i] = rand() % 26;
return buf;
}
int main(int argc, char* argv[])
{
printf("Type a number:\n");
int length;
scanf("%d", length);
char* buffer;
buffer = return_buffer(length);
int i;
for (i = 0; i < length; i++)
printf("Value \"%c\" at \"%d\"\n", buffer[i], i);
free(buffer);
return 0;
}
O erro no seu código é bem sutil. Mas como você quer o raciocínio vamos lá. Segmentation fault é algo que ocorre quando você tenta acessar um endereço de memória proibido. Isso ocorre de várias maneiras, como acessar um ponteiro que não foi alocado, uma passagem errada de parâmetro para uma função.
Uma forma bem sutil de ocorrer esse problema é quando você quer passar um ponteiro para um int para uma função que recebe isso como parâmetro e você passa um int. Um ponteiro para int é um endereço que na verdade é um número que pode ser armazenado em um int. E isso implica que o compilador não emitirár erros de compilação, embora na maioria dos compiladores seja emitido um warning. Pense nisso.
A propósito, o compilador não consegue descobrir que você passou um int (em vez de um int*) para scanf, porque ele só checa o primeiro argumento, já que a declaração de scanf é basicamente:
int scanf (const char *format, ...);
Esse tipo de declaração (chamada de “varargs”) diz ao compilador “rapaz, eu posso passar qualquer número de parâmetros depois desse. Então não cheque os tipos parâmetros; quem tem de checar se está tudo bem é quem vai receber os parâmetros”.
[quote=entanglement]Você pediu ajuda em C, mas seu programa não é um programa C válido e sim C++ (variáveis locais não podem ser declaradas no meio dos comandos em C).
Indiquei onde está seu erro - é bem boboca mas serve para você prestar mais atenção.
[code] #include <stdio.h> #include <stdlib.h>
char* return_buffer(int len)
{
char* buf;
int i;
buf = (char*) malloc(len * sizeof(char));
if (buf == NULL) exit(1);
for (i = 0; i < len; i++)
buf[i] = rand() % 26;
return buf;
}
int main(int argc, char* argv[])
{
int length;
char* buffer;
int i;
printf("Type a number:\n");
scanf("%d", &length); /* <-- scanf requer enderecos!!!! */
buffer = return_buffer(length);
for (i = 0; i < length; i++)
printf("Value \"%c\" at \"%d\"\n", buffer[i], i);
free(buffer);
return 0;
}
[/code][/quote]
isso que da raiva em C, por que não da erro ? ou existe necessidade de usar scanf sem &
Bom, você está usando uma linguagem que é intrinsecamente insegura (que é a C). Você precisa se acostumar com os problemas que ela tem. Uma delas é esse - você precisa usar algumas ferramentas que não são só o compilador, para descobrir erros.
Por exemplo, checagem de parâmetros do scanf pode ser feita com ferramentas como o “lint” (alguns compiladores têm uma opção que é mais ou menos como o “lint”).
Eu chequei aqui o MS Visual Studio e vi que ele não checa os parâmetros do printf ou do scanf de jeito nenhum - ele só dá uma outra mensagem de erro, bem depois, que é
seuprograma.c(24) : warning C4700: uninitialized local variable 'length' used
Se você já tiver uma certa prática, vai entender que o scanf não funcionou para preencher o valor da variável length.
O problema é que normalmente você acha que é a variável length que tem de ser inicializada na sua declaração, então você toma a providência errada, que é a de inicializar a variável length com zero.
O que vai acabar ocorrendo é um Segmentation Fault do mesmo jeito
É possível declarar variáveis no meio dos métodos desde o C99, não?
Só que esse padrão ainda não foi implementado no Visual Studio (que na versão 2012 ainda usa C89), mas creio que tenha sido no GCC.
De qualquer forma, boa parte do que o entanglement falou é verdade. Em C, você tem que ficar muito atento com:
Casts automáticos - a linguagem foi feita para permitir manipulação bastante livre de dados, portanto, você pode fazer casts bastante agressivos, e a linguagem também faz casts assim de foma automática;
Undefined behaviors - A especificação da linguagem é simples, e muitas situações não são especificadas, ficando a carga do implementador do compilador escolher como fazer. Um exemplo é a instrução x = x++, ou o uso de uma variável não inicializada;
Práticas antigas - C e C++ são linguagens antigas. Portanto, há muitos comandos cujo uso já é obsoleto, e muitas práticas que devem ser aprendidas para um uso efetivo da linguagem. Cuidado para não programar em 2012 como se programava em 1980.
PS: A dúvida para o entanglement é genuína. Eu sou programador C++, não C.
[quote=entanglement]Você pediu ajuda em C, mas seu programa não é um programa C válido e sim C++ (variáveis locais não podem ser declaradas no meio dos comandos em C).
[/quote]
Hum, é mesmo - em C99 essa restrição de não poder declarar variáveis locais no meio de um bloco de código (que acho terrivelmente idiota) foi removida. Sorry - esse é um problema de eu não ter aqui instalada uma versão de um compilador que suporte corretamente C99.
De qualquer forma, eu acabo reforçando essa restrição - um por eu ter aprendido C há quase 30 anos atrás, e outra porque a gente acaba tendo de escrever programas em C de forma que compilem em qualquer compilador, mesmo naqueles muito antigos (por exemplo, quando você tem de usar um sistema Unix proprietário como o AIX e tem de usar versões de compiladores muito antigas - ou então quando você precisa usar o MS Visual Studio ). Desculpem minha desatualização.
Vinyodoy e entanglement, vocês costumam usar o quê para detectar problemas em programas C++? Apenas o debug do Visual Studio? Ou usam mais ferramentas?
Hum… eu diria que 20% são as mensagens do compilador, 60% a cabeça, 10% o debugador do compilador, e 10% alguma ferramenta que a gente encontre disponível para ser usada - por exemplo, para vazamentos de memória usa-se um tipo de ferramenta, para erros do tipo “segmentation fault” um outro tipo, e assim por diante.
Note que nos 60% inclui-se a escolha da ferramenta adequada - para cada tipo de problemas há uma ferramenta diferente. Pode ser, por exemplo, que no ambiente de produção só consigamos usar o Performance Monitor e mais nada, mas em desenvolvimento possamos usar o DebugDiag, e assim por diante.
[quote=entanglement]Hum… eu diria que 20% são as mensagens do compilador, 60% a cabeça, 10% o debugador do compilador, e 10% alguma ferramenta que a gente encontre disponível para ser usada - por exemplo, para vazamentos de memória usa-se um tipo de ferramenta, para erros do tipo “segmentation fault” um outro tipo, e assim por diante.
Note que nos 60% inclui-se a escolha da ferramenta adequada - para cada tipo de problemas há uma ferramenta diferente. Pode ser, por exemplo, que no ambiente de produção só consigamos usar o Performance Monitor e mais nada, mas em desenvolvimento possamos usar o DebugDiag, e assim por diante. [/quote]
Já imaginava essa resposta. Usar a cabeça sempre é o mais importante mesmo. E às vezes é necessário usar alguma coisa mais obscura pra diagnosticar o problema. Esses dias mesmo eu tive que ver o assembly de uma função em Pascal porque ela fazia um cálculo muito maluco usando ponteiros e via Debug não dava pra entender o que estava ocorrendo. Então às vezes é preciso usar a caixola mesmo.