FAQ
Contato
- Jean Paulo Martins (jeanmartins utfpr edu br)
- Sala 105, Bloco S (UTFPR - Campus Pato Branco)
Conteúdo
Segmentation Fault
Se seu código está dando falha de segmentação, rode ele dentro do gdb, em muitos casos nem é preciso procurar o erro passo a passo, pois o gdb mostra informações de onde a falha de segmentação ocorreu.
Exemplo 1
@:~$ ./list < input/input02.txt
[1] 35
Falha de segmentação (imagem do núcleo gravada)
Esta saída não nos dá informação suficiente para a correção do erro. Portanto, utilizarei o gdb. Primeiro passo, compilar com a flag -g
@:~$ gcc main.c list.c -o list -g
Após isso, inicializar o gdb passando o executável como parâmetro
@:~$ gdb ./list
A seguir, utilizar o comando run
do gdb para iniciar a execução do programa.
(gdb) run ./list < input/input02.txt > out
Starting program: ./list < input/input02.txt > out
Program received signal SIGSEGV, Segmentation fault.
0x00000000004009fb in list_erase (v=0x7fffffffdfd0, i=1) at list.c:101
101 n->next=j->next;
Desta saída, podemos focar apenas em algumas partes.
Program received signal SIGSEGV, Segmentation fault.
in list_erase (v, i=1) at list.c:101
101 n->next=j->next;
SIGSEGV, é uma constante representando o tipo de erro, neste caso segmentation fault, que se refere à tentativa de acesso de memória não permitido: além dos limites de um vetor, ou em posições de memória que não foram alocadas pelo programador.
Note, que list_erase (v, i=1)
, nos diz que o erro aconteceu na função list_erase
, quando recebendo a posição i=1
e nos mostra também a linha exata onde a falha de segmentação aconteceu.
Exemplo 2
Seguiremos o mesmo procedimento descrito anteriormente.
@:~$ ./list < input/input03.txt
[1] 68
[2] 68 98
[3] 16 68 98
[2] 68 98
[1] 98
Falha de segmentação (imagem do núcleo gravada)
@:~$ gdb ./list
(gdb) run ./list < input/input03.txt
[1] 68
[2] 68 98
[3] 16 68 98
[2] 68 98
[1] 98
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400acb in list_pop_back (l=0x604020) at list.c:183
183 while(ptr->next->next != NULL)
Neste exemplo, a falha de segmentação ocorreu na linha 183, na função list_pop_back
.
in list_pop_back (l=0x604020) at list.c:183
183 while(ptr->next->next != NULL)
Como sabemos, segmentation fault indica acesso indevido de memória. Portanto podemos imaginar que algo errado com a operação
183 while(ptr->next->next != NULL)
O que aconteceria se ptr->next
for NULL
? Bom, neste caso, ao tentar acessar o próximo ->next
, estariamos acessando o endereço indicado por NULL
, e lá procurando o campo next
, algo como: NULL->next
.
Como NULL
é usualmente definido como o endereço 0
da memória, qualquer acesso a esse endereço levará à falha de segmentação. Portanto, sempre que houver falha de segmentação, a primeira suspeita deve ser:
- Acesso fora dos limites de um vetor
- Acesso (dereferenciamento) ao endereço
NULL
.
Programação estruturada
Função list_size(l)
“Se a estrutura de dados list
duplamente encadeada já possui um campo size
, qual a necessidade da função list_size()
.”
Em algumas situações certas funções parecem desnecessárias, é o caso do list_size()
. Para entender o porque de sua utilidade, é bom pensarmos em termos da lista como estrutura abstrata, a qual tem uma interface list.h
.
Nesse sentido, ambas implementações, “lista simples”, “listas duplas”, teriam uma implementação de list_size()
. Nas listas simples, preciso percorrer toda lista para saber o tamanho, nas duplas, basta acessar o size já existente. Algo desse tipo
// forward_list.c
int list_size(list* l) {
while (ptr != NULL) size++...
return size;
}
// list.c
int list_size(list* l) {
return l->size;
}
Agora suponha que estejamos utilizando uma estrutura lista para resolver algum problema, soma/multiplicação de inteiros grandes, splice, por exemplo.
Neste caso, se sempre que precisarmos saber o tamanho de uma lista, utilizarmos list_size()
(e as demais funções de acesso a lista), isso nos permitiria utilizar tanto a implementação da lista de encadeamento simples (forward_list.c
) quanto a de encadeamento duplo (list.c
). Bastando escolher durante a compilação.
Se pelo contrário, eu utilizasse l->size
diretamente, eu não conseguiria utilizar a implementação forward_list.c
, visto que nela, esse campo não existe.
Portanto, a ideia de termos várias funções para acessar certas propriedades da estrutura, nos permite separar a implementação da definição da estrutura de dados. Isso é sugerido quando programamos pensando em reutilização, e programação de bibliotecas.
Obviamente, podemos também querer implementar uma lista para algo bem específico, sem pensar que tal código será reutilizado para várias coisas diferentes, e então, não precisaríamos nos preocupar com isso.
Em geral é essa a ideia, e se aplica ao uso das demais funções também.