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?
NULL
. (é provavelmente por isso que o C ++ introduziunullptr
: o C ++ não permite lançamentos implícitos de ponteiros)Respostas:
Não ; você não transmite o resultado, pois:
void *
é automaticamente e com segurança promovido para qualquer outro tipo de ponteiro nesse caso.<stdlib.h>
. Isso pode causar falhas (ou, pior, não causar uma falha até muito mais tarde em alguma parte totalmente diferente do código). Considere o que acontece se ponteiros e números inteiros tiverem tamanhos diferentes; você está ocultando um aviso ao transmitir e pode perder partes do seu endereço retornado. Nota: a partir de C99, as funções implícitas passaram de C, e esse ponto não é mais relevante, pois não há suposição automática de que as funções não declaradas retornamint
.Como esclarecimento, observe que eu disse "você não lança", e não "você não precisa transmitir". Na minha opinião, é um fracasso em incluir o elenco, mesmo se você acertar. Simplesmente não há benefícios em fazê-lo, mas vários riscos em potencial, e a inclusão do elenco indica que você não conhece os riscos.
Observe também, como os comentaristas apontam, que o acima mencionado fala sobre C direto, não C ++. Eu acredito firmemente em C e C ++ como linguagens separadas.
Para adicionar ainda mais, seu código repete desnecessariamente as informações de tipo (
int
) que podem causar erros. É melhor des-referenciar o ponteiro que está sendo usado para armazenar o valor de retorno e "bloquear" os dois juntos:Isso também move o
length
para a frente para aumentar a visibilidade e descarta os parênteses redundantessizeof
; 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 detalhado. Lembre-se:sizeof
não é uma função! :)Embora mover
length
para a frente possa aumentar a visibilidade em alguns casos raros, também se deve prestar atenção que, no caso geral, deve ser melhor escrever a expressão como:Como manter o
sizeof
primeiro, neste caso, garante que a multiplicação seja feita com pelo menossize_t
matemática.Compare:
malloc(sizeof *sieve * length * width)
vs.malloc(length * width * sizeof *sieve)
o segundo pode exceder olength * width
quandowidth
elength
são tipos menores quesize_t
.fonte
int x = (int) 12;
apenas para esclarecer as coisas?(int)12
não é comparável.12
é umint
, o elenco simplesmente não faz nada. O retval demalloc()
isvoid *
, não o tipo de ponteiro convertido. (Se não forvoid *
. Assim, a analogia com(int)12
seria(void*)malloc(…)
o que ninguém está discutindo.)Em C, você não precisa converter o valor de retorno de
malloc
. O ponteiro para anular retornado pormalloc
é convertido automaticamente para o tipo correto. No entanto, se você deseja que seu código seja compilado com um compilador C ++, uma conversão é necessária. Uma alternativa preferida entre a comunidade é usar o seguinte:o que, além disso, libera você de se preocupar em alterar o lado direito da expressão, se você alterar o tipo de
sieve
.Elencos são ruins, como as pessoas apontaram. Especialmente ponteiro lança.
fonte
malloc(length * sizeof *sieve)
isso parecesizeof
uma variável - então eu acho quemalloc(length * sizeof(*sieve))
é mais legível.malloc(length * (sizeof *sieve))
mais legível ainda. NA MINHA HUMILDE OPINIÃO.()
questão de lado, observe que o estilo sugerido mudou de ordem., Considere quando a contagem de elementos é calculada comolength*width
, manter osizeof
primeiro nesse caso garante que a multiplicação seja feita com pelo menossize_t
matemática. Comparemalloc(sizeof( *ptr) * length * width)
vs.malloc(length * width * sizeof (*ptr))
- o segundo pode estourarlength*width
quandowidth,length
são tipos menores quesize_t
.malloc(sizeof *sieve * length)
static_cast>()
(oureinterpret_cast<>()
) não é compatível com qualquer dialeto do C.Você não fundido, porque:
type *
versustype **
.#include
um arquivo de cabeçalho apropriado perde a floresta para as árvores . É o mesmo que dizer "não se preocupe com o fato de você ter falhado em pedir ao compilador para reclamar por não ter visto protótipos - que o irritante stdlib.h é a coisa realmente importante a lembrar!"malloc()
bugs são detectados muito mais rapidamente quando há um lançamento. Assim como as asserções, as anotações que revelam a intenção diminuem os erros.fonte
.c
/.cpp
para compilar como ambos não é útil com muita frequência, mas um caso é adicionarthrow
suporte ao C ++ quando compilado com o compilador C ++ (masreturn -1;
quando compilado com o compilador C ou o que for).malloc
chamada:char **foo = malloc(3*sizeof(*foo));
se for uma prova completa: 3 ponteiros para ponteiros char. então faça um loop e façafoo[i] = calloc(101, sizeof(*(foo[i])));
. Aloque uma matriz de 101 caracteres, inicializada ordenadamente em zeros. Não é necessário elenco. altere a declaração paraunsigned char
ou qualquer outro tipo, para que o assunto, e você ainda está bomstruct Zebra *p; ... p=malloc(sizeof struct Zebra);
o malloc não pode evitar informações duplicadas sobre o tipo de p, mas nem o compilador nem a inspeção de código local detectariam qualquer problema se um tipo fosse alterado, mas o outro não. Altere o código parap=(struct Zebra*)malloc(sizeof struct Zebra);
e o compilador gritará se o tipo de elenco não corresponderp
e a inspeção local irá revelar ... #Como outros declararam, 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:
Dessa forma, você ainda pode escrevê-lo de uma maneira muito compacta:
e ele será compilado para C e C ++.
fonte
new
na definição de C ++?new
você deve usardelete
e se você usar,malloc()
você devefree()
. Nunca os misture.NEW
provavelmente é uma má ideia, pois o recurso nunca é retornado usandodelete
(ouDELETE
), para que você misture seu vocabulário. Em vez disso, nomeá-loMALLOC
, ou melhorCALLOC
, nesse caso, faria mais sentido.De Wikipedia :
Embora o malloc sem conversão seja o método preferido e os programadores mais experientes o escolham , você deve usar o que quiser ter conhecimento dos problemas.
ou seja: Se você precisar compilar o programa C como C ++ (embora seja uma linguagem separada), deverá converter o resultado do uso
malloc
.fonte
malloc()
chamada " significa? Você poderia dar um exemplo?p = malloc(sizeof(*p) * count)
idioma capta alterações no tipo automaticamente, para que você não precise receber avisos e alterar nada. Portanto, essa não é uma vantagem real versus a melhor alternativa para não transmitir.Em C, você pode converter implicitamente um
void
ponteiro em qualquer outro tipo de ponteiro, para que uma conversão não seja necessária. O uso de um pode sugerir ao observador casual que existe alguma razão pela qual ele é necessário, o que pode ser enganoso.fonte
Você não lança o resultado do malloc, porque isso adiciona uma confusão inútil ao seu código.
A razão mais comum pela qual as pessoas lançam o resultado do malloc é porque não têm certeza sobre como a linguagem C funciona. Esse é um sinal de alerta: se você não sabe como funciona um mecanismo de linguagem específico, não adivinhe. Procure ou pergunte no Stack Overflow.
Alguns comentários:
Um ponteiro nulo pode ser convertido para / de qualquer outro tipo de ponteiro sem uma conversão explícita (C11 6.3.2.3 e 6.5.16.1).
C ++, no entanto, 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 ++, deve usarnew
e 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 classificar os dois padrões de idioma com o mesmo código, porque eles não são compatíveis.
Se um compilador C não conseguir encontrar uma função porque você esqueceu de incluir o cabeçalho, você receberá um erro do compilador / vinculador sobre isso. Portanto, se você esqueceu de incluir
<stdlib.h>
isso não é nada demais, não poderá criar 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 comportamento perigoso. Porque nesse padrão antigo, funções sem um protótipo visível convertiam implicitamente o tipo de retorno emint
. A transmissão explícita do resultado do malloc ocultaria esse bug.Mas isso é realmente um problema. Você não está usando um computador de 25 anos, então por que você usaria um compilador de 25 anos?
fonte
Em C, você obtém uma conversão implícita de
void *
para qualquer outro ponteiro (dados).fonte
Lançando o valor retornado por
malloc()
Não é necessário converter agora, mas eu gostaria de acrescentar um ponto que parece que ninguém apontou:Antigamente, ou seja, antes do ANSI C fornecer o
void *
tipo genérico de ponteiros,char *
é o tipo para esse uso. Nesse caso, o elenco pode desligar os avisos do compilador.Referência: C FAQ
fonte
Apenas adicionando minha experiência, estudando engenharia da computação, vejo que os dois ou três professores que eu vi escrevendo em C sempre lançam malloc, mas o que eu perguntei (com um imenso currículo e compreensão de C) me disse que é absolutamente desnecessário, mas costumava ser absolutamente específico e levar os alunos à mentalidade de serem absolutamente específicos. Essencialmente, a conversão não muda nada em como funciona, faz exatamente o que diz, aloca memória e a conversão não afeta, você obtém a mesma memória e, mesmo se a converter para outra coisa por engano (e de alguma forma evitar o compilador) erros) C acessará da mesma maneira.
Edit: Casting tem um certo ponto. Quando você usa a notação de matriz, o código gerado precisa saber quantos lugares de memória precisa avançar para alcançar o início do próximo elemento, isso é obtido através da conversão. Dessa forma, você sabe que, para um dobro, você avança 8 bytes, enquanto para um int, você avança 4, e assim por diante. Portanto, não tem efeito se você usar a notação de ponteiro; na notação de matriz, isso se torna necessário.
fonte
p = malloc(sizeof *p * n);
é tão simples e melhor.Não é obrigatório converter os resultados de
malloc
, pois ele retornavoid*
e avoid*
pode ser apontado para qualquer tipo de dados.fonte
Um ponteiro nulo é um ponteiro de objeto genérico e C suporta conversão implícita de um tipo de ponteiro nulo para outros tipos, portanto, não há necessidade de lançá-lo explicitamente.
No entanto, se você deseja que o mesmo código funcione perfeitamente compatível em uma plataforma C ++, que não suporta conversão implícita, é necessário fazer a conversão de tipo, para que tudo dependa da usabilidade.
fonte
malloc
e amigos em C ++ é um bom sinal de aviso de que merece atenção especial (ou reescrita em C).void *
, portanto,void *
é insuficiente para armazenar bem um ponteiro de função.Isto é o que o manual de referência da biblioteca C GNU diz:
E, de fato, o padrão ISO C11 (p347) diz o seguinte:
fonte
O tipo retornado é nulo *, que pode ser convertido no tipo de ponteiro de dados desejado para ser desreferenciável.
fonte
void*
pode ser convertido para o tipo desejado, mas não é necessário fazê-lo, pois ele será convertido automaticamente. Portanto, o elenco não é necessário e, de fato, indesejável pelas razões mencionadas nas respostas de maior pontuação.No idioma C, um ponteiro nulo pode ser atribuído a qualquer ponteiro, e é por isso que você não deve usar uma conversão de tipo. Se você deseja alocação "tipo seguro", posso recomendar as seguintes funções de macro, que eu sempre uso nos meus projetos em C:
Com estes no lugar, você pode simplesmente dizer
Para matrizes não dinâmicas, a terceira macro de função obrigatória é
o que torna os loops de matriz mais seguros e mais convenientes:
fonte
malloc()
um.void*
para / de um ponteiro de função pode perder informações, de modo que "um ponteiro nulo pode ser atribuído a qualquer ponteiro" é um problema nesses casos. Atribuir umvoid*
, demalloc()
para qualquer ponteiro de objeto não é um problema.do
comentário do loop está relacionado às macros que envolvem um loop e ainda estão se perguntando sobre a questão do título. Removendo esse comentário. Também derrubará este aqui mais tarde.Depende da linguagem de programação e do compilador. Se você usar
malloc
C, não há necessidade de digitar a conversão, pois ela digitará automaticamente a conversão. No entanto, se você estiver usando C ++, digite cast porquemalloc
retornará umvoid*
tipo.fonte
Pessoas acostumadas a GCC e Clang são mimadas. Não é tão bom assim por aí.
Fiquei bastante horrorizado ao longo dos anos pelos compiladores incrivelmente envelhecidos que fui obrigado a usar. Freqüentemente, empresas e gerentes adotam uma abordagem ultra-conservadora para alterar os compiladores e nem sequer testam se um novo compilador (com melhor conformidade com os padrões e otimização de código) funcionará em seu sistema. A realidade prática para os desenvolvedores que trabalham é que, quando você está codificando, 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.
Sugiro também que muitas organizações apliquem seus próprios padrões de codificação e que deve ser o método que as pessoas seguem se for definido. Na ausência de orientação explícita, tenho a tendência de 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 do dia, mas pelas práticas do que eu gosto de chamar de "campo da realidade da administração local". E isso é torcido e distorcido mais do que o tempo espacial jamais esteve. :-)
YMMV.
Costumo pensar em lançar malloc como uma operação defensiva. Não é bonito, não é perfeito, mas geralmente seguro. (Honestamente, se você não incluiu stdlib.h, você tem muito mais problemas do que lançar malloc!).
fonte
Eu coloquei no elenco simplesmente para mostrar a desaprovação do buraco feio no sistema de tipos, que permite que códigos como o seguinte trecho sejam compilados sem diagnóstico, mesmo que nenhum elenco seja usado para provocar uma conversão ruim:
Eu gostaria que isso não existisse (e não existe em C ++) e, por isso, fiz a transmissão. Representa meu gosto e minha política de programação. Não estou apenas lançando um ponteiro, mas efetivamente, votando e expulsando demônios da estupidez . Se eu realmente não posso expulsar a estupidez , pelo menos deixe-me expressar o desejo de fazê-lo com um gesto de protesto.
De fato, uma boa prática é agrupar
malloc
(e amigos) com funções que retornamunsigned char *
e basicamente nunca usarvoid *
em seu código. Se você precisar de um ponteiro genérico para qualquer objeto, use achar *
ouunsigned char *
e tenha projeções nas duas direções. O único relaxamento que pode ser tolerado, talvez, é usar funções comomemset
ememcpy
sem elencos.No tópico fundição e compatibilidade com C ++, se você escrever seu código para que ele seja compilado tanto em C quanto em C ++ (nesse caso, você precisará converter o valor de retorno
malloc
ao atribuí-lo a algo diferente devoid *
), poderá fazer uma ajuda muito útil coisa para você: você pode usar macros para conversão que convertem para conversões no estilo C ++ ao compilar como C ++, mas reduzem para uma conversão C ao compilar como C:Se você aderir a essas macros, uma simples
grep
pesquisa na sua base de códigos por esses identificadores mostrará onde estão todas as suas transmissões, para que você possa verificar se alguma delas está incorreta.A partir de agora, se você compilar regularmente o código com C ++, ele aplicará o uso de uma conversão apropriada. Por exemplo, se você usar
strip_qual
apenas para remover umconst
ouvolatile
, mas o programa mudar de tal maneira que uma conversão de tipo esteja envolvida, você receberá um diagnóstico e precisará usar uma combinação de conversões para obter a conversão desejada.Para ajudá-lo a aderir a essas macros, o compilador GNU C ++ (não C!) Possui um recurso bonito: um diagnóstico opcional produzido para todas as ocorrências de conversões no estilo C.
Se o seu código C for compilado como C ++, você poderá usar esta
-Wold-style-cast
opção para descobrir todas as ocorrências da(type)
sintaxe de conversão que podem surgir no código e acompanhar esses diagnósticos, substituindo-o por uma escolha apropriada dentre as macros acima (ou um combinação, se necessário).Esse tratamento de conversões é a maior justificativa técnica autônoma para trabalhar em um "C limpo": o dialeto C e C ++ combinado, que por sua vez tecnicamente justifica lançar o valor de retorno de
malloc
.fonte
A melhor coisa a fazer ao programar em C sempre que possível:
-Wall
e corrija todos os erros e avisosauto
-Wall
e-std=c++11
. Corrija todos os erros e avisos.Este procedimento permite tirar proveito da verificação estrita do tipo C ++, reduzindo assim o número de bugs. Em particular, este procedimento obriga a incluir
stdlib.h
ou você receberáe também obriga a lançar o resultado
malloc
ou você receberá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
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
fonte
gcc -c c_code.c
), o código C ++ como C ++ (por exemplog++ -c cpp_code.cpp
) e vinculá-los (por exemplo,gcc c_code.o cpp_code.o
ou vice-versa, dependendo das dependências do projeto). Agora deve haver nenhuma razão para privar-se de quaisquer características agradáveis de uma ou outra língua ...p = malloc(sizeof(*p));
, o que não precisa ser alterado em primeiro lugar sep
mudar para um nome de tipo diferente. A "vantagem" proposta da conversão é que você recebe um erro de compilação sep
for do tipo errado, mas é ainda melhor se apenas funcionar.Não, você não lança o resultado de
malloc()
.Em geral, você não transmite de ou para
void *
.Um motivo típico para não fazer isso é que a falha em
#include <stdlib.h>
passar despercebida. Isso não é mais um problema há muito tempo, pois o C99 tornou ilegal a declaração implícita de funções . Portanto, se o seu compilador estiver em conformidade com pelo menos o C99, você receberá uma mensagem de diagnóstico.Mas há uma razão muito mais forte para não introduzir projeções desnecessárias de ponteiros:
Em C, uma conversão de ponteiro é quase sempre um erro . Isso ocorre devido à seguinte regra ( §6.5 p7 no N1570, o último rascunho do C11):
Isso também é conhecido como regra estrita de alias . Portanto, o código a seguir é um comportamento indefinido :
E, às vezes surpreendentemente, também é o seguinte:
À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 convertido em seu código é um local em que você deve verificar novamente sua validade . Portanto, você nunca escreve um elenco desnecessário de ponteiros.
tl; dr
Em poucas palavras: como em C, qualquer ocorrência de vazamento de ponteiro deve exibir uma bandeira vermelha para código que requer atenção especial, você nunca deve escrever vazamentos desnecessários de ponteiro.
Notas laterais:
Há casos em que você realmente precisa de uma conversão
void *
, por exemplo, se deseja imprimir um ponteiro: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ão
void *
esteja implícita. O C ++ possui um conjunto completo de diferentes sabores de conversão.fonte
Eu prefiro fazer o elenco, mas não manualmente. O meu favorito é usar
g_new
eg_new0
macros de 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, obteria uma conversão implícita entre ponteiros não nulos, o que causaria um aviso (erro em C ++). Se você esquecer de incluir o cabeçalho que defineg_new
eg_new0
, você receberá um erro.g_new
eg_new0
ambos têm os mesmos argumentos, ao contrário demalloc
que leva menos argumentos quecalloc
. Basta adicionar0
para obter memória inicializada com zero. O código pode ser compilado com um compilador C ++ sem alterações.fonte
A conversão é apenas para C ++ e não C. No caso de você estar usando um compilador C ++, é melhor alterá-lo para o compilador C.
fonte
O conceito por trás do ponteiro nulo é que ele pode ser convertido para qualquer tipo de dados, por isso o malloc retorna nulo. 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. Ajuda a manter o código limpo e ajuda na depuração
fonte
Um ponteiro nulo é um ponteiro genérico e C suporta conversão implícita de um tipo de ponteiro nulo para outros tipos, portanto, não há necessidade de lançá-lo explicitamente.
No entanto, se você deseja que o mesmo código funcione perfeitamente compatível em uma plataforma C ++, que não suporta conversão implícita, é necessário fazer a conversão de tipo, para que tudo dependa da usabilidade.
fonte
Como já foi dito, não é necessário para C, mas para C ++.
A inclusão do elenco pode permitir que um programa ou função C seja compilado como C ++.
Em C, isso é desnecessário, pois o void * é promovido de forma automática e segura para qualquer outro tipo de ponteiro.
Mas se você converter, poderá ocultar um erro se você esquecer de incluir stdlib.h . Isso pode causar falhas (ou, pior, não causar uma falha até muito mais tarde em alguma parte totalmente diferente do código).
Como stdlib.h contém o protótipo para malloc, foi encontrado. Na ausência de um protótipo para malloc, o padrão exige que o compilador C assuma malloc retornando um int. Se não houver conversão, um aviso será emitido quando esse número inteiro for atribuído ao ponteiro; no entanto, com o elenco, esse aviso não é produzido, ocultando um bug.
fonte
A conversão do malloc é desnecessária em C, mas obrigatória em C ++.
A transmissão é desnecessária em C devido a:
void *
é promovido de forma automática e segura para qualquer outro tipo de ponteiro no caso de C.<stdlib.h>
. Isso pode causar falhas.malloc
é chamado e convertido.Por outro lado, a transmissão pode aumentar a portabilidade do seu programa. isto é, permite que um programa ou função C compile como C ++.
fonte
Para mim, a conclusão e conclusão aqui é que a transmissão
malloc
em C NÃO é totalmente necessária, mas, se você transmitir, isso não afetará,malloc
poismalloc
ainda alocará o espaço de memória abençoado solicitado. Outra coisa que leva para casa é a razão ou uma das razões pelas quais as pessoas fazem a transmissão e isso lhes permite compilar o mesmo programa em C ou C ++.Pode haver outras razões, mas outras, quase certamente, o colocariam em sérios problemas mais cedo ou mais tarde.
fonte
Você pode, mas não precisa converter em C. Você deve converter se esse código for compilado como C ++.
fonte