casts - cast c c++




Eu conjuro o resultado de malloc? (18)

Nesta questão , alguém sugeriu em um comment que eu não deveria lançar 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?


Não, você não joga o resultado de malloc().

Em geral, você não joga para ou devoid * .

Uma razão típica dada para não fazê-lo é que a falha #include <stdlib.h>pode passar despercebida. Isso não é mais um problema há muito tempo, pois o C99 tornou 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 lançamentos desnecessários:

Em C, um elenco de ponteiro é quase sempre um erro . Isso ocorre devido à seguinte regra ( §6.5 p7 no N1570, o último rascunho para C11):

Um objeto deve ter seu valor armazenado acessado apenas por uma expressão lvalue que tenha 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 é assinado ou não assinado correspondente ao tipo efetivo do objeto,
- um tipo que é assinado ou não assinado correspondente a uma versão qualificada do tipo efetivo do objeto,
- um tipo de agregação ou união que inclui um dos tipos acima mencionados entre seus membros (incluindo, recursivamente, um membro de uma união subagregada ou contida), ou
- um tipo de caractere.

Isso também é conhecido como a regra de aliasing restrita . 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 lançar ponteiros, 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 para verificar sua validade . Portanto, você nunca escreve um elenco de ponteiro desnecessário.

tl; dr

Resumindo: como em C, qualquer ocorrência de um elenco de ponteiro deve levantar uma bandeira vermelha para o código que requer atenção especial, você nunca deve escrever lançamentos de ponteiro desnecessários .

Notas laterais:

  • Há casos em que você realmente precisa de um elenco para void *, por exemplo, se você quiser imprimir um ponteiro:

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

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

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


  1. Como outro afirmou, não é necessário para C, mas para C ++.

  2. Incluindo o elenco pode permitir que um programa C ou função para compilar como C ++.

  3. Em C isso é desnecessário, pois void * é promovido automaticamente e com segurança para qualquer outro tipo de ponteiro.

  4. Mas se você converter, ele pode ocultar um erro se você esqueceu de incluir stdlib.h . Isso pode causar falhas (ou, pior ainda, não causar uma falha até 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 presuma que malloc retorne um int. Se não houver conversão, um aviso será emitido quando esse inteiro for atribuído ao ponteiro; no entanto, com o elenco, esse aviso não é produzido, ocultando um bug.


As pessoas acostumadas ao GCC e ao Clang são mimadas. Não é tão bom lá fora.

Eu tenho ficado bastante horrorizado ao longo dos anos pelos compiladores incrivelmente idosos que eu fui obrigado a usar. Frequentemente, as empresas e os gerentes adotam uma abordagem ultraconservadora para alterar os compiladores e nem 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 os desenvolvedores que trabalham é 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 aplicam um padrão de codificação próprio e que esse deve ser o método que as pessoas seguem se for definido. Na ausência de orientação explícita, tenho tendência a compilar em todos os lugares, em vez de aderir 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ós não codificamos em um mundo governado exclusivamente pelo padrão do dia, mas pelos aspectos práticos do que eu gosto de chamar de "campo da realidade da administração local". E isso é mais torcido e torcido do que o tempo no espaço. :-)

YMMV.

Eu costumo pensar em lançar malloc como uma operação defensiva. Não bonita, não perfeita, mas geralmente segura. (Honestamente, se você não incluiu stdlib.h, então você tem muito mais problemas do que lançar malloc!).


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

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

Referência: C FAQ


Da Wikipedia

Vantagens para fundição

  • Incluindo o elenco pode permitir que um programa C ou função para compilar como C ++.

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

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

Desvantagens de fundição

  • Sob o padrão ANSI C, a conversão é redundante.

  • Adicionar o lançamento pode mascarar a falha para 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 retorne um int. Se não houver conversão, um aviso será emitido quando esse inteiro for atribuído ao ponteiro; no entanto, com o elenco, esse aviso não é produzido, ocultando um bug. Em determinadas arquiteturas e modelos de dados (como LP64 em sistemas de 64 bits, em que long e ponteiros são 64 bits e int é 32 bits), esse erro pode resultar em um comportamento indefinido, já que o malloc declarado implicitamente retorna um valor de 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 em esmagamento da pilha. É menos provável que essa questão passe despercebida nos compiladores modernos, pois eles uniformemente produzem avisos de que uma função não declarada foi usada, portanto, um aviso ainda aparecerá. Por exemplo, o comportamento padrão do GCC é mostrar um aviso que diz "declaração implícita incompatível da função interna", independentemente de o elenco estar presente ou não.

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

Embora o malloc sem elenco seja o método preferido e os programadores mais experientes o escolham , você deve usar o que quiser, tendo conhecimento dos problemas.

ie: Se você precisa compilar o programa C como C ++ (embora esses sejam idiomas separados) você deve usar malloc com casting.


Depende da linguagem de programação e do compilador. Se você usar malloc em C, não há necessidade de digitar convertê-lo, pois ele será automaticamente convertido. No entanto, se você estiver usando C ++, digite cast porque malloc retornará um tipo void* .


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


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


Isto é o que o manual GNU C Library Reference diz:

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

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

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


Não é obrigatório lançar os resultados de malloc , já que ele retorna void* , e um void* pode ser apontado para qualquer tipo de dado.


O tipo retornado é void *, que pode ser convertido no tipo de ponteiro de dados desejado para ser desreferenciavel.


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

No entanto, se você quiser que o mesmo código funcione perfeitamente compatível em uma plataforma C ++, que não suporta conversão implícita, será necessário fazer o typecasting, então tudo depende da usabilidade.


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

A razão mais comum pela qual as pessoas atribuem o resultado de malloc é porque elas não têm certeza sobre como a linguagem C funciona. Isso é um sinal de alerta: se você não sabe como um determinado mecanismo de linguagem funciona, não adivinhe. Pesquise ou pergunte no .

Alguns comentários:

  • Um ponteiro vazio 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).

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

    Se você precisar suportar C e C ++ com o mesmo código-fonte, use as opções do compilador para marcar as diferenças. Não tente estabelecer 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 puder encontrar uma função porque você esqueceu de incluir o cabeçalho, você receberá um erro de compilador / linker sobre isso. Então, se você esqueceu de incluir o <stdlib.h> isso não é nada demais, você não poderá construir seu programa.

  • Em antigos compiladores que seguem uma versão do padrão com mais de 25 anos, esquecer de incluir <stdlib.h> resultaria em um comportamento perigoso. Como nesse padrão antigo, as funções sem um protótipo visível implicitamente converteram o tipo de retorno para int . Lançar o resultado de malloc explicitamente esconderia esse bug.

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


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

Casting é desnecessário em C devido a:

  • void * é promovido automaticamente e com segurança para qualquer outro tipo de ponteiro no caso de C.
  • Pode ocultar um erro se você se esqueceu de incluir <stdlib.h>. Isso pode causar falhas.
  • Se os ponteiros e números inteiros tiverem tamanhos diferentes, você estará ocultando um aviso por meio de conversão e poderá perder alguns bits do endereço retornado.
  • Se o tipo do ponteiro é alterado na sua declaração, também pode ser necessário alterar todas as linhas onde mallocé chamado e lançado.

Por outro lado, o casting pode aumentar a portabilidade do seu programa. isto é, permite que um programa ou função C seja compilado como C ++.


A melhor coisa a fazer quando estiver programando em C sempre que for possível:

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

Este procedimento permite que você tire proveito da verificação de tipo estrito do C ++, reduzindo assim o número de erros. Em particular, este procedimento o obriga a incluir stdlib.hou você receberá

malloc não foi declarado dentro deste escopo

e também obriga você a lançar o resultado de mallocou você receberá

conversão inválida de void*paraT*

ou o que nunca o seu tipo de alvo é.

Os únicos benefícios de escrever em C em vez de C ++ eu 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 quando se utiliza o subconjunto comum a C junto com o recurso polimórfico estático .

Para aqueles que acham as regras rígidas do C ++ inconvenientes, podemos usar o recurso C ++ 11 com o tipo inferido

auto memblock=static_cast<T*>(malloc(n*sizeof(T))); //Mult may overflow...

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


Eu prefiro fazer o elenco, mas não manualmente. Meu favorito é usando g_newe g_new0macros de glib. Se o glib não for usado, adicionarei macros semelhantes. Essas macros reduzem a duplicação de código sem comprometer a segurança do tipo. Se você obtiver o tipo errado, você obterá 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 define g_newe g_new0receberá um erro. g_newe g_new0ambos levam os mesmos argumentos, ao contrário mallocdisso, menos argumentos do que calloc. Basta adicionar 0para obter memória inicializada zero. O código pode ser compilado com um compilador C ++ sem alterações.


O conceito por trás do apontador void é que ele pode ser convertido para qualquer tipo de dado e é por isso que malloc retorna void. Além disso, você deve estar ciente do typecasting automático. 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





casting