Diferença entre malloc e calloc?


780

Qual é a diferença entre doing:

ptr = (char **) malloc (MAXELEMS * sizeof(char *));

ou:

ptr = (char **) calloc (MAXELEMS, sizeof(char*));

Quando é uma boa ideia usar calloc sobre malloc ou vice-versa?



8
Em C, você poderia escrever o acima mais genericamente como:ptr = calloc(MAXELEMS, sizeof(*ptr));
chqrlie

7
Um post interessante sobre a diferença entre calloc e malloc + memset vorpus.org/blog/why-does-calloc-exist #
ddddavidee

2
@ddddavidee Eu também encontrei esse blog depois que estava insatisfeito com tantas respostas na rede. Nathaniel J. Smith merece mais de 100 pontos SO para sua análise.
lifebalance

Respostas:


851

calloc()fornece um buffer inicializado com zero, enquanto malloc()deixa a memória não inicializada.

Para alocações grandes, a maioria das callocimplementações nos sistemas operacionais convencionais obterá páginas zeradas conhecidas do sistema operacional (por exemplo, via POSIX mmap(MAP_ANONYMOUS)ou Windows VirtualAlloc), para que não seja necessário gravá-las no espaço do usuário. É assim que o normal também mallocobtém mais páginas do sistema operacional; callocapenas aproveita a garantia do sistema operacional.

Isso significa que a callocmemória ainda pode ser "limpa" e alocada preguiçosamente e a cópia na gravação mapeada para uma página física compartilhada de zeros em todo o sistema. (Supondo um sistema com memória virtual.)

Alguns compiladores podem otimizar malloc + memset (0) em calloc para você, mas você deve usar calloc explicitamente se quiser que a memória seja lida como 0.

Se você nunca ler a memória antes de escrevê-la, use mallocpara que ela possa (potencialmente) fornecer memória suja de sua lista gratuita interna, em vez de obter novas páginas do sistema operacional. (Ou em vez de zerar um bloco de memória na lista livre para uma pequena alocação).


As implementações incorporadas callocpodem deixar calloca memória com zero se não houver sistema operacional ou não for um sistema operacional multiusuário sofisticado que zere páginas para interromper o vazamento de informações entre os processos.

No Linux incorporado, o malloc poderia mmap(MAP_UNINITIALIZED|MAP_ANONYMOUS), que é ativado apenas para alguns kernels incorporados porque é inseguro em um sistema multiusuário.


224
As variantes * atribuição são bastante mnemônicas - alocação clara, alocação de memória, realocação.
Cascabel

43
Use malloc () para definir tudo o que você usa no espaço alocado. Use calloc () para deixar partes dos dados não inicializadas - e seria benéfico zerar as partes não definidas.
31416 Jonathan Leffler

268
callocnão é necessariamente mais caro, pois o sistema operacional pode fazer alguns truques para acelerar isso. Eu sei que o FreeBSD, quando fica com o tempo ocioso da CPU, usa isso para executar um processo simples que gira e zera os blocos de memória desalocados e marca os blocos, portanto, processa com uma flag. Então, quando você o faz calloc, ele primeiro tenta encontrar um desses blocos pré-zerados e apenas fornece a você - e provavelmente encontrará um.
Pavel Minaev 08/10/09

28
Costumo sentir que, se o seu código se tornar "mais seguro" como resultado de alocações de entrada zero por padrão, ele será insuficientemente seguro se você usa malloc ou calloc. Usar malloc é um bom indicador de que os dados precisam ser inicializados - eu só uso calloc nos casos em que esses 0 bytes são realmente significativos. Observe também que o calloc não faz necessariamente o que você pensa para tipos não-char. Ninguém mais usa representações de interceptações ou flutua fora do IEEE, mas isso não é desculpa para pensar que seu código é verdadeiramente portátil quando não é.
Steve Jessop

18
@SteveJessop "Mais seguro" não é a palavra correta. Eu acho que "determinista" é o melhor termo. O código que é mais determinístico, em vez de apresentar falhas que dependem do tempo e das seqüências de dados, será mais fácil de isolar falhas. Às vezes, o Calloc é uma maneira fácil de obter esse determinismo, em comparação à inicialização explícita.
Dennis 4/14

362

Uma diferença menos conhecida é que, em sistemas operacionais com alocação de memória otimista, como o Linux, o ponteiro retornado mallocnão é suportado pela memória real até que o programa realmente o toque.

callocde fato toca a memória (escreve zeros nela) e, portanto, você terá certeza de que o sistema operacional está apoiando a alocação com RAM (ou swap) real. É também por isso que é mais lento que o malloc (não apenas precisa zerá-lo, o sistema operacional também deve encontrar uma área de memória adequada trocando outros processos)

Veja, por exemplo, esta questão do SO para uma discussão mais aprofundada sobre o comportamento do malloc.


49
callocnão precisa escrever zeros. Se o bloco alocado consistir principalmente em novas zero páginas fornecidas pelo sistema operacional, poderá deixá-las intocadas. Obviamente, isso precisa callocser ajustado ao sistema operacional, em vez de uma função genérica da biblioteca malloc. Ou, um implementador pode calloccomparar cada palavra com zero antes de zerá-lo. Isso não economizaria tempo, mas evitaria sujar as novas páginas.
R .. GitHub Pare de ajudar o gelo

3
@ R .. nota interessante. Mas, na prática, essas implementações existem na natureza?
Isak Savo

10
As dlmallocimplementações semelhantes ignoram memsetse o pedaço foi obtido por meio de mmapnovas páginas anônimas (ou equivalente). Normalmente, esse tipo de alocação é usado para pedaços maiores, começando em 256k ou mais. Não conheço nenhuma implementação que faça a comparação contra zero antes de escrever zero além da minha.
R .. GitHub Pare de ajudar o gelo

1
omalloctambém pula o memset; callocnão precisa tocar em nenhuma página que ainda não esteja sendo usada pelo aplicativo (cache de página). Porém, implementações extremamente primitivascalloc diferem.
mirabilos

10
O calloc da glibc verifica se está recebendo nova memória do sistema operacional. Nesse caso, ele sabe que NÃO precisa gravá-lo, porque mmap (..., MAP_ANONYMOUS) retorna a memória que já está zerada.
31568 Peter Cordes

112

Uma vantagem muitas vezes esquecida callocé que (implementações compatíveis) ajudarão a protegê-lo contra vulnerabilidades de estouro inteiro. Comparar:

size_t count = get_int32(file);
struct foo *bar = malloc(count * sizeof *bar);

vs.

size_t count = get_int32(file);
struct foo *bar = calloc(count, sizeof *bar);

O primeiro pode resultar em uma alocação minúscula e estouros de buffer subsequentes, se countfor maior que SIZE_MAX/sizeof *bar. O último falhará automaticamente neste caso, pois um objeto que é grande não pode ser criado.

É claro que você pode estar atento a implementações não conformes que simplesmente ignoram a possibilidade de transbordamento ... Se essa é uma preocupação nas plataformas direcionadas, você terá que fazer um teste manual de transbordamento de qualquer maneira.


17
Aparentemente, o estouro aritmético foi o que causou o buraco no OpenSSH em 2002. Bom artigo do OpenBSD sobre os perigos disso com funções relacionadas à memória: undeadly.org/cgi?action=article&sid=20060330071917
Philip P.

4
@KomradeP .: Interessante. Infelizmente, o artigo que você vinculou tem informações erradas logo no início. O exemplo com nãochar é um estouro, mas uma conversão definida pela implementação ao atribuir o resultado novamente a um objeto. char
R .. GitHub Pare de ajudar o gelo

Provavelmente está lá apenas para fins ilustrativos. Porque o compilador provavelmente otimizará isso de qualquer maneira. Mina compila neste asm: impulso 1.
Philip P.

1
@tristopia: O ponto não é que o código seja explorável em todas as implementações, mas que esteja incorreto sem suposições adicionais e, portanto, não seja correto / portátil.
R .. GitHub Pare de ajudar o gelo

3
@tristopia: Se seu modo de pensar é "de size_t64 bits, não há problema", é uma maneira de pensar falha que levará a erros de segurança. size_té um tipo abstrato que representa tamanhos e não há razão para pensar que o produto arbitrário de um número de 32 bits e um size_t(nota: sizeof *barem princípio, poderia ser maior que 2 ^ 32 em uma implementação C de 64 bits!) se encaixam size_t.
R .. GitHub Pare de ajudar o gelo

37

A documentação faz com que o calloc pareça malloc, que apenas inicializa zero a memória; essa não é a principal diferença! A idéia do calloc é abstact semântica copy-on-write para alocação de memória. Quando você aloca memória com calloc, tudo é mapeado para a mesma página física que é inicializada como zero. Quando qualquer uma das páginas da memória alocada é gravada em uma página física, ela é alocada. Isso geralmente é usado para criar tabelas de hash ENORMES, por exemplo, porque as partes vazias de hash não são suportadas por nenhuma memória extra (páginas); eles apontam para a única página inicializada com zero, que pode ser compartilhada entre processos.

Qualquer gravação no endereço virtual é mapeada para uma página, se essa página é a página zero, outra página física é alocada, a página zero é copiada para lá e o fluxo de controle é retornado ao processo do cliente. Isso funciona da mesma maneira que os arquivos mapeados na memória, memória virtual, etc. funcionam .. ele usa paginação.

Aqui está uma história de otimização sobre o tópico: http://blogs.fau.de/hager/2007/05/08/benchmarking-fun-with-calloc-and-zero-pages/


26

Não há diferença no tamanho do bloco de memória alocado. callocapenas preenche o bloco de memória com um padrão físico de zero-bits. Na prática, assume-se frequentemente que os objetos localizados no bloco de memória alocado com callocvalor inicial como se tivessem sido inicializados com literal 0, ou seja, números inteiros devem ter valor de 0variáveis ​​de ponto flutuante - valor de 0.0, ponteiros - o valor de ponteiro nulo apropriado , e assim por diante.

Do ponto de vista pedante, porém, calloc(e também memset(..., 0, ...)) é garantido que apenas inicialize adequadamente (com zeros) objetos do tipo unsigned char. Não há garantia de que tudo o mais seja inicializado corretamente e pode conter a chamada representação de interceptação , o que causa comportamento indefinido. Em outras palavras, para qualquer outro tipo que não unsigned charo padrão de todos os bits zero acima mencionado pode representar um valor ilegal, representação de interceptação.

Posteriormente, em uma das correções técnicas para o padrão C99, o comportamento foi definido para todos os tipos de números inteiros (o que faz sentido). Ou seja, formalmente, no idioma C atual, você pode inicializar apenas tipos inteiros com calloc(e memset(..., 0, ...)). Usá-lo para inicializar qualquer outra coisa, em geral, leva a um comportamento indefinido, do ponto de vista da linguagem C.

Na prática, callocfunciona, como todos sabemos :), mas se você deseja usá-lo (considerando o exposto acima) é com você. Pessoalmente, prefiro evitá-lo completamente, usar malloce executar minha própria inicialização.

Finalmente, outro detalhe importante callocé o necessário para calcular internamente o tamanho final do bloco , multiplicando o tamanho do elemento pelo número de elementos. Ao fazer isso, callocdeve observar o possível estouro aritmético. Isso resultará em alocação malsucedida (ponteiro nulo) se o tamanho do bloco solicitado não puder ser calculado corretamente. Enquanto isso, sua mallocversão não faz nenhuma tentativa de observar o estouro. Ele alocará uma quantidade "imprevisível" de memória, caso ocorra um estouro.


De acordo com o parágrafo "outro detalhe importante": isso parece causar memset(p, v, n * sizeof type);um problema porque n * sizeof typepode transbordar. Acho que vou precisar usar um for(i=0;i<n;i++) p[i]=v;loop para código robusto.
chux - Restabelece Monica

Seria útil se houvesse um meio padrão pelo qual o código pudesse afirmar que uma implementação deve usar todos os bits zero como um ponteiro nulo (recusando a compilação de outra forma), já que existem implementações que usam outras representações de ponteiros nulos, mas elas são comparativamente raro; o código que não precisa ser executado nessas implementações pode ser mais rápido se for possível usar calloc () ou memset para inicializar matrizes de ponteiros.
Supercat

@chux Não, se existir uma matriz com nelementos em que um elemento tenha o tamanho sizeof type, ele n*sizeof typenão poderá exceder, porque o tamanho máximo de qualquer objeto deve ser menor que SIZE_MAX.
12431234123412341234123

@ 12431234123412341234123 Verdadeiro sobre o tamanho de uma matriz <= SIZE_MAX, ainda não há matrizes aqui. O ponteiro retornado de calloc()pode apontar para a memória alocada que excede SIZE_MAX. Muitas implementações limitam o produto dos 2 argumentos calloc()a SIZE_MAX, mas a especificação C não impõe esse limite.
chux - Reinstala Monica

21

de um artigo Benchmarking divertido com calloc () e zero páginas no Blog de Georg Hager

Ao alocar memória usando calloc (), a quantidade de memória solicitada não é alocada imediatamente. Em vez disso, todas as páginas que pertencem ao bloco de memória são conectadas a uma única página contendo todos os zeros por alguma mágica da MMU (links abaixo). Se essas páginas forem lidas apenas (o que era verdadeiro para as matrizes b, c e d na versão original do benchmark), os dados são fornecidos a partir da única página zero, que, é claro, se encaixa no cache. Tanta coisa para os kernels de loop vinculados à memória. Se uma página é gravada (não importa como), ocorre uma falha, a página "real" é mapeada e a página zero é copiada para a memória. Isso é chamado de copiar na gravação, uma abordagem de otimização conhecida (que eu já ensinei várias vezes em minhas palestras em C ++). Depois disso,


onde está o link?
Rupesh Yadav.

2
A primeira linha de resposta contém um link para o Blog de Georg Hager.
Ashish Chavan

11

callocé geralmente malloc+memset0

Geralmente é um pouco melhor usar malloc+memsetexplicitamente, especialmente quando você está fazendo algo como:

ptr=malloc(sizeof(Item));
memset(ptr, 0, sizeof(Item));

Isso é melhor porque sizeof(Item)é conhecido pelo compilador no momento da compilação e, na maioria dos casos, o substitui pelas melhores instruções possíveis para zerar a memória. Por outro lado, se memsetestiver ocorrendo calloc, o tamanho do parâmetro da alocação não é compilado no calloccódigo e o real memseté frequentemente chamado, o que normalmente contém código para preencher byte a byte até o limite longo, em vez de alternar para o preenchimento. memória em sizeof(long)pedaços e, finalmente, preenchimento de byte a byte do espaço restante. Mesmo que o alocador seja inteligente o suficiente para chamar alguns aligned_memset, ainda será um loop genérico.

Uma exceção notável seria quando você estiver fazendo malloc / calloc de uma grande parte da memória (alguns power_of_two kilobytes), caso em que a alocação pode ser feita diretamente do kernel. Como os kernels do SO normalmente zeram toda a memória que liberam por motivos de segurança, um calloc inteligente o suficiente pode devolvê-lo sem zerar adicional. Novamente - se você está apenas alocando algo que sabe ser pequeno, pode ser melhor usar o malloc + memset em termos de desempenho.


+1 como lembrete de que uma implementação genérica de uma funcionalidade em uma biblioteca do sistema não é necessariamente mais rápida que a mesma operação no código do usuário.
Patrick Schlüter

1
Há também um segundo ponto que torna calloc()mais lento que malloc(): a multiplicação para o tamanho. calloc()é necessário usar uma multiplicação genérica (se size_tfor 64 bits, mesmo a operação muito cara de 64 bits * 64 bits = 64 bits) enquanto o malloc () geralmente terá uma constante de tempo de compilação.
Patrick Schlüter

4
O glibc calloc tem alguns smarts para decidir como limpar o bloco retornado com mais eficiência, por exemplo, às vezes apenas parte dele precisa de limpeza e também uma limpeza desenrolada de até 9 * sizeof (size_t). Memória é memória, limpá-lo 3 bytes por vez não será mais rápido, apenas porque você o usará para reter struct foo { char a,b,c; };. callocé sempre melhor que malloc+ memset, se você sempre limpar toda a mallocregião ed. calloctambém tem uma verificação cuidadosa, mas eficiente, do excesso de int nos elementos size *.
27568 Peter Cordes

8

Diferença 1:

malloc() geralmente aloca o bloco de memória e é o segmento de memória inicializado.

calloc() aloca o bloco de memória e inicializa todo o bloco de memória em 0.

Diferença 2:

Se você considerar a malloc()sintaxe, serão necessários apenas 1 argumento. Considere o seguinte exemplo abaixo:

data_type ptr = (cast_type *)malloc( sizeof(data_type)*no_of_blocks );

Ex: se você deseja alocar 10 blocos de memória para o tipo int,

int *ptr = (int *) malloc(sizeof(int) * 10 );

Se você considerar a calloc()sintaxe, serão necessários 2 argumentos. Considere o seguinte exemplo abaixo:

data_type ptr = (cast_type *)calloc(no_of_blocks, (sizeof(data_type)));

Ex: se você deseja alocar 10 blocos de memória para o tipo int e inicializar tudo isso em ZERO,

int *ptr = (int *) calloc(10, (sizeof(int)));

Semelhança:

Ambos malloc()e calloc()retornarão nulo * por padrão, se não forem do tipo fundido.!


E por que você mantém data_type e cast_type diferentes?
Esgotado

7

Existem duas diferenças.
Primeiro, está no número de argumentos. malloc()usa um único argumento (memória necessária em bytes), enquanto calloc()precisa de dois argumentos.
Em segundo lugar, malloc()não inicializa a memória alocada, enquanto calloc()inicializa a memória alocada para ZERO.

  • calloc()aloca uma área de memória, o comprimento será o produto de seus parâmetros. callocpreenche a memória com ZERO e retorna um ponteiro para o primeiro byte. Se não conseguir localizar espaço suficiente, ele retornará um NULLponteiro.

Sintaxe: ptr_var=(cast_type *)calloc(no_of_blocks , size_of_each_block); ieptr_var=(type *)calloc(n,s);

  • malloc()aloca um único bloco de memória de REQUSTED SIZE e retorna um ponteiro para o primeiro byte. Se não conseguir localizar a quantidade necessária de memória, ele retornará um ponteiro nulo.

Sintaxe: ptr_var=(cast_type *)malloc(Size_in_bytes); A malloc()função aceita um argumento, que é o número de bytes a serem alocados, enquanto a calloc()função recebe dois argumentos, um sendo o número de elementos e o outro o número de bytes a serem alocados para cada um desses elementos. Além disso, calloc()inicializa o espaço alocado para zeros, enquanto malloc()não.


6

A calloc()função declarada no <stdlib.h>cabeçalho oferece algumas vantagens sobre a malloc()função.

  1. Ele aloca memória como um número de elementos de um determinado tamanho e
  2. Inicializa a memória alocada para que todos os bits sejam zero.

6

malloc()e calloc()são funções da biblioteca padrão C que permitem alocação dinâmica de memória, o que significa que ambas permitem alocação de memória durante o tempo de execução.

Seus protótipos são os seguintes:

void *malloc( size_t n);
void *calloc( size_t n, size_t t)

Existem principalmente duas diferenças entre os dois:

  • Comportamento: malloc()aloca um bloco de memória, sem inicializá-lo, e a leitura do conteúdo desse bloco resultará em valores de lixo. calloc(), por outro lado, aloca um bloco de memória e o inicializa em zeros, e obviamente a leitura do conteúdo desse bloco resultará em zeros.

  • Sintaxe: malloc()recebe 1 argumento (o tamanho a ser alocado) e calloc()recebe dois argumentos (número de blocos a serem alocados e tamanho de cada bloco).

O valor de retorno de ambos é um ponteiro para o bloco de memória alocado, se for bem-sucedido. Caso contrário, NULL será retornado, indicando a falha na alocação de memória.

Exemplo:

int *arr;

// allocate memory for 10 integers with garbage values
arr = (int *)malloc(10 * sizeof(int)); 

// allocate memory for 10 integers and sets all of them to 0
arr = (int *)calloc(10, sizeof(int));

A mesma funcionalidade que calloc()pode ser obtida usando malloc()e memset():

// allocate memory for 10 integers with garbage values   
arr= (int *)malloc(10 * sizeof(int));
// set all of them to 0
memset(arr, 0, 10 * sizeof(int)); 

Observe que, de malloc()preferência, é usado mais, calloc()pois é mais rápido. Se desejar inicializar com zero os valores, use calloc().


5

Uma diferença ainda não mencionada: limite de tamanho

void *malloc(size_t size)só pode alocar até SIZE_MAX.

void *calloc(size_t nmemb, size_t size);pode alocar-se sobre SIZE_MAX*SIZE_MAX.

Essa capacidade não é frequentemente usada em muitas plataformas com endereçamento linear. Tais sistemas limitam calloc()com nmemb * size <= SIZE_MAX.

Considere um tipo de 512 bytes chamado disk_sectore o código deseja usar vários setores. Aqui, o código pode usar apenas SIZE_MAX/sizeof disk_sectorsetores.

size_t count = SIZE_MAX/sizeof disk_sector;
disk_sector *p = malloc(count * sizeof *p);

Considere o seguinte, que permite uma alocação ainda maior.

size_t count = something_in_the_range(SIZE_MAX/sizeof disk_sector + 1, SIZE_MAX)
disk_sector *p = calloc(count, sizeof *p);

Agora, se esse sistema pode fornecer uma alocação tão grande é outra questão. Hoje a maioria não vai. No entanto, ocorre há muitos anos quando SIZE_MAXfoi 65535. Dada a lei de Moore , suspeite que isso ocorra por volta de 2030 com certos modelos de memória com SIZE_MAX == 4294967295e conjuntos de memória nos 100 GBytes.


2
Geralmente, size_t será capaz de armazenar o tamanho do maior tipo de objeto que um programa poderia manipular. É improvável que um sistema em que size_t seja 32 bits seja capaz de lidar com uma alocação maior que 4294967295 bytes, e um sistema que seja capaz de lidar com alocações desse tamanho quase certamente size_taumentará em 32 bits. A única questão é se o uso de callocvalores cujo produto excede o valor SIZE_MAXzero, em vez de retornar um ponteiro para uma alocação menor.
supercat

Concorde com sua generalização , mas a especificação C permite calloc()alocações superiores SIZE_MAX. Isso aconteceu no passado com 16 bits size_te, como a memória continua barateando, não vejo razão para que isso não aconteça no futuro, mesmo que não seja comum .
chux - Reinstala Monica 15/08/16

1
O padrão C possibilita que o código solicite uma alocação cujo tamanho exceda SIZE_MAX. Certamente não exige que haja qualquer circunstância sob a qual tal alocação possa ter sucesso; Não tenho certeza de que exista algum benefício específico ao exigir que as implementações que não podem lidar com essas alocações devam retornar NULL(especialmente porque é comum que algumas implementações tenham mallocponteiros de retorno para o espaço que ainda não foi confirmado e que pode não estar disponível quando o código realmente tentar usar isto).
Supercat

Além disso, onde havia sistemas no passado cujo intervalo de endereçamento disponível excedia o maior número inteiro representável, não vejo nenhuma possibilidade realista de que isso ocorra novamente, pois isso exigiria uma capacidade de armazenamento de bilhões de gigabytes. Mesmo que a Lei de Moore continuasse em vigor, ir do ponto em que 32 bits deixa de ser suficiente até o ponto em que 64 bits deixou de ser suficiente levaria o dobro do tempo que chegar do ponto em que 16 bits era suficiente até o ponto em que 32 não estava. 't.
Supercat

1
Por que uma implementação que pode acomodar uma única alocação em excesso de 4G não definir size_ta uint64_t?
supercat

2

Número de blocos:
malloc () atribui um único bloco de memória solicitada,
calloc () atribui vários blocos da memória solicitada

Inicialização:
malloc () - não limpa e inicializa a memória alocada.
calloc () - inicializa a memória alocada por zero.

Velocidade:
malloc () é rápido.
calloc () é mais lento que malloc ().

Argumentos e sintaxe:
malloc () leva 1 argumento:

  1. bytes

    • O número de bytes a serem alocados

calloc () recebe 2 argumentos:

  1. comprimento

    • o número de blocos de memória a serem alocados
  2. bytes
    • o número de bytes a serem alocados em cada bloco de memória
void *malloc(size_t bytes);         
void *calloc(size_t length, size_t bytes);      

Alocação de maneira de memória:
A função malloc atribui memória do 'tamanho' desejado a partir da pilha disponível.
A função calloc atribui à memória o tamanho igual ao 'num * size'.

Significado no nome:
O nome malloc significa "alocação de memória".
O nome calloc significa "alocação contígua".

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.