Qual é o sentido do malloc (0)?


Respostas:


121

De acordo com as especificações, malloc (0) retornará "um ponteiro nulo ou um ponteiro exclusivo que pode ser passado com sucesso para free ()".

Isso basicamente permite que você aloque nada, mas ainda passe a variável "artist" para uma chamada para free () sem se preocupar. Para fins práticos, é praticamente o mesmo que fazer:

artist = NULL;

51
Pessoalmente, acho que definir NULL é uma estratégia melhor entre plataformas, pois free () é garantido (por especificação) para funcionar bem no NULL como entrada.
Reed Copsey

2
Conforme mencionado por C. Ross, algumas plataformas, tecnicamente, podem retornar um ponteiro aqui (que é um "ponteiro exclusivo que pode ser transmitido gratuitamente"), mas se você estiver tratando isso como um caractere *, poderá fornecer uma caractere inválido e não terminado. Pode ser perigoso confiar nisso em situações de plataforma cruzada.
Reed Copsey

9
Realmente desejo as especificações diria "passou com segurança para realloc" bem -.-
hanshenrik

1
@NSAddict "estrutura vazia, onde sizeof retornará 0", forneça um exemplo, soa como uma extensão de idioma.
chux - Restabelece Monica 12/09

1
@hanshenrik Quem disse que você não pode? realloc()permite passar qualquer ponteiro válido retornado por malloc(). Deve ser suficiente.
glglgl

51

O padrão C (C17 7.22.3 / 1) diz:

Se o tamanho do espaço solicitado for zero, o comportamento é definido como implementação: um ponteiro nulo é retornado ou o comportamento é como se o tamanho tivesse algum valor diferente de zero, exceto que o ponteiro retornado não deve ser usado para acessar um objeto.

Portanto, malloc(0)poderia retornar NULLou um ponteiro válido que não pode ser desreferenciado . Em ambos os casos, é perfeitamente válido chamá free()-lo.

Eu realmente não acho que malloc(0)tenha muita utilidade, exceto nos casos em que malloc(n)é chamado em um loop, por exemplo, e npode ser zero.

Olhando o código no link, acredito que o autor tenha dois conceitos errados:

  • malloc(0)retorna sempre um ponteiro válido e
  • free(0) é ruim.

Portanto, ele garantiu que artistoutras variáveis ​​sempre tivessem algum valor "válido". O comentário diz muito: // these must always point at malloc'd data.


10
O fato de ser dependente da implementação o torna mais ou menos completamente inútil - esse é um dos bits mais ruins do padrão C, e vários comitês de padrões (por exemplo, PJ Plauger) se queixaram.

10
Concordo. Se malloc(0)retornado um ponteiro válido, malloc()retornar NULLsignifica sempre "falha" e 0não é mais um caso especial, que é mais consistente.
Alok Singhal

1
Como as circunstâncias de mallocfalha na obtenção de memória são definidas pela implementação, uma implementação poderia simplesmente definir que as alocações tamanho 0 são sempre insatisfatórias ( ENOMEM) e agora o malloc(0)retorno 0 (com errno==ENOMEM) é consistente. :-)
R .. GitHub Pare de ajudar o gelo

6
Você pode reallocum ponteiro retornado por malloc(0)? Você pode realloc((char*)NULL)?
Braden Best

3
@ Braden Melhor Sim para ambos.
chux - Restabelece Monica

11

O comportamento do malloc (0) é específico da implementação. A biblioteca pode retornar NULL ou ter o comportamento malloc regular, sem memória alocada. O que quer que faça, deve ser documentado em algum lugar.

Geralmente, ele retorna um ponteiro válido e exclusivo, mas NÃO deve ser desreferenciado. Observe também que PODE consumir memória, embora não tenha realmente alocado nada.

É possível realocar um ponteiro não nulo malloc (0).

Ter um malloc (0) literalmente não é muito útil. É usado principalmente quando uma alocação dinâmica é zero byte e você não se importou em validá-la.


9
malloc()deve manter "informações de manutenção" em algum lugar (esse tamanho do bloco alocado, por exemplo, e outros dados auxiliares). Portanto, se malloc(0)não retornar NULL, ele usará memória para armazenar essas informações e, se não for free()d, constituirá um vazamento de memória.
Alok Singhal

As implementações do Malloc executam a manutenção de registros, o que poderia adicionar uma certa quantidade de dados por ponteiro retornado além do tamanho solicitado.
precisa saber é o seguinte

1
Memória consumida e memória alocada não significa a mesma coisa. Nesse mesmo caso, a maioria das implementações retornará um ponteiro exclusivo. Isso significa que uma parte do espaço de endereço precisa ser sacrificada para esse ponteiro. Dependendo do alocador, isso pode significar que ele alocará 1 byte ou mais.
Coincoin

1
A biblioteca pode fazer o que quiser - bem, ela pode retornar um ponteiro exclusivo que nenhum outro malloc()retornará ou retornar NULL.
Alok Singhal

1
@ jldupont: Pelo menos a biblioteca Microsoft C Run-Time retorna um ponteiro exclusivo para malloc(0). No entanto, na mesma implementação da biblioteca C padrão, realloc(ptr, 0)libera ptre retorna NULL.
Medinoc 12/04/19

5

Há uma resposta em outra parte desta página que começa "malloc (0) retornará um endereço de memória válido e cujo intervalo dependerá do tipo de ponteiro ao qual está sendo alocada memória". Esta afirmação está incorreta (não tenho reputação suficiente para comentar diretamente essa resposta, portanto, não posso colocar esse comentário diretamente lá embaixo).

Fazer malloc (0) não alocará automaticamente a memória do tamanho correto. A função malloc não tem conhecimento do que você está lançando seu resultado. A função malloc depende exclusivamente do número do tamanho que você fornece como argumento. Você precisa fazer malloc (sizeof (int)) para obter armazenamento suficiente para armazenar um int, por exemplo, não 0.


4

malloc(0)não faz sentido para mim, a menos que o código dependa de comportamento específico da implementação. Se o código foi concebido para ser portátil, ele deve levar em consideração o fato de que um retorno NULL de malloc(0)não é uma falha. Então, por que não atribuir NULL de artistqualquer maneira, já que esse é um resultado válido e bem-sucedido, e é menos código, e não fará com que os programadores de manutenção levem tempo para descobrir isso?

malloc(SOME_CONSTANT_THAT_MIGHT_BE_ZERO)ou malloc(some_variable_which_might_be_zero)talvez possa ter seus usos, embora, novamente, você precise tomar um cuidado extra para não tratar um retorno NULL como uma falha se o valor for 0, mas um tamanho 0 deve estar OK.


3

Há muitas respostas semi-verdadeiras por aqui, então aqui estão os fatos concretos. A página de manual para malloc()diz:

Se size for 0, malloc () retornará NULL ou um valor de ponteiro exclusivo que pode ser passado com êxito para free () com êxito.

Isso significa que não há absolutamente nenhuma garantia de que o resultado malloc(0)seja único ou não NULL. A única garantia é fornecida pela definição de free(), novamente, aqui está o que a página de manual diz:

Se ptr for NULL, nenhuma operação será executada.

Portanto, seja qual for o malloc(0)retorno, ele pode ser transmitido com segurança free(). Mas o mesmo pode acontecer com um NULLponteiro.

Consequentemente, escrever não artist = malloc(0); é nada melhor do que escrever artist = NULL;


1
Pena que a implementação não tem permissão para retornar um ponteiro não nulo e não exclusivo. Dessa forma, malloc(0)poderia retornar, digamos, 0x1 e free()poderia ter uma verificação de caso especial de 0x1, assim como ocorre com 0x0.
Todd Lehman

3
@Odd Lehman Uma implementação pode fazer o que você sugere. A especificação C não especifica que o resultado deve ser " NULLou um ponteiro exclusivo". em vez disso, "um ponteiro nulo ou um ponteiro para o espaço alocado". Não há um requisito exclusivo . OTOH, retornar um valor especial não exclusivo pode interromper o código que conta com valores únicos. Talvez uma questão de canto para SO.
chux - Reintegrar Monica

mantambém pode documentar o formulário definido pela implementação usado no * nix. Neste caso isso não aconteça, mas ainda não é uma fonte canônica para C. geral
Lundin

@Lundin True. Mas as páginas de manual são muito mais acessíveis que o padrão C, e as páginas de manual nos sistemas GNU / Linux geralmente documentam muito bem quais padrões a implementação segue. Juntamente com as informações de quais partes aderem a qual padrão, elas devem diferir. Eu tenho a sensação de que ambos querem ser mais preciso, e anunciar cada bit que é uma extensão GNU ...
cmaster - Reintegrar monica

3

Por que você não deve fazer isso ...

Como o valor de retorno do malloc depende da implementação, você pode obter um ponteiro NULL ou outro endereço de volta. Isso pode acabar criando estouros de buffer de pilha se o código de manipulação de erros não verificar o tamanho e o valor retornado, levando a problemas de estabilidade (falhas) ou problemas de segurança ainda piores.

Considere este exemplo, onde o acesso adicional à memória via endereço retornado irá corromper a pilha se o tamanho for zero e a implementação retornar um valor não NULL.

size_t size;

/* Initialize size, possibly by user-controlled input */

int *list = (int *)malloc(size);
if (list == NULL) {
  /* Handle allocation error */
}
else {
  /* Continue processing list */
}

Consulte esta página de codificação segura nos padrões de codificação da CERT, onde peguei o exemplo acima para ler mais.



2

É certo que nunca vi isso antes, é a primeira vez que vejo essa sintaxe, poderíamos dizer, um caso clássico de exagero de função. Em conjunto com a resposta de Reed, gostaria de salientar que há uma coisa semelhante, que aparece como uma função sobrecarregada realloc:

  • foo é não NULL e tamanho é zero realloc(foo, size);,. Quando você passa um ponteiro não NULL e um tamanho de zero para realloc, o realloc se comporta como se você tivesse chamado free (…)
  • foo é NULL e tamanho é diferente de zero e maior que 1 realloc(foo, size);,. Quando você passa um ponteiro NULL e o tamanho é diferente de zero, o realloc se comporta como se você tivesse chamado malloc (…)

Espero que ajude, Atenciosamente, Tom.


1

Para realmente responder à pergunta: não há razão para fazer isso


0

malloc (0) retornará NULL ou um ponteiro válido que pode ser passado corretamente para livre. E, embora pareça que a memória para a qual ela aponta é inútil ou não possa ser escrita ou lida, nem sempre é verdade. :)

int *i = malloc(0);
*i = 100;
printf("%d", *i);

Esperamos uma falha de segmentação aqui, mas, surpreendentemente, isso imprime 100! É porque o malloc realmente pede uma grande quantidade de memória quando chamamos malloc pela primeira vez. Toda chamada para malloc depois disso usa a memória desse grande pedaço. Somente após o término desse grande pedaço, é solicitada nova memória.

Uso de malloc (0): se você estiver em uma situação em que deseja que as chamadas subseqüentes sejam mais rápidas, chamar malloc (0) deve fazer isso por você (exceto em casos extremos).


1
Escrever para *ipode não falhar no seu caso, mas é um comportamento indefinido. Cuidado com os demônios nasais!
John Dvorak

1
Sim. Isso é verdade. É uma implementação específica. Eu verifiquei no MaxOS X e em alguma distribuição Linux. Eu não tentei em outras plataformas. Dito isto, o conceito que descrevi foi descrito no livro "A linguagem de programação C", de Brain Kernighan e Dennis Ritchie.
precisa saber é o seguinte

Eu sei: comentário super tardio sobre esta questão. Mas às vezes há um uso para malloc(0)isso não mencionado. Nas implementações em que ele retorna um valor não NULL, especialmente em uma compilação DEBUG, ele provavelmente aloca MAIS do que o solicitado e fornece o ponteiro para apenas passar o cabeçalho interno. Isso permite que você tenha uma idéia do uso real da memória, se conseguir isso antes e depois de uma série de alocações. por exemplo: void* before = malloc(0); ... void* after = malloc(0); long long total = after - before;ou algo assim.
Jesse Chisholm

Eu li "A linguagem de programação C", de Brain Kernighan e Dennis Ritchie, e não me lembro de dizer nada malloc(0). Você poderia dizer a qual capítulo você está se referindo também? Fornecer uma cotação exata também seria bom.
Андрей Беньковский

0

No Windows:

  • void *p = malloc(0);alocará um buffer de tamanho zero no heap local. O ponteiro retornado é um ponteiro de heap válido.
  • mallocem última análise, chama HeapAllocusando o heap de tempo de execução C padrão, que então chama RtlAllocateHeapetc.
  • free(p);usa HeapFreepara liberar o buffer de tamanho 0 no heap. Não liberá-lo resultaria em um vazamento de memória.

0

É realmente bastante útil, e (obviamente IMHO), o comportamento permitido de retornar um ponteiro NULL está quebrado. Um ponteiro dinâmico é útil não apenas para o que aponta, mas também pelo fato de seu endereço ser único. Retornar NULL remove essa segunda propriedade. Todos os mallocs incorporados que eu programa (com bastante frequência) têm esse comportamento.



-2

Aqui está a análise após a execução com a ferramenta de verificação de memória valgrind.

==16740== Command: ./malloc0
==16740==
p1 = 0x5204040
==16740==
==16740== HEAP SUMMARY:
==16740==     in use at exit: 0 bytes in 0 blocks
==16740==   total heap usage: 2 allocs, 2 frees, 1,024 bytes allocated
==16740==
==16740== All heap blocks were freed -- no leaks are possible

e aqui está o meu código de exemplo:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
   //int i;
   char *p1;

   p1 = (char *)malloc(0);
   printf("p1 = %p\n", p1);

   free(p1);

   return 0;

}

Por padrão, 1024 bytes são alocados. Se eu aumentar o tamanho do malloc, os bytes alocados aumentarão em 1025 e assim por diante.


-3

De acordo com a resposta de Reed Copsey e a página de manual do malloc, escrevi alguns exemplos para testar. E eu descobri que malloc (0) sempre dará a ele um valor único. Veja meu exemplo:

char *ptr;
if( (ptr = (char *) malloc(0)) == NULL )
    puts("Got a null pointer");
else
    puts("Got a valid pointer");

A saída será "Tem um ponteiro válido", o que significa que ptrnão é nulo.


-6

malloc(0)retornará um endereço de memória válido e cujo intervalo dependerá do tipo de ponteiro ao qual está sendo alocada memória. Também é possível atribuir valores à área de memória, mas isso deve estar dentro do alcance do tipo de ponteiro que está sendo usado. Você também pode liberar a memória alocada. Vou explicar isso com um exemplo:

int *p=NULL;
p=(int *)malloc(0);
free(p);

O código acima funcionará bem em um gcccompilador na máquina Linux. Se você tiver um compilador de 32 bits, poderá fornecer valores no intervalo inteiro, ou seja, -2147483648 a 2147483647. O mesmo se aplica aos caracteres também. Observe que, se o tipo de ponteiro declarado for alterado, o intervalo de valores será alterado independentemente do malloctipo de impressão, ou seja,

unsigned char *p=NULL;
p =(char *)malloc(0);
free(p);

p assumirá um valor de 0 a 255 de char, pois é declarado um int sem sinal.


5
Krellan está certo ao apontar que esta resposta está errada: malloc()não sabe nada sobre o elenco (que na verdade é inteiramente superfluente em C). A desreferenciação do valor de retorno malloc(0)chamará um comportamento indefinido.
cmaster - reinstate monica

-6

Apenas para corrigir uma impressão falsa aqui:

artist = (char *) malloc(0);nunca mais voltará NULL; não é o mesmo que artist = NULL;. Escreva um programa simples e compare artistcom NULL. if (artist == NULL)é falso e if (artist)é verdadeiro.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.