2009-02-01 9 views

Respuesta

2

Es lo mismo. La razón es porque la mayoría de las veces quiere usar un operador sizeof como uno de los argumentos. Si le molestan dos parámetros, llame al malloc() que tiene un solo argumento.

+12

Debe tener en cuenta que malloc() no pondrá a cero la memoria como lo hace calloc(). –

+0

Bueno, eso es cierto. Gracias por el aviso. –

+0

¿Es cierto que son iguales cuando se toma en cuenta el embalaje? ¿Calloc (16,3) es lo mismo que calloc (3,16) en una máquina que necesita que cualquier elemento de datos comience en un límite de 4 bytes? – ChrisW

20

La mayoría de las personas usa rutinas de asignación para asignar espacio para un número determinado de elementos, por lo que calloc() permite que se especifique muy bien. Así, por ejemplo, si quieres espacio para 100 enteros o 20 de su propia estructura:

int *pInt = calloc (100, sizeof(int)); 
tMyStruct *pMyStruct = calloc (20, sizeof(tMyStruct)); 

Este código en realidad se ve un poco "mejor" que el equivalente malloc() llamadas:

int *pInt = malloc (100 * sizeof(int)); 
tMyStruct *pMyStruct = malloc (20 * sizeof(tMyStruct)); 

aunque, a codificadores C experimentados, no hay distinción real (aparte de la inicialización cero, por supuesto).

tengo que decir que tengo Nunca calloc utilizado en la naturaleza, ya que estoy casi siempre la creación de un struct donde cero de no tienen sentido. Prefiero inicializar todos los campos manualmente para asegurarme de obtener los valores que quiero.

+0

Gracias. Por cierto, necesitas cambiar el segundo calloc en el código inferior a un malloc. – Ali

+1

Ta, operaciones cut'n'paste Damnable! :-) – paxdiablo

+0

Estoy de acuerdo: casi nunca he usado calloc(). A veces creo una estructura estática que se inicializa correctamente por defecto, y luego la asigno a las estructuras recién asignadas y realizo cualquier reparación residual. –

4

Hay una pequeña distinción: Calloc puede decidir poner a cero la memoria solo cuando sea necesario y luego existe la ventaja de conocer el tamaño de los elementos.

No puedo nombrar ninguna implementación haciendo esto, pero fue pensado para esto.

A modo de ejemplo:

Una callocates 4 GB de memoria, pero el sistema sólo tiene 2 GB: no tendría sentido escribir 2GBs de cero en la memoria virtual, por lo tanto, el sistema podría establecer una sucia bandera en esta memoria para ponerlo a cero a medida que se carga en la memoria.

+0

Me sorprendería que las implementaciones * real * realmente implementen 'calloc' como' malloc' y 'memzero' para todos los tamaños de memoria excepto para los pequeños. Una optimización muy trivial sería mapear la (s) página (s) a '/ dev/zero'. – sanjoyd

+0

@theDigtialEngel: Nunca he visto una implementación de calloc desde adentro, así que no puedo decirlo. –

12

calloc (4, 6) y calloc (6, 4) son NO el mismo:

En un sistema típico de 32 bits/64 bits, la primera asignaría 32 bytes y los segundos 24 bytes.


El punto clave es que calloc debe devolver la memoria como si se alineó correctamente como una matriz. Está destinado a asignar una matriz y ser usado como sigue:

A *p = (A*)calloc(count, sizeof(A)); 
for (int i = 0; i < count; ++i) 
{ 
    f(&(p[i])); 
    // f(p + i) is also valid 
} 

o

A *p = (A*)calloc(count, sizeof(A)); 
for (A *q = p; q != p + count; ++q) 
{ 
    f(q); 
} 

calloc se supone que asignar la matriz, teniendo en cuenta el relleno y otros requisitos de funcionamiento del sistema de destino. Entonces, en la mayoría de las máquinas de 32 bits, donde una estructura de 6 bytes tendría que ser rellenada a 8 bytes, asignaría 4 lotes de 8 bytes.

calloc donde el primer argumento es un sizeof() es muy probable que sea un error y se debe investigar.

calloc donde el segundo argumento no es sizeof (atype) no está definido. Apesta a suposiciones ocultas y es peligroso para el puerto.

Aclaración: En un sistema típico de 32 bits/64 bits, una estructura es probable que se acolchado y alineado a un múltiplo de 32 bits. Como tal, en estos sistemas sizeof no sería devolver 6 bytes. De hecho, no hay garantía de que el compilador no pueda rellenar y alinear con un múltiplo de 16 bytes si eso es lo que requiere el compilador/plataforma.

Mi respuesta se basa en el hecho de que no debe hacer suposiciones sobre el tamaño de la estructura. Pueden cambiar con las opciones del compilador o la plataforma de destino. Solo asegúrese de que su segundo argumento sea un tamaño de expresión y no haga suposiciones.


Desde el standard:

La función calloc() asignará espacio no utilizado para un conjunto de elementos nelem cada uno de cuyos tamaño en bytes es elsize. El espacio se inicializará en todos los bits 0.

El puntero devuelto si la asignación tiene éxito se alineará adecuadamente para que pueda asignarse a un puntero a cualquier tipo de objeto y luego utilizarse para acceder a dicho objeto o matriz de tales objetos en el espacio asignado (hasta que el espacio sea explícitamente liberado o reasignado). Cada una de esas asignaciones arrojará un puntero a un objeto disjuntado de cualquier otro objeto. El puntero devuelto debe apuntar al inicio (dirección de byte más bajo) del espacio asignado.

+0

La mayoría de los programadores con los que he trabajado asumen que son lo mismo. Un proyecto escribió un asignador personalizado con esa suposición. Cuando se demostró que era incorrecto, el jefe del equipo decidió solucionarlo agregando un comentario que indicaba que el segundo argumento debe ser un tamaño o provocará un bloqueo. –

+6

creo que estás equivocado. tanto malloc como calloc deben devolver un puntero adecuado alineado para cualquier tipo de objeto. da la casualidad, eso es exactamente lo que has citado del estándar. Suponiendo que mi creencia es incorrecta, ¿puede proporcionar un enlace para leer más sobre esto, por favor? –

+0

de todos modos, si cambia el orden de los argumentos, nunca se garantiza para ninguna función que ocurra exactamente lo mismo. las implementaciones siempre pueden hacer optimizaciones en su nombre, tan pronto como las restricciones impuestas por el estándar no se violen, de modo que el uso del código no funcione. –

5

A pesar de la respuesta aceptada (que creo que es correcta), parece haber confusiones acerca de cuántos bytes se asignan debido a la alineación. Así que aquí hay un poco de prueba en mi Linux de 32 bits con gcc-4.3:

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

int main() 
{ 
    char* p1 = calloc(6, 4); 
    char* p2 = calloc(4, 6); 
    char* p3 = calloc(1,1); 
    printf("%p, %p, %p\n", p1, p2, p3); 
    return 0; 
} 

El resultado es:

0x826b008, 0x826b028, 0x826b048 

que demuestra que tanto calloc(6,4) y calloc(4,6) asignar la misma cantidad de memoria, que es redondeó a 32 bytes en mi sistema. Cambiar los números de calloc(3,4) y calloc(4,3) dará el siguiente resultado:

0x95d2008, 0x95d2018, 0x95d2028 

que muestra que 16 bytes están reservados Cuando se soliciten y asignado al programa 12. En cualquier caso, las llamadas calloc(a,b) y calloc(b,a) tienen el mismo efecto en el uso de la memoria.


Añadida por Jonathan Leffler porque 300 caracteres nunca serán suficientes.

consideran este programa, que pierde memoria como un colador verdadera, pero demuestra un punto:

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

int main() 
{ 
    int i, j, k; 

    for (i = 1; i < 17; i++) 
     for (j = 1; j < 9; j++) 
      for (k = 0; k < 4; k++) 
       printf("(%2d,%d)%d: %p\n", i, j, k, calloc(i, j)); 
    return(0); 
} 

En Windows, bajo Cygwin, esto comienza mediante la asignación de bloques que son 16 bytes separados (en realidad, el segundo bloque es de 24 bytes después del primero, pero a partir de entonces, están separados por 16 bytes).Al asignar (2,7), las direcciones del bloque comienzan a incrementarse en 24 bytes; del mismo modo, (3,4) asigna bloques de 16 bytes de separación, pero (3,5) asigna bloques de 24 bytes de separación. Y, para el registro, ambos (4,6) y (6,4) devuelven punteros separados por 32 bytes.

Esto simplemente demuestra que hay una sobrecarga asociada con una llamada de asignación. Si observa la implementación arquetípica de malloc() et al en K & R, verá que el tamaño del bloque se almacena antes que la memoria que tiene derecho a usar. Las diferentes implementaciones hacen estas cosas de manera diferente; aquellos preocupados por el atasco de memoria evitarán almacenar datos de control cerca de donde el usuario puede causar estragos.

Cuando llama (4,6), solo tiene acceso confiable a 24 bytes de datos. Incluso si su implementación le proporciona valores de retorno que están separados por 32 bytes, no puede usar con seguridad más de los 24 bytes que solicitó. Y las versiones de depuración de malloc() observarán si escribe fuera de los límites que solicitó.

+0

Eso solo muestra que hay al menos 4 bytes de sobrecarga y que las asignaciones están cuantificadas. Cada asignación de memoria normalmente incluye cierta sobrecarga (por ejemplo, el tamaño del bloque que se asignó se escribe justo antes del puntero devuelto; los detalles varían según la implementación). –

+0

Puntos tomados. Supongo que no lo dije claramente, pero quería mostrar que el 'efecto' de calloc (a, b) y calloc (b, a) es exactamente el mismo. – PolyThinker

10

Para las excelentes respuestas publicadas, quiero añadir un punto de diferencia mayor entre el uso de calloc(nelem, elsize) frente malloc(nelem * elsize): implementaciones de calidad de calloc se asegurará de que si su nelem y elsize eran lo suficientemente grande como para causar un desbordamiento de enteros cuando se multiplican juntos, fallará en lugar de causar una asignación de tamaño insuficiente, como lo haría una invocación ingenua al malloc.

Solo esta característica sería suficiente para mí para preferir calloc a malloc. Background reading.

1

Hay otra manera de analizar esta cuestión.

GNU C Library define calloc así:

void * __libc_calloc (size_t n, size_t elem_size) 
{ 
    // ... (declarations) 

    /* size_t is unsigned so the behavior on overflow is defined. */ 
    bytes = n * elem_size; 
#define HALF_INTERNAL_SIZE_T \ 
    (((INTERNAL_SIZE_T) 1) << (8 * sizeof (INTERNAL_SIZE_T)/2)) 
    if (__builtin_expect ((n | elem_size) >= HALF_INTERNAL_SIZE_T, 0)) 
    { 
     if (elem_size != 0 && bytes/elem_size != n) 
     { 
      __set_errno (ENOMEM); 
      return 0; 
     } 
    } 

    void *(*hook) (size_t, const void *) = atomic_forced_read (__malloc_hook); 
    if (__builtin_expect (hook != NULL, 0)) 
    { 
     sz = bytes; 
     mem = (*hook)(sz, RETURN_ADDRESS (0)); 
     if (mem == 0) 
     return 0; 

     return memset (mem, 0, sz); 
    } 

    sz = bytes; 

    // ...more stuff, but no mention of n & elem_size anymore 
} 

Por lo tanto, al menos en glibc estas dos llamadas tienen efecto idéntico.

Cuestiones relacionadas