resueltos - malloc sizeof
Estoy muy confundido acerca de malloc() y calloc() en C (5)
2 - Esta declaración de matriz es estática:
pthread_t tid[MAX_OPS];
No necesitamos asignar bloque de memoria, en lugar de asignación dinámica:
pthread_t *tid = (pthread_t *)malloc( MAX_OPS * sizeof(pthread_t) );
No te olvides de liberar la memoria:
free(tid);
3 - La diferencia entre malloc y calloc es que calloc asigna un bloque de memoria para una matriz e inicializa todos sus bits en 0.
Siempre he programado en Java, y probablemente por eso estoy tan confundido acerca de esto:
En Java declaro un puntero:
int[] array
Inicializarlo o asignarle un poco de memoria:
int[] array = {0,1,0}
int[] array = new int[3]
Ahora, en C, todo es tan confuso. Al principio pensé que era tan fácil como declararlo:
int array[]
e inicializándolo o asignándole algo de memoria:
int array[] = {0,1,0}
int array[] = malloc(3*sizeof(int))
int array[] = calloc(3,sizeof(int))
A menos que esté equivocado, todo lo anterior es equivalente a Java-C, ¿verdad?
Entonces, hoy conocí un código en el que encontré lo siguiente:
pthread_t tid[MAX_OPS];
Y algunas líneas abajo, sin ningún tipo de inicialización ...
pthread_create(&tid[0],NULL,mou_usuari,(void *) 0);
Sorprendentemente (al menos para mí), el código funciona! Al menos en Java, eso devolvería una buena "NullPointerException"!
Entonces, en orden:
¿Estoy en lo cierto con todas las "traducciones" de Java-C?
¿Por qué funciona ese código?
¿Hay alguna diferencia entre usar
malloc(n*sizeof(int))
ycalloc(n,sizeof(int))
?
Gracias por adelantado
C ofrece asignación de memoria estática además de dinámica: puede asignar arreglos fuera de la pila o en la memoria ejecutable (administrada por el compilador). Esto es 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: se salen del alcance, etc. En C99 también pueden tener un tamaño variable, aunque no se pueden redimensionar.
La principal diferencia entre {} y malloc / calloc es que las matrices {} están asignadas estáticamente (no es necesario liberarlas) y se inicializan automáticamente para usted, mientras que las matrices malloc / calloc deben liberarse explícitamente y usted debe inicializarlas explícitamente. Pero, por supuesto, los arreglos malloc / calloc no quedan fuera del alcance y usted puede (a veces) reasignarlos ().
Me resulta útil cuando está programando en C (a diferencia de C ++) para indicar explícitamente la matriz, para recordar que hay un puntero que se puede mover. Entonces, me gustaría comenzar por reformular su ejemplo como:
int array[] = {0,1,2};
int *array = malloc(3*sizeof(int));
int *array = calloc(3,sizeof(int));
El primero deja en claro que hay algo llamado matriz que apunta a un bloque de memoria que contiene una matriz 0, 1 y 2. no se puede mover a ningún otro lugar.
Su siguiente código: pthread_t tid [MAX_OPS];
De hecho, hace que se asigne una matriz con sizeof (pthread_t) * MAX_OPS. Pero no asigna un puntero llamado * tid. Hay una dirección de la base de la matriz, pero no puede moverla a otra parte.
El tipo ptherad_t es en realidad una cubierta para un puntero. Así que tid
anterior es en realidad una serie de punteros. Y todos están asignados estáticamente, pero no están inicializados.
pthread_create
toma la ubicación al principio de la matriz ( &tid[0]
), que es un puntero, y asigna un bloque de memoria para contener la estructura de datos pthread. El puntero está configurado para apuntar a la nueva estructura de datos y la estructura de datos está asignada.
Su última pregunta: la diferencia entre malloc(n*sizeof(int))
y calloc(n,sizeof(int))
es que la última inicializa cada byte a 0
, mientras que la primera no lo hace.
No puedes 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 (que es 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 a la matriz se define a través de los 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 declara una matriz sin un tamaño (es decir, array[]
), simplemente significa que el tamaño de la matriz se determina a partir de la lista de inicializadores. Es decir
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 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 es global), entonces 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 a la 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 entre calloc
y malloc
es que el bloque de memoria que devuelve calloc
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 la array
tiene almacenamiento automático, (se almacena en la pila), y se "libera" después de que está fuera de alcance. ptr
, sin embargo, (se almacena en el montón), se asigna dinámicamente y debe ser free
d por el programador.
Te faltan tres temas muy básicos y estrictos (¡y engañosos!) C:
- La diferencia entre array y punteros.
- La diferencia entre asignación estática y dinámica.
- La diferencia de declarar variables en la pila o en el montón
Si escribe int array[] = malloc(3*sizeof(int));
obtendría un error de compilación (algo así como ''identificador'': la inicialización de la matriz necesita llaves ).
Esto significa que declarar una matriz solo permite la inicialización estática:
-
int array[] = {1,2,3};
que reserva 3 enteros contiguos en la pila; -
int array[3] = {1,2,3};
que es lo mismo que el anterior; -
int array[3];
que aún reserva 3 enteros contiguos en la pila, pero no los inicializa (el contenido será basura aleatoria) -
int array[4] = {1,2,3};
cuando la lista de inicializadores no inicializa todos los elementos, el resto se establece en 0 (C99 §6.7.8 / 19): en este caso obtendrá 1,2,3,0
Tenga en cuenta que en todos estos casos no está asignando nueva memoria, simplemente está utilizando la memoria ya asignada a la pila. Se ejecutaría en un problema solo si la pila está llena (supongo que sería un desbordamiento de pila ). Por esta razón declarando int array[];
Sería incorrecto y sin sentido.
Para usar malloc
tienes que declarar un puntero: int* array
.
Cuando escribe int* array = malloc(3*sizeof(int));
En realidad estás haciendo tres operaciones:
-
int* array
le dice al compilador que reserve un puntero en la pila (una variable entera que contiene una dirección de memoria) -
malloc(3*sizeof(int))
asigna en el montón 3 enteros contiguos y devuelve la dirección del primero -
=
asigna copias de ese valor de retorno (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 necesita ser asignada (si MAX_OPS
es, por ejemplo, 16, en la pila se reservará el número de bytes contiguos necesarios para encajar 16 pthread_t). El contenido de esta memoria será basura (las variables de la 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 está bien.