2010-11-21 18 views
21

Siempre he programado en Java, que es probablemente por qué estoy tan confundido acerca de esto:Estoy muy confundido acerca de malloc() y calloc() en C

En Java declaro un puntero:

int[] array 

e inicializar o asignarla parte de la memoria:

int[] array = {0,1,0} 
int[] array = new int[3] 

Ahora, en C, que es todo tan confuso. Al principio pensé que era tan fácil como la que se declara que:

int array[] 

e inicializar o cederla parte de la memoria:

int array[] = {0,1,0} 
int array[] = malloc(3*sizeof(int)) 
int array[] = calloc(3,sizeof(int)) 

A menos que me equivoque, todo lo anterior es equivalente Java-C , ¿derecho?

Entonces, hoy me encontré con un código en el que me encontré con lo siguiente:

pthread_t tid[MAX_OPS]; 

y algunas líneas de abajo, sin ningún tipo de inicialización ...

pthread_create(&tid[0],NULL,mou_usuari,(void *) 0); 

sorprendente (al menos a yo), el código funciona! ¡Al menos en Java, devolvería una buena "NullPointerException"!

Así, en orden:

  1. Estoy en lo correcto con todas las "traducciones" Java-C?

  2. ¿Por qué funciona ese código?

  3. ¿Hay alguna diferencia entre usar malloc(n*sizeof(int)) y calloc(n,sizeof(int))?

Gracias de antemano

Respuesta

32

No puede asignar memoria a una matriz. Una matriz tiene un tamaño fijo, durante toda su vida útil. Una matriz nunca puede ser nula. Una matriz no es un puntero.

malloc devuelve la dirección a un bloque de memoria que está reservado para el programa. No puede "asignar" eso (siendo el bloque de memoria) a una matriz, pero puede almacenar la dirección de este bloque de memoria en un puntero: afortunadamente, la suscripción de matriz se define a través de punteros, por lo que puede "usar punteros como matrices" , p.ej

int *ptr = malloc(5 * sizeof *ptr); 
ptr[2] = 5; // access the third element "of ptr" 
free(ptr); // always free at the end 

Cuando se declara una matriz sin un tamaño (es decir array[]), simplemente significa el tamaño de la matriz se determina a partir de la lista de inicialización. Es

int array[] = {1, 2, 3, 4, 5}; // is equal to 
int array[5] = {1, 2, 3, 4, 5}; 

Intentar declarar una matriz sin un tamaño y sin un inicializador es un error.


El código pthread_t tid[MAX_OPS]; declara una matriz llamada tid de tipo pthread_t y de un tamaño MAX_OPS.

Si la matriz tiene almacenamiento automático (es decir, la declaración está dentro de una función y no estática, no global), cada uno de los elementos de la matriz tiene un valor indeterminado (y causaría un comportamiento indefinido al intentar leer dicho valor). Afortunadamente, todo lo que hace la llamada de función es que toma la dirección del primer elemento de la matriz como primer parámetro, y probablemente la inicialice (el elemento) dentro de la función.


La diferencia de calloc y malloc es que el bloque de memoria que calloc rendimientos se inicializa a cero.Es decir;

int *ptr = calloc(5, sizeof *ptr); 
// is somewhat equal to 
int *ptr = malloc(5 * sizeof *ptr); 
memset(ptr, 0, 5 * sizeof *ptr); 

La diferencia entre

int *ptr = malloc(5 * sizeof *ptr); 
// and 
int array[5]; 

es que array tiene almacenamiento automático, (se almacena en la pila), y se "libera" después de que sale del ámbito. ptr, sin embargo, (se almacena en el montón), se asigna dinámicamente y debe ser free d por el programador.

+1

El 1er párrafo tiene algunas afirmaciones peligrosamente ambiguas. El OP no intentaba asignar memoria a una matriz, estaba intentando asignar un (vacío *), el retorno de malloc() a una matriz, y si esa matriz había sido una int * Matriz [i], probablemente en una para {} loop, funcionaría bien, y es la base de cómo las matrices dinámicas y multidimensionales se asignan fuera del montón. Además, C99 admite matrices de tamaño variable asignadas fuera de la pila, una característica que usan pocos programadores en C, la mayoría prefiere alloca(), incluido yo mismo. http://stackoverflow.com/q/1018853/2548100 – user2548100

+1

calloc() es prácticamente memset (malloc (n * mysize), 0, (n * mysize)). Especialmente porque C usa cadenas terminadas en nulo, calloc() es muy útil, especialmente cuando se ven cadenas en un depurador, que típicamente muestra la cadena solo hasta el terminador nulo. Si solo está declarando con C, use calloc en lugar de malloc, le ahorrará la posibilidad de cometer muchos errores de cadena C no terminados que pueden, y probablemente bloquearán, su programa. Para el código de producción/liberación, use calloc() solo cuando realmente necesite inicializar el búfer/matriz/vector a (_int8) 0. – user2548100

+7

Solo para concluir, y para completar, una matriz ES un puntero. De hecho, cualquier nombre de matriz en C es exactamente, precisamente, un puntero a la base del primer byte del primer objeto en la matriz, y nada más. Para las personas que vienen de Java,.Net, etc., es útil saber que C mantiene el tipo de objetos/variables completamente separados del almacenamiento asignado para mantenerlos. Es por eso que puedes lanzar un puntero como int, crear UNIONs, etc. Muy, muy flexible, pero peligroso para noobies. Cuando asigna una matriz int, solo almacena en una ubicación. Puedes poner lo que quieras en ese almacenamiento. – user2548100

-2

que les resulta útil cuando se está programando en C (a diferencia de C++) para indicar * array de forma explícita, recordar que no es un puntero que se puede mover alrededor. Así que me gustaría empezar por reformular su ejemplo, como:

int array[] = {0,1,2}; 
int *array = malloc(3*sizeof(int)); 
int *array = calloc(3,sizeof(int)); 

La primera deja claro que hay algo llamado matriz que está apuntando a un bloque de memoria que contiene una matriz 0, 1 y 2. puede' ser movido a otro lugar

Tu próximo código: pthread_t tid [MAX_OPS];

De hecho, causa una matriz con sizeof (pthread_t) * MAX_OPS a asignar. Pero no asigna un puntero llamado * tid. Hay una dirección de la base de la matriz, pero no se puede mover a otra parte.

El tipo ptherad_t es en realidad una cubierta para un puntero. Entonces tid arriba es en realidad una matriz de punteros. Y están todos asignados estáticamente pero no están inicializados.

El pthread_create toma la ubicación al comienzo de la matriz (&tid[0]), que es un puntero, y asigna un bloque de memoria para contener la estructura de datos pthread. El puntero se configura para que apunte a la nueva estructura de datos y se asigna la estructura de datos.

Su última pregunta --- la diferencia entre malloc(n*sizeof(int)) y calloc(n,sizeof(int)) es que cuanto más tarde se inicializa cada byte a 0, mientras que el primero no lo hace.

+0

Entonces, si declaro: int array [] ¿Ya tiene memoria asignada? Entonces, ¿es lo mismo que declarar el puntero y luego usar malloc? gracias de nuevo – bluehallu

+0

@Hallucynogenyc: No, no es lo mismo. int array [size] se asigna fuera de la pila. int array [] = malloc() está en el montón. – Puppy

+2

En C, la primera de esas 3 líneas es simplemente * no válida *. No compilará –

0

2 - Esta declaración de matriz es estática:

pthread_t tid[MAX_OPS]; 

No necesitamos asignar bloque de memoria, en lugar de la asignación dinámica:

pthread_t *tid = (pthread_t *)malloc(MAX_OPS * sizeof(pthread_t)); 

No se olvide de liberar la memoria:

free(tid); 

3 - La diferencia entre malloc y calloc es calloc asignar un bloque de memoria para una matriz e inicializa todos sus bits en 0.

+0

Entonces, ¿cuál sería la diferencia entre el primero y el segundo? ¿Y por qué estás lanzando a un puntero la segunda línea? Lo siento si sueno estúpido, pero esto es nuevo para mí ... – bluehallu

+0

Ok, acabo de ver por qué estás lanzando. Aún así, ¿hay alguna diferencia práctica entre la primera y la segunda línea, a partir de la cual puede "mover" el puntero a cualquier cosa que desee? – bluehallu

+0

Una declaración estática es más segura que una dinámica, pero no puede reasignar su bloque de memoria para cambiar su tamaño. –

1

C ofrece asignación de memoria estática, así como dinámica: puede asignar matrices fuera de la pila o en la memoria ejecutable (administrada por el compilador). Esto es exactamente lo mismo que en Java, puede asignar un int en la pila o un entero en el montón. Las matrices en C son como cualquier otra variable de pila: salen del alcance, etc. En C99 también pueden tener un tamaño variable, aunque no se pueden cambiar de tamaño.

La principal diferencia entre {} y malloc/calloc es que {} las matrices están asignadas estáticamente (no necesitan liberación) y se inicializan automáticamente, mientras que las matrices malloc/calloc deben liberarse explícitamente y debe inicializarlas explícitamente. Pero, por supuesto, las matrices malloc/calloc no salen de alcance y usted puede (a veces) realloc() ellas.

+1

Las matrices solo son estáticas si están fuera de cualquier función o están explícitamente marcadas como 'estáticas'; de lo contrario, son automáticos –

3

Te faltan tres muy básica y apriete los temas C (y engañosa!):

  • la diferencia entre la matriz y punteros
  • la diferencia entre la asignación estática y dinámica
  • la diferencia con respecto a la declaración de variables en la pila o en el montón

Si se escribe int array[] = malloc(3*sizeof(int)); que se obtiene un error de compilación (algo así como 'identificador': la inicialización del array necesita llaves (llaves)).

Esto significa que la declaración de una matriz permite la inicialización solamente estática:

  • int array[] = {1,2,3}; que se reserva 3 enteros contiguas en la pila;
  • int array[3] = {1,2,3}; que es el mismo que el anterior;
  • int array[3]; que todavía se reserva 3 enteros contiguas en la pila, pero no inicializa ellos (el contenido será basura aleatoria)
  • int array[4] = {1,2,3}; cuando la lista de inicialización no inicializa todos los elementos, el resto se pone a 0 (C99 §6.7.8/19): en este caso obtendrá 1,2,3,0

Tenga en cuenta que en todos estos casos no está asignación de memoria nueva, usted está usando la memoria ya comprometido con la pila. Correrías en un problema solo si la pila está llena (adivínalo, sería desbordamiento de pila). Por esta razón, declarar int array[]; sería incorrecto y sin sentido.

Para usar malloc debe declarar un puntero: int* array.

Cuando se escribe int* array = malloc(3*sizeof(int)); en realidad se está haciendo tres operaciones:

  1. int* array indica al compilador que reservar un puntero en la pila (una variable entera que contiene una dirección de memoria)
  2. malloc(3*sizeof(int)) asigna en el montón 3 enteros contiguos y devuelve la dirección del primero
  3. = asigna copias que devuelven valor (la dirección del primer entero que ha asignado) a su variable de puntero

Por lo tanto, para volver a su pregunta:

pthread_t tid[MAX_OPS]; 

es una matriz en la pila, por lo que no necesitará ser asignado (si MAX_OPS es, digamos, 16 a continuación, en la pila se se debe reservar el número de bytes contiguos necesarios para caber 16 pthread_t). El contenido de esta memoria será basura (las variables de pila no se inicializan a cero), pero pthread_create devuelve un valor en su primer parámetro (un puntero a una variable pthread_t) y descarta cualquier contenido anterior, por lo que el código es correcto.

+4

para 'int array [4]', todos están inicializados. Cuando la lista de inicializadores no inicializa todos los elementos, el resto se establece en 0/NULL (C99 §6.7.8/19). –

+0

Esto es confuso; "montón" y "asignación dinámica" se refieren a lo mismo. "Inicialización estática" significa inicialización de variables estáticas, que no es el caso cuando se habla de las llamadas variables de "pila". El tipo de asignación en 'int array [3];' dentro de una función, es "asignación automática" (o "pila" informalmente, algunos sistemas no tienen una pila), no "estática". –

Cuestiones relacionadas