Agora, antes que as pessoas comecem a marcar isso como um duplicado, li tudo o que se segue, nenhum deles fornece a resposta que procuro:
- C FAQ: O que há de errado em lançar o valor de retorno de malloc?
- SO: Devo converter explicitamente o valor de retorno de malloc ()?
- SO: Ponteiros desnecessários em C
- SO: Eu lanço o resultado de malloc?
Tanto o C FAQ quanto muitas respostas às perguntas acima citam um erro misterioso que malloc
o valor de retorno do casting pode ocultar; entretanto, nenhum deles dá um exemplo específico de tal erro na prática. Agora preste atenção que eu disse erro , não aviso .
Agora com o seguinte código:
#include <string.h>
#include <stdio.h>
// #include <stdlib.h>
int main(int argc, char** argv) {
char * p = /*(char*)*/malloc(10);
strcpy(p, "hello");
printf("%s\n", p);
return 0;
}
Compilar o código acima com gcc 4.2, com e sem o elenco, dá os mesmos avisos, e o programa é executado corretamente e fornece os mesmos resultados em ambos os casos.
anon@anon:~/$ gcc -Wextra nostdlib_malloc.c -o nostdlib_malloc
nostdlib_malloc.c: In function ‘main’:
nostdlib_malloc.c:7: warning: incompatible implicit declaration of built-in function ‘malloc’
anon@anon:~/$ ./nostdlib_malloc
hello
Então, alguém pode dar um exemplo de código específico de um erro de compilação ou tempo de execução que pode ocorrer por causa do malloc
valor de retorno do casting , ou isso é apenas uma lenda urbana?
Editar Eu encontrei dois argumentos bem escritos sobre este assunto:
- A favor do Casting: CERT Advisory: Converta imediatamente o resultado de uma chamada de função de alocação de memória em um ponteiro para o tipo alocado
- Contra a transmissão (erro 404 de 14/02/2012: use a cópia do Internet Archive Wayback Machine de 27/01/2010. {18/03/2016: "A página não pode ser rastreada ou exibida devido ao robots.txt."})
void
ponteiros permite compilar o código como C ++; algumas pessoas dizem que é um recurso, eu diria que é um bug;)malloc
valor de retorno do casting: casting paraint*
um arco de 64 bits.C
não está marcadaC++
(são duas línguas diferentes). Portanto, qualquer discussão (como em algumas das respostas) não é relevante para esta questão.Respostas:
Você não receberá um erro do compilador , mas um aviso do compilador . Como dizem as fontes que você cita (especialmente a primeira ), você pode obter um erro de tempo de execução imprevisível ao usar o elenco sem incluir
stdlib.h
.Portanto, o erro do seu lado não é o elenco, mas o esquecimento de incluir
stdlib.h
. Os compiladores podem assumir quemalloc
é um retorno de funçãoint
, portanto, convertendo ovoid*
ponteiro realmente retornado pormalloc
paraint
e, em seguida, para o seu tipo de ponteiro devido à conversão explícita. Em algumas plataformas, osint
ponteiros podem ocupar diferentes números de bytes, portanto, as conversões de tipo podem levar à corrupção de dados.Felizmente, os compiladores modernos fornecem avisos que apontam para o erro real. Veja a
gcc
saída que você forneceu: avisa que a declaração implícita (int malloc(int)
) é incompatível com a embutidamalloc
. Entãogcc
parece sabermalloc
mesmo semstdlib.h
.Deixar de lado o elenco para evitar esse erro é basicamente o mesmo raciocínio que escrever
if (0 == my_var)
ao invés de
if (my_var == 0)
já que o último pode levar a um erro sério se for confundido
=
e==
, enquanto o primeiro levaria a um erro de compilação. Pessoalmente, prefiro o último estilo, pois reflete melhor minha intenção e não costumo cometer esse erro.O mesmo é verdadeiro para converter o valor retornado por
malloc
: Prefiro ser explícito na programação e geralmente faço uma verificação dupla para incluir os arquivos de cabeçalho para todas as funções que utilizo.fonte
stdlib.h
já é um erro por si só, mesmo se você receber apenas avisos de "declaração implícita".Um dos bons argumentos de nível superior contra a projeção do resultado de
malloc
muitas vezes não é mencionado, embora, em minha opinião, seja mais importante do que os problemas de nível inferior bem conhecidos (como truncar o ponteiro quando a declaração está faltando).Uma boa prática de programação é escrever código, que seja o mais independente de tipo possível. Isso significa, em particular, que os nomes de tipo devem ser mencionados no código o menos possível ou, melhor ainda, não devem ser mencionados. Isso se aplica a casts (evite casts desnecessários), tipos como argumentos de
sizeof
(evite usar nomes de tipo emsizeof
) e, geralmente, todas as outras referências a nomes de tipo.Os nomes de tipo pertencem às declarações. Tanto quanto possível, os nomes de tipo devem ser restritos a declarações e apenas a declarações.
Deste ponto de vista, este pedaço de código é ruim
int *p; ... p = (int*) malloc(n * sizeof(int));
e isso é muito melhor
int *p; ... p = malloc(n * sizeof *p);
não simplesmente porque "não lança o resultado de
malloc
", mas sim porque é independente de tipo (ou agnosítico de tipo, se você preferir), porque se ajusta automaticamente a qualquer tipo quep
seja declarado com, sem exigir qualquer intervenção de o usuário.fonte
Presume-se que funções não prototipadas retornem
int
.Então você está lançando um
int
para um ponteiro. Se os ponteiros forem mais largos do queint
s em sua plataforma, esse é um comportamento altamente arriscado.Além disso, é claro, algumas pessoas consideram os avisos como erros, ou seja, o código deve ser compilado sem eles.
Pessoalmente, acho que o fato de você não precisar converter
void *
para outro tipo de ponteiro é um recurso em C e considero o código que está quebrado.fonte
void*
.int
." - Quer dizer que é possível alterar o tipo de retorno de funções não prototipadas?#ifdef __cplusplus \nextern "C" { \n#endif static inline uint16_t swb(uint16_t a) {return ((a << 8) | ((a >> 8) & 0xFF); } \n#ifdef __cplusplus\n } \n#endif
. Agora, por que você deseja chamar malloc em uma função embutida estática, eu realmente não sei, mas cabeçalhos que funcionam em ambos dificilmente são desconhecidos.Se você fizer isso ao compilar no modo de 64 bits, o ponteiro retornado será truncado para 32 bits.
EDIT: Desculpe por ser muito breve. Aqui está um fragmento de código de exemplo para fins de discussão.
Suponha que o ponteiro de heap retornado seja algo maior do que o representável em um int, digamos 0xAB00000000.
Se malloc não for prototipado para retornar um ponteiro, o valor int retornado inicialmente estará em algum registro com todos os bits significativos definidos. Agora o compilador diz, "ok, como faço para converter e int em um ponteiro". Isso vai ser uma extensão de sinal ou extensão zero dos 32 bits de ordem inferior que foi dito que malloc "retorna" ao omitir o protótipo. Como int é assinado, acho que a conversão será a extensão de sinal, que neste caso converterá o valor para zero. Com um valor de retorno de 0xABF0000000, você obterá um ponteiro diferente de zero que também causará diversão quando você tentar desreferenciá-lo.
fonte
Uma regra de software reutilizável:
No caso de escrever uma função inline na qual usou malloc (), a fim de torná-la reutilizável para código C ++ também, faça uma conversão de tipo explícita (por exemplo, (char *)); caso contrário, o compilador reclamará.
fonte
malloc
/free
para ambos tende a ser melhor do que tentar usarmalloc
em C enew
em C ++, especialmente se estruturas de dados são compartilhados entre C e C ++ código e existe a possibilidade de que um objeto seja criado em código C e lançado em código C ++ ou vice-versa.Um ponteiro void em C pode ser atribuído a qualquer ponteiro sem uma conversão explícita. O compilador dará um aviso, mas pode ser reutilizável em C ++ pela conversão de tipo
malloc()
para o tipo correspondente. Sem a conversão de tipo, também pode ser usado em C , porque C não é uma verificação de tipo estrita . Mas C ++ é estritamente verificação de tipo, portanto, é necessário convertermalloc()
em C ++.fonte