Eu conjuro o resultado de malloc?

Mar 03 2009

Em esta pergunta , alguém sugeriu em um comentário que eu deveria não converter o resultado de malloc, ou seja,

int *sieve = malloc(sizeof(int) * length);

ao invés de:

int *sieve = (int *) malloc(sizeof(int) * length);

Por que isso seria o caso?

Respostas

2285 unwind Mar 03 2009 at 17:17

Não ; você não lança o resultado, uma vez que:

  • É desnecessário, pois void *é automaticamente e seguramente promovido a qualquer outro tipo de ponteiro neste caso.
  • Ele adiciona confusão ao código, os casts não são muito fáceis de ler (especialmente se o tipo de ponteiro for longo).
  • Faz você se repetir, o que geralmente é ruim.
  • Ele pode ocultar um erro se você se esquecer de incluí-lo <stdlib.h>. Isso pode causar travamentos (ou, pior, não causar um travamento até bem mais tarde em alguma parte totalmente diferente do código). Considere o que acontece se ponteiros e inteiros tiverem tamanhos diferentes; então você está escondendo um aviso ao lançar e pode perder partes do seu endereço de retorno. Nota: a partir de C99, as funções implícitas deixaram de ser C, e este ponto não é mais relevante, pois não há suposição automática de que funções não declaradas retornem int.

Como esclarecimento, observe que eu disse "você não lança", e não "você não precisa lançar". Na minha opinião, é uma falha em incluir o elenco, mesmo que você acerte. Simplesmente não há benefícios em fazer isso, mas um monte de riscos potenciais, e incluir o elenco indica que você não sabe sobre os riscos.

Observe também, como os comentaristas apontam, que o texto acima fala sobre C puro, não C ++. Eu acredito firmemente em C e C ++ como linguagens separadas.

Para adicionar mais, seu código repete desnecessariamente as informações de tipo ( int), o que pode causar erros. É melhor remover a referência do ponteiro que está sendo usado para armazenar o valor de retorno, para "bloquear" os dois juntos:

int *sieve = malloc(length * sizeof *sieve);

Isso também move o lengthpara a frente para aumentar a visibilidade e remove os parênteses redundantes com sizeof; eles são necessários apenas quando o argumento é um nome de tipo. Muitas pessoas parecem não saber (ou ignorar) isso, o que torna seu código mais prolixo. Lembre-se: sizeofnão é uma função! :)


Embora mover lengthpara a frente possa aumentar a visibilidade em alguns casos raros, deve-se também prestar atenção que, no caso geral, deve ser melhor escrever a expressão como:

int *sieve = malloc(sizeof *sieve * length);

Já que manter o sizeofprimeiro, neste caso, garante que a multiplicação seja feita com pelo menos size_tmatemática.

Compare: malloc(sizeof *sieve * length * width)vs. malloc(length * width * sizeof *sieve)o segundo pode transbordar o length * widthquando widthe lengthsão tipos menores que size_t.

387 dirkgently Mar 03 2009 at 17:17

Em C, você não precisa converter o valor de retorno de malloc. O ponteiro para void retornado por mallocé convertido automagicamente para o tipo correto. No entanto, se você deseja que seu código seja compilado com um compilador C ++, um elenco é necessário. Uma alternativa preferida entre a comunidade é usar o seguinte:

int *sieve = malloc(sizeof *sieve * length);

que adicionalmente o livra de ter que se preocupar em mudar o lado direito da expressão se você mudar o tipo de sieve.

Casts são ruins, como as pessoas apontaram. Especialmente lançamentos de ponteiro.

360 RonBurk Feb 14 2013 at 23:15

Você não fundido, porque:

  • Isso torna seu código mais portável entre C e C ++ e, como mostra a experiência do SO, muitos programadores afirmam que estão escrevendo em C quando na verdade estão escrevendo em C ++ (ou C mais extensões de compilador local).
  • Não fazer isso pode ocultar um erro : observe todos os exemplos de SO de confundir quando escrever type *versus type **.
  • A ideia de que isso impede você de perceber que falhou em #includeum arquivo de cabeçalho apropriado, faz com que perca a floresta para as árvores . É o mesmo que dizer "não se preocupe com o fato de você não ter pedido ao compilador para reclamar por não ver protótipos - aquele estdlib.h incômodo é REALMENTE importante lembrar!"
  • Isso força uma verificação cruzada cognitiva extra . Ele coloca o (suposto) tipo desejado ao lado da aritmética que você está fazendo para o tamanho bruto dessa variável. Aposto que você poderia fazer um estudo do SO que mostre que os malloc()bugs são detectados muito mais rápido quando há um elenco. Assim como acontece com as asserções, as anotações que revelam a intenção diminuem os bugs.
  • Repetir-se de uma maneira que a máquina possa verificar costuma ser uma ótima idéia. Na verdade, isso é uma afirmação, e o uso de elenco é uma afirmação. Asserções ainda são a técnica mais geral que temos para obter o código correto, desde que Turing teve a ideia há tantos anos.
174 quinmars Mar 03 2009 at 18:17

Como outros afirmaram, não é necessário para C, mas necessário para C ++. Se você acha que vai compilar seu código C com um compilador C ++, por qualquer motivo, você pode usar uma macro, como:

#ifdef __cplusplus
# define NEW(type, count) ((type *)calloc(count, sizeof(type)))
#else
# define NEW(type, count) (calloc(count, sizeof(type)))
#endif

Dessa forma, você ainda pode escrever de uma forma muito compacta:

int *sieve = NEW(int, 1);

e ele irá compilar para C e C ++.

146 ashiquzzaman33 Oct 10 2015 at 04:23

Da Wikipedia :

Vantagens para fundir

  • Incluir o elenco pode permitir que um programa ou função C compile como C ++.

  • O elenco permite versões pré-1989 de malloc que originalmente retornavam um char *.

  • A conversão pode ajudar o desenvolvedor a identificar inconsistências no dimensionamento de tipo caso o tipo de ponteiro de destino mude, especialmente se o ponteiro for declarado longe da chamada malloc () (embora compiladores modernos e analisadores estáticos possam alertar sobre tal comportamento sem exigir a conversão).

Desvantagens do elenco

  • Sob o padrão ANSI C, o elenco é redundante.

  • Adicionar o elenco pode mascarar a falha em incluir o cabeçalho stdlib.h , no qual o protótipo de malloc é encontrado. Na ausência de um protótipo para malloc, o padrão requer que o compilador C assuma que malloc retorna um int. Se não houver conversão, um aviso será emitido quando este inteiro for atribuído ao ponteiro; entretanto, com o elenco, esse aviso não é produzido, ocultando um bug. Em certas arquiteturas e modelos de dados (como LP64 em sistemas de 64 bits, onde long e pointers são de 64 bits e int é de 32 bits), este erro pode realmente resultar em comportamento indefinido, já que o malloc declarado implicitamente retorna um 32- valor de bit, enquanto a função realmente definida retorna um valor de 64 bits. Dependendo das convenções de chamada e do layout da memória, isso pode resultar no esmagamento da pilha. É menos provável que esse problema passe despercebido em compiladores modernos, pois eles produzem avisos de que uma função não declarada foi usada de maneira uniforme, então um aviso ainda aparecerá. Por exemplo, o comportamento padrão do GCC é mostrar um aviso que diz "declaração implícita incompatível de função integrada", independentemente de o elenco estar presente ou não.

  • Se o tipo do ponteiro for alterado em sua declaração, também pode ser necessário alterar todas as linhas onde malloc é chamado e convertido.

Embora malloc sem conversão seja o método preferido e a maioria dos programadores experientes o escolha , você deve usar o que desejar para ter conhecimento dos problemas.

ou seja: Se você precisa compilar um programa C como C ++ (embora seja uma linguagem separada), você deve converter o resultado do uso malloc.

108 PaulJWilliams Mar 03 2009 at 17:18

Em C, você pode converter implicitamente um voidponteiro em qualquer outro tipo de ponteiro, portanto, não é necessária uma conversão . Usar um pode sugerir ao observador casual que há alguma razão pela qual um é necessário, o que pode ser enganoso.

104 Lundin Mar 20 2014 at 22:53

Você não lança o resultado de malloc, porque isso adiciona uma desordem inútil ao seu código.

A razão mais comum pela qual as pessoas lançam o resultado de mallocé porque não têm certeza sobre como a linguagem C funciona. Este é um sinal de alerta: se você não sabe como funciona um mecanismo de linguagem em particular, não tente adivinhar. Pesquise ou pergunte no Stack Overflow.

Alguns comentários:

  • Um ponteiro void pode ser convertido de / para qualquer outro tipo de ponteiro sem uma conversão explícita (C11 6.3.2.3 e 6.5.16.1).

  • C ++, entretanto, não permitirá uma conversão implícita entre void*e outro tipo de ponteiro. Portanto, em C ++, o elenco estaria correto. Mas se você programa em C ++, você deve usar newe não malloc(). E você nunca deve compilar código C usando um compilador C ++.

    Se você precisar oferecer suporte a C e C ++ com o mesmo código-fonte, use as opções do compilador para marcar as diferenças. Não tente satisfazer os dois padrões de linguagem com o mesmo código, porque eles não são compatíveis.

  • Se um compilador C não puder encontrar uma função porque você se esqueceu de incluir o cabeçalho, você receberá um erro do compilador / vinculador sobre isso. Portanto, se você se esqueceu de incluir <stdlib.h>, não é nada demais, não será possível construir seu programa.

  • Em compiladores antigos que seguem uma versão do padrão com mais de 25 anos, esquecer de incluir <stdlib.h>resultaria em um comportamento perigoso. Porque naquele padrão antigo, as funções sem um protótipo visível convertiam implicitamente o tipo de retorno para int. A conversão do resultado mallocexplicitamente ocultaria esse bug.

    Mas isso realmente não é um problema. Você não está usando um computador de 25 anos, então por que usaria um compilador de 25 anos?

95 EFraim Mar 03 2009 at 17:16

Em C, você obtém uma conversão implícita de void *para qualquer outro ponteiro (de dados).

72 YuHao Jun 10 2013 at 00:31

Converter o valor retornado por malloc()não é necessário agora, mas gostaria de adicionar um ponto que parece que ninguém apontou:

Nos tempos antigos, isto é, antes de ANSI C fornecer o void *como o tipo genérico de ponteiros, char *era o tipo para tal uso. Nesse caso, o elenco pode desligar os avisos do compilador.

Referência: C FAQ

55 user3079666 Mar 29 2014 at 01:21

Apenas adicionando minha experiência, estudando engenharia da computação, vejo que os dois ou três professores que vi escrevendo em C sempre lançam malloc, porém o que eu perguntei (com um currículo imenso e compreensão de C) me disse que é absolutamente desnecessário, mas costumava ser absolutamente específico, e para levar os alunos à mentalidade de serem absolutamente específicos. Essencialmente, o cast não mudará nada em como funciona, ele faz exatamente o que diz, aloca memória e o cast não o afeta, você obtém a mesma memória, e mesmo se você a lançar para outra coisa por engano (e de alguma forma escapar do compilador erros) C irá acessá-lo da mesma maneira.

Edit: Casting tem um certo ponto. Quando você usa a notação de array, o código gerado tem que saber quantos lugares de memória ele tem que avançar para chegar ao início do próximo elemento, isso é conseguido através da conversão. Dessa forma, você sabe que para um duplo você avança 8 bytes, enquanto para um interno avança 4 e assim por diante. Portanto, não tem efeito se você usar a notação de ponteiro, na notação de array torna-se necessário.

53 user968000 Feb 08 2013 at 05:22

Não é obrigatório converter os resultados de malloc, uma vez que retorna void*, e a void*pode ser apontado para qualquer tipo de dados.

37 Slothworks Oct 10 2015 at 00:47

Isto é o que o manual de referência da biblioteca GNU C diz:

Você pode armazenar o resultado de mallocem qualquer variável de ponteiro sem uma conversão, porque ISO C converte automaticamente o tipo void *em outro tipo de ponteiro quando necessário. Mas o elenco é necessário em contextos diferentes dos operadores de atribuição ou se você quiser que seu código seja executado em C.

E, de fato, o padrão ISO C11 (p347) diz isso:

O ponteiro retornado se a alocação for bem-sucedida é alinhado adequadamente de modo que possa ser atribuído a um ponteiro para qualquer tipo de objeto com um requisito de alinhamento fundamental e, em seguida, usado para acessar tal objeto ou uma matriz de tais objetos no espaço alocado (até que espaço é explicitamente desalocado)

35 Endeavour Jul 13 2014 at 13:42

Um ponteiro void é um ponteiro de objeto genérico e C suporta a conversão implícita de um tipo de ponteiro void para outros tipos, portanto, não há necessidade de um typecasting explicitamente.

Porém, se você quiser que o mesmo código funcione perfeitamente compatível em uma plataforma C ++, que não suporta conversão implícita, você precisa fazer o typecasting, então tudo depende da usabilidade.

30 swaps Apr 26 2013 at 23:43

O tipo retornado é void *, que pode ser convertido para o tipo desejado de ponteiro de dados para ser desreferenciável.

29 Jeyamaran Mar 17 2014 at 20:16

Depende da linguagem de programação e do compilador. Se você usar mallocem C, não há necessidade de digitar cast, pois ele digitará cast automaticamente. No entanto, se você estiver usando C ++, deverá digitar cast porque mallocretornará um void*tipo.

28 AugustKarlstrom Sep 04 2015 at 18:52

Na linguagem C, um ponteiro void pode ser atribuído a qualquer ponteiro, por isso você não deve usar um tipo de conversão. Se você quiser uma alocação "segura de tipo", posso recomendar as seguintes funções de macro, que sempre uso em meus projetos C:

#include <stdlib.h>
#define NEW_ARRAY(ptr, n) (ptr) = malloc((n) * sizeof *(ptr))
#define NEW(ptr) NEW_ARRAY((ptr), 1)

Com eles no lugar, você pode simplesmente dizer

NEW_ARRAY(sieve, length);

Para matrizes não dinâmicas, a terceira macro de função obrigatória é

#define LEN(arr) (sizeof (arr) / sizeof (arr)[0])

o que torna os loops de array mais seguros e convenientes:

int i, a[100];

for (i = 0; i < LEN(a); i++) {
   ...
}
18 StephenG Dec 05 2015 at 00:27

Pessoas acostumadas com GCC e Clang são mimadas. Não é tão bom lá fora.

Ao longo dos anos, fiquei bastante horrorizado com os compiladores incrivelmente antigos que fui obrigada a usar. Freqüentemente, as empresas e gerentes adotam uma abordagem ultraconservadora para alterar os compiladores e nem mesmo testam se um novo compilador (com melhor conformidade de padrões e otimização de código) funcionará em seu sistema. A realidade prática para desenvolvedores ativos é que quando você está codificando, você precisa cobrir suas bases e, infelizmente, lançar mallocs é um bom hábito se você não pode controlar qual compilador pode ser aplicado ao seu código.

Eu também sugeriria que muitas organizações aplicassem um padrão de codificação próprio e que esse deveria ser o método que as pessoas deveriam seguir, caso fosse definido. Na ausência de orientação explícita, costumo optar por mais propensos a compilar em todos os lugares, em vez da adesão servil a um padrão.

O argumento de que não é necessário sob os padrões atuais é bastante válido. Mas esse argumento omite os aspectos práticos do mundo real. Não codificamos em um mundo regido exclusivamente pelo padrão da época, mas pelos aspectos práticos do que gosto de chamar de "campo de realidade da gestão local". E isso é dobrado e distorcido mais do que o espaço-tempo jamais foi. :-)

YMMV.

Tendo a pensar em lançar malloc como uma operação defensiva. Nem bonito, nem perfeito, mas geralmente seguro. (Honestamente, se você não incluiu stdlib.h, então você terá muito mais problemas do que lançar malloc!).

16 Noname Aug 16 2017 at 19:25

Não, você não lança o resultado de malloc().

Em geral, você não transmite de ou paravoid * .

Uma razão típica apresentada para não o fazer é que o não cumprimento #include <stdlib.h>pode passar despercebido. Isso não é mais um problema por um longo tempo, já que o C99 tornava as declarações de função implícitas ilegais, portanto, se seu compilador estiver em conformidade com pelo menos C99, você receberá uma mensagem de diagnóstico.

Mas há uma razão muito mais forte para não introduzir conversões de ponteiro desnecessárias:

Em C, uma conversão de ponteiro quase sempre é um erro . Isso ocorre devido à seguinte regra ( §6.5 p7 em N1570, o rascunho mais recente para C11):

Um objeto deve ter seu valor armazenado acessado apenas por uma expressão lvalue que tem um dos seguintes tipos:
- um tipo compatível com o tipo efetivo do objeto,
- uma versão qualificada de um tipo compatível com o tipo efetivo do objeto,
- um tipo que é o tipo assinado ou não assinado correspondente ao tipo efetivo do objeto,
- um tipo que é o tipo assinado ou não assinado correspondente a uma versão qualificada do tipo efetivo do objeto,
- um tipo agregado ou de união que inclui um dos tipos acima mencionados entre seus membros (incluindo, recursivamente, um membro de um subagregado ou união contida), ou
- um tipo de caractere.

Isso também é conhecido como regra de aliasing estrita . Portanto, o código a seguir é um comportamento indefinido :

long x = 5;
double *p = (double *)&x;
double y = *p;

E, às vezes surpreendentemente, o seguinte também é:

struct foo { int x; };
struct bar { int x; int y; };
struct bar b = { 1, 2};
struct foo *p = (struct foo *)&b;
int z = p->x;

Às vezes, você não precisa ponteiros do elenco, mas dada a regra aliasing estrita , você tem que ter muito cuidado com ele. Portanto, qualquer ocorrência de um ponteiro lançado em seu código é um lugar que você deve verificar novamente para sua validade . Portanto, você nunca escreve um lançamento de ponteiro desnecessário.

tl; dr

Resumindo: como em C, qualquer ocorrência de conversão de ponteiro deve levantar uma bandeira vermelha para código que requer atenção especial, você nunca deve escrever conversões de ponteiro desnecessárias .


Notas laterais:

  • Existem casos em que você realmente precisa de um elenco void *, por exemplo, se você deseja imprimir um ponteiro:

    int x = 5;
    printf("%p\n", (void *)&x);
    

    O elenco é necessário aqui, porque printf()é uma função variável, portanto, as conversões implícitas não funcionam.

  • Em C ++, a situação é diferente. A conversão de tipos de ponteiro é algo comum (e correto) ao lidar com objetos de classes derivadas. Portanto, faz sentido que em C ++ a conversão de e para nãovoid * esteja implícita. C ++ tem todo um conjunto de diferentes sabores de fundição.

15 Kaz Mar 30 2016 at 07:23

Eu coloquei o elenco simplesmente para mostrar desaprovação do buraco feio no sistema de tipos, que permite que códigos como o seguinte snippet sejam compilados sem diagnóstico, mesmo que nenhum elenco seja usado para causar a conversão incorreta:

double d;
void *p = &d;
int *q = p;

Eu queria que isso não existisse (e não existe em C ++) e então eu lancei. Representa meu gosto e minha política de programação. Não estou apenas lançando um ponteiro, mas efetivamente, lançando uma votação e expulsando demônios da estupidez . Se não posso realmente expulsar a estupidez , pelo menos deixe-me expressar o desejo de fazê-lo com um gesto de protesto.

Na verdade, uma boa prática é envolver malloc(e amigos) com funções que retornam unsigned char *e, basicamente, nunca usar void *em seu código. Se você precisar de um ponteiro genérico para qualquer objeto, use um char *ou unsigned char *e tenha conversões em ambas as direções. O único relaxamento que pode ser tolerado, talvez, é usar funções como memsete memcpysem gesso.

No tópico de conversão e compatibilidade com C ++, se você escrever seu código para que seja compilado como C e C ++ (nesse caso, você terá que converter o valor de retorno de mallocao atribuí-lo a algo diferente de void *), você pode fazer uma tarefa muito útil coisa para você: você pode usar macros para conversão que se traduzem em conversões de estilo C ++ ao compilar como C ++, mas reduzem a uma conversão de C ao compilar como C:

/* In a header somewhere */
#ifdef __cplusplus
#define strip_qual(TYPE, EXPR) (const_cast<TYPE>(EXPR))
#define convert(TYPE, EXPR) (static_cast<TYPE>(EXPR))
#define coerce(TYPE, EXPR) (reinterpret_cast<TYPE>(EXPR))
#else
#define strip_qual(TYPE, EXPR) ((TYPE) (EXPR))
#define convert(TYPE, EXPR) ((TYPE) (EXPR))
#define coerce(TYPE, EXPR) ((TYPE) (EXPR))
#endif

Se você aderir a essas macros, uma simples greppesquisa em sua base de código por esses identificadores mostrará onde estão todos os seus elencos, para que você possa verificar se algum deles está incorreto.

Então, daqui para frente, se você compilar regularmente o código com C ++, ele irá forçar o uso de um elenco apropriado. Por exemplo, se você usar strip_qualapenas para remover um constou volatile, mas o programa mudar de tal forma que uma conversão de tipo esteja envolvida, você obterá um diagnóstico e terá que usar uma combinação de casts para obter a conversão desejada.

Para ajudá-lo a aderir a essas macros, o compilador GNU C ++ (não C!) Tem um belo recurso: um diagnóstico opcional que é produzido para todas as ocorrências de casts de estilo C.

     -Wold-style-cast (apenas C ++ e Objective-C ++)
         Avisa se um elenco de estilo antigo (estilo C) para um tipo não vazio é usado
         dentro de um programa C ++. Os casts de novo estilo (dynamic_cast,
         static_cast, reinterpret_cast e const_cast) são menos vulneráveis
         a efeitos indesejados e muito mais fáceis de pesquisar.

Se o seu código C compilar como C ++, você pode usar esta -Wold-style-castopção para descobrir todas as ocorrências da (type)sintaxe de conversão que podem se infiltrar no código e acompanhar esses diagnósticos substituindo-os por uma escolha apropriada entre as macros acima (ou um combinação, se necessário).

Este tratamento de conversões é a maior justificativa técnica independente para trabalhar em um "C limpo": o dialeto C e C ++ combinado, que por sua vez justifica tecnicamente a conversão do valor de retorno de malloc.

15 proski Jun 29 2016 at 14:30

Prefiro fazer o elenco, mas não manualmente. Meu favorito é usar macros g_newe g_new0do glib. Se glib não for usado, eu adicionaria macros semelhantes. Essas macros reduzem a duplicação de código sem comprometer a segurança do tipo. Se você errar o tipo, obterá uma conversão implícita entre ponteiros não vazios, o que causaria um aviso (erro em C ++). Se você esquecer de incluir o cabeçalho que define g_newe g_new0, receberá um erro. g_newe g_new0ambos usam os mesmos argumentos, ao contrário de mallocque leva menos argumentos do que calloc. Basta adicionar 0para obter memória inicializada com zero. O código pode ser compilado com um compilador C ++ sem alterações.

14 user877329 Jun 12 2015 at 22:23

A melhor coisa a fazer ao programar em C sempre que possível:

  1. Faça seu programa compilar através de um compilador C com todos os avisos ativados -Walle corrija todos os erros e avisos
  2. Certifique-se de que não haja variáveis ​​declaradas como auto
  3. Em seguida, compile-o usando um compilador C ++ com -Walle -std=c++11. Corrija todos os erros e avisos.
  4. Agora compile usando o compilador C novamente. Seu programa agora deve compilar sem qualquer aviso e conter menos bugs.

Este procedimento permite tirar vantagem da verificação estrita de tipo do C ++, reduzindo assim o número de bugs. Em particular, este procedimento força você a incluir stdlib.hou você obterá

malloc não foi declarado dentro deste escopo

e também o força a lançar o resultado de mallocou você obterá

conversão inválida de void*paraT*

ou qualquer que seja o seu tipo de alvo.

Os únicos benefícios de escrever em C em vez de C ++ que posso encontrar são

  1. C tem um ABI bem especificado
  2. C ++ pode gerar mais código [exceções, RTTI, modelos, polimorfismo de tempo de execução ]

Observe que os segundos contras devem, no caso ideal, desaparecer ao usar o subconjunto comum a C junto com o recurso polimórfico estático .

Para aqueles que consideram as regras estritas do C ++ inconvenientes, podemos usar o recurso C ++ 11 com tipo inferido

auto memblock=static_cast<T*>(malloc(n*sizeof(T))); //Mult may overflow...
12 Noname Sep 10 2015 at 22:58

Casting é apenas para C ++, não C. Caso você esteja usando um compilador C ++, é melhor alterá-lo para um compilador C.

10 Aashish Oct 03 2017 at 20:13

A conversão de malloc é desnecessária em C, mas obrigatória em C ++.

A fundição é desnecessária em C por causa de:

  • void * é automaticamente e seguramente promovido para qualquer outro tipo de ponteiro no caso de C.
  • Ele pode ocultar um erro se você se esquecer de incluí-lo <stdlib.h>. Isso pode causar travamentos.
  • Se ponteiros e inteiros tiverem tamanhos diferentes, você está ocultando um aviso ao lançar e pode perder partes do endereço retornado.
  • Se o tipo do ponteiro for alterado em sua declaração, também pode ser necessário alterar todas as linhas onde mallocé chamado e convertido.

Por outro lado, a transmissão pode aumentar a portabilidade de seu programa. ou seja, permite que um programa ou função C compile como C ++.

9 iec2011007 Jul 29 2015 at 12:53

O conceito por trás do ponteiro void é que ele pode ser convertido em qualquer tipo de dados, por isso malloc retorna void. Além disso, você deve estar ciente da conversão automática de tipos. Portanto, não é obrigatório lançar o ponteiro, embora você deva fazê-lo. Isso ajuda a manter o código limpo e ajuda a depurar

9 dhanagovindarajan Jul 18 2016 at 17:36

Um ponteiro void é um ponteiro genérico e C suporta a conversão implícita de um tipo de ponteiro void para outros tipos, portanto, não há necessidade de um typecasting explicitamente.

Porém, se você quiser que o mesmo código funcione perfeitamente compatível em uma plataforma C ++, que não suporta conversão implícita, você precisa fazer o typecasting, então tudo depende da usabilidade.

9 Mohit Nov 08 2016 at 17:09
  1. Como já foi dito, não é necessário para C, mas para C ++.

  2. Incluir o elenco pode permitir que um programa ou função C compile como C ++.

  3. Em C, isso é desnecessário, pois void * é promovido de forma automática e segura a qualquer outro tipo de ponteiro.

  4. Mas se você lançar então, pode ocultar um erro se você se esqueceu de incluir stdlib.h . Isso pode causar travamentos (ou, pior, não causar um travamento até bem mais tarde em alguma parte totalmente diferente do código).

    Porque stdlib.h contém o protótipo para malloc é encontrado. Na ausência de um protótipo para malloc, o padrão requer que o compilador C assuma que malloc retorna um int. Se não houver conversão, um aviso será emitido quando este inteiro for atribuído ao ponteiro; entretanto, com o elenco, esse aviso não é produzido, ocultando um bug.

4 RobertSsupportsMonicaCellio Jun 13 2020 at 02:20

Esta questão é objeto de abuso baseado em opinião.

Às vezes eu noto comentários como este:

Não lance o resultado de malloc

ou

Por que você não lança o resultado de malloc

em questões onde o OP usa casting. Os próprios comentários contêm um hiperlink para esta questão.

Isso é de qualquer maneira inadequada e incorreta também. Não há certo ou errado quando se trata realmente de um estilo de codificação próprio.


Por que isso está acontecendo?

É baseado em dois motivos:

  1. Esta questão é de fato baseada na opinião. Tecnicamente, a questão deveria ter sido encerrada com base na opinião anos atrás. Uma pergunta " Eu " ou " Não, " ou equivalente " Eu deveria " ou " Não deveria ", você simplesmente não pode responder com foco sem uma atitude de opinião própria. Uma das razões para fechar uma pergunta é porque ela "pode ​​levar a respostas baseadas em opiniões", como é bem mostrado aqui.

  2. Muitas respostas (incluindo o mais aparente e aceite resposta de @unwind ) são totalmente ou quase totalmente baseada em parecer (fe a "desorganização" misteriosa que seria adicionado ao seu código se você lançar ou repetir-se seria ruim) e mostrar uma tendência clara e focada de omitir o elenco. Eles discutem sobre a redundância do elenco de um lado, mas também e ainda pior argumentam para resolver um bug causado por um bug / falha da própria programação - não #include <stdlib.h>se alguém quiser usar malloc().


Quero trazer uma visão verdadeira de alguns pontos discutidos, com menos da minha opinião pessoal. Alguns pontos precisam ser observados especialmente:

  1. Uma questão tão suscetível a cair na própria opinião precisa de uma resposta com prós e contras neutros. Não apenas contras ou prós.

    Uma boa visão geral dos prós e contras está listada nesta resposta:

    https://stackoverflow.com/a/33047365/12139179

    (Eu pessoalmente considero isso, por esse motivo, a melhor resposta, até agora.)


  1. Uma razão encontrada para justificar a omissão do elenco é que o elenco pode esconder um bug.

    Se alguém usa um declarado implícito malloc()que retorna int(as funções implícitas foram retiradas do padrão desde C99) e sizeof(int) != sizeof(int*), como mostrado nesta questão

    Por que este código falha em segfault na arquitetura de 64 bits, mas funciona bem em 32 bits?

    o elenco esconderia um bug.

    Embora isso seja verdade, mostra apenas metade da história, já que a omissão do elenco seria apenas uma solução inovadora para um bug ainda maior - não incluindo stdlib.hdurante o uso malloc().

    Isso nunca será um problema sério, se você,

    1. Use um compilador compatível com C99 ou superior (o que é recomendado e deve ser obrigatório), e

    2. Não fique tão ausente para se esquecer de incluir stdlib.h, quando quiser usar malloc()em seu código, o que é um grande bug em si.


  1. Algumas pessoas discutem sobre a conformidade do código C com C ++, já que o elenco é obrigatório em C ++.

    Em primeiro lugar, para dizer em geral: Compilar código C com um compilador C ++ não é uma boa prática.

    C e C ++ são na verdade duas linguagens completamente diferentes com semânticas diferentes.

    Mas se você realmente deseja / precisa tornar o código C compatível com C ++ e vice-versa, use as opções do compilador em vez de qualquer elenco.

    Uma vez que o elenco é com tendência declarado como redundante ou mesmo prejudicial, quero me concentrar nessas questões, que dão boas razões pelas quais o elenco pode ser útil ou mesmo necessário:

    • https://stackoverflow.com/a/34094068/12139179

    • https://stackoverflow.com/a/36297486/12139179

    • https://stackoverflow.com/a/33044300/12139179


  1. A conversão pode não ser benéfica quando seu código, respectivamente o tipo do ponteiro atribuído (e com isso o tipo de conversão), muda, embora isso seja improvável na maioria dos casos. Então, você precisaria manter / alterar todos os casts também e, se tiver alguns milhares de chamadas para funções de gerenciamento de memória em seu código, isso pode realmente resumir e diminuir a eficiência da manutenção.

Resumo:

O fato é que o elenco é redundante pelo padrão C (já desde ANSI-C (C89 / C90)) se o ponteiro atribuído apontar para um objeto de requisito de alinhamento fundamental (que inclui a maioria de todos os objetos).

Você não precisa fazer o elenco, pois o ponteiro é alinhado automaticamente neste caso:

"A ordem e a contiguidade do armazenamento alocado por chamadas sucessivas para as funções align_alloc, calloc, malloc e realloc não são especificadas. O ponteiro retornado se a alocação for bem-sucedida está adequadamente alinhado para que possa ser atribuído a um ponteiro para qualquer tipo de objeto com um requisito fundamental de alinhamento e, em seguida, usado para acessar esse objeto ou uma série de tais objetos no espaço alocado (até que o espaço seja explicitamente desalocado). "

Fonte: C18, §7.22.3 / 1


"Um alinhamento fundamental é um alinhamento válido menor ou igual a _Alignof (max_align_t). Os alinhamentos fundamentais devem ser suportados pela implementação para objetos de todas as durações de armazenamento. Os requisitos de alinhamento dos seguintes tipos devem ser alinhamentos fundamentais:

- todos os tipos básicos atômicos, qualificados ou não qualificados;

- todos os tipos enumerados atômicos, qualificados ou não qualificados;

- todos os tipos de ponteiros atômicos, qualificados ou não qualificados;

- todos os tipos de matriz cujo tipo de elemento tem um requisito fundamental de alinhamento; 57)

- todos os tipos especificados na Cláusula 7 como tipos de objetos completos;

- todos os tipos de estrutura ou união cujos elementos têm tipos com requisitos de alinhamento fundamentais e nenhum dos elementos tem um especificador de alinhamento especificando um alinhamento que não seja um alinhamento fundamental.

  1. Conforme especificado em 6.2.1, a declaração posterior pode ocultar a declaração anterior. "

Fonte: C18, §6.2.8 / 2

No entanto, se você alocar memória para um objeto definido pela implementação do requisito de alinhamento estendido, o elenco será necessário.

Um alinhamento estendido é representado por um alinhamento maior que _Alignof (max_align_t). É definido pela implementação se quaisquer alinhamentos estendidos são suportados e as durações de armazenamento para as quais eles são suportados. Um tipo com um requisito de alinhamento estendido é um tipo superalinhado.58)

Fonte. C18, § 6.2.8 / 3

Todo o resto depende do caso de uso específico e da opinião de cada um.

Por favor, tome cuidado como você se educa.

Eu recomendo que você leia primeiro todas as respostas feitas até agora cuidadosamente (assim como seus comentários que podem apontar para uma falha) e então construa sua própria opinião se você ou não lançou o resultado de malloc()um caso específico.

Observe:

Não há uma resposta certa ou errada para essa pergunta. É uma questão de estilo e você mesmo decide o caminho que escolher (se você não for obrigado pela educação ou pelo trabalho, é claro). Esteja ciente disso e não se deixe enganar .


Última nota: votei recentemente para encerrar esta questão com base na opinião, o que é de fato necessário há anos. Se você obteve o privilégio de fechar / reabrir, gostaria de convidá-lo a fazer isso também.

pasignature Mar 05 2020 at 22:56

Para mim, a conclusão e a conclusão aqui é que a conversão mallocem C NÃO é totalmente necessária, mas se você lançar, isso não afetará, mallocpois mallocainda alocará para você o seu espaço de memória abençoado solicitado. Outra lição é a razão ou uma das razões pelas quais as pessoas fazem casting e isso é para permitir que compilem o mesmo programa em C ou C ++.

Pode haver outros motivos, mas outros motivos, quase certamente, o colocariam em sérios problemas mais cedo ou mais tarde.

ivan.ukr May 22 2020 at 14:03

Você pode, mas não precisa, converter em C. Você precisa converter se o código for compilado como C ++.