Encontrei o seguinte quebra-cabeça C:
P: Por que o seguinte programa falha em segfault no IA-64, mas funciona bem no IA-32?
int main()
{
int* p;
p = (int*)malloc(sizeof(int));
*p = 10;
return 0;
}
Eu sei que o tamanho de int
em uma máquina de 64 bits pode não ser o mesmo que o tamanho de um ponteiro ( int
pode ser de 32 bits e o ponteiro pode ser de 64 bits). Mas não tenho certeza de como isso se relaciona com o programa acima. Alguma ideia?
c
pointers
segmentation-fault
usuário7
fonte
fonte
stdlib.h
não ser incluído?#include stdlib.h
(para malloc)#include <stdlib.h>
, é perfeitamente encontrado, mas isso não está em questão.sizeof(int) == sizeof(int*)
, por exemplo, os ponteiros são retornados por meio de um registro diferente paraint
s na convenção de chamada usada.malloc()
. GCC diz:warning: incompatible implicit declaration of built-in function 'malloc'
também.Respostas:
A conversão para
int*
mascara o fato de que, sem o apropriado,#include
o tipo de retorno demalloc
é assumido comoint
. Acontece que IA-64 tem, osizeof(int) < sizeof(int*)
que torna este problema óbvio.(Observe também que, por causa do comportamento indefinido, ele ainda pode falhar mesmo em uma plataforma onde
sizeof(int)==sizeof(int*)
é verdadeiro, por exemplo, se a convenção de chamada usou registros diferentes para retornar ponteiros do que inteiros)O FAQ comp.lang.c tem uma entrada discutindo por que
malloc
converter o retorno de nunca é necessário e é potencialmente ruim .fonte
new
em C ++ e sempre compilar C com um compilador C e não um compilador C ++.int
se não for conhecidomalloc
)void*
. Mas o código de chamada pensa que a função retornaint
(já que você optou por não dizer o contrário), então ele tenta ler o valor de retorno de acordo com a convenção de chamada para umint
. Portantop
, não necessariamente aponta para a memória alocada. Acontece que funcionou para IA32 porque umint
e umvoid*
são do mesmo tamanho e retornados da mesma maneira. Em IA64 você obtém o valor errado.Provavelmente porque você não está incluindo o arquivo de cabeçalho para
malloc
e, embora o compilador normalmente avise sobre isso, o fato de que você está convertendo explicitamente o valor de retorno significa que você está dizendo a ele que sabe o que está fazendo.Isso significa que o compilador espera que um
int
seja retornado domalloc
qual ele então se transforma em um ponteiro. Se eles forem de tamanhos diferentes, você ficará triste.É por isso que você nunca lança o
malloc
return em C. Ovoid*
que ele retorna será convertido implicitamente em um ponteiro do tipo correto (a menos que você não tenha incluído o cabeçalho, caso em que provavelmente teria avisado sobre o int- potencialmente inseguro conversão para ponteiro).fonte
void *
pode ser convertido para qualquer outro tipo de ponteiro implicitamente.int *p = malloc(sizeof(int))
funciona se o protótipo adequado está no escopo e falha se não estiver (porque então o resultado é considerado como estandoint
). Com o elenco, ambos seriam compilados e o último resultaria em erros quandosizeof(int) != sizeof(void *)
.stdlib.h
, o compilador não sabemalloc
e nem o seu tipo de retorno. Portanto, é apenas assumidoint
como padrão.É por isso que você nunca compila sem avisos sobre protótipos ausentes.
O elenco é necessário para compatibilidade com C ++. Há pouca razão (leia: nenhuma razão aqui) para omiti-lo.
A compatibilidade com C ++ nem sempre é necessária e, em alguns casos, nem sempre é possível, mas na maioria dos casos é facilmente alcançada.
fonte