2010-11-21 10 views
21

ho sempre programmato in Java, che è probabilmente il motivo per cui sono così confuso su questo:Sono molto confuso su malloc() e calloc() su C

In Java dichiaro un puntatore:

int[] array 

e inizializzare o assegnarlo qualche ricordo:

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

Ora, in C, è tutto così confuso. In un primo momento ho pensato che fosse facile come dichiarandolo:

int array[] 

e l'inizializzazione o assegnandolo qualche ricordo:

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

A meno che non mi sbaglio, tutto quanto sopra è equivalente Java-C , destra?

Poi, oggi ho incontrato un codice in cui ho trovato il seguente:

pthread_t tid[MAX_OPS]; 

e alcune righe sotto, senza alcun tipo di inizializzazione ...

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

Sorprendentemente (almeno a io), il codice funziona! Almeno in Java, ciò restituirebbe una bella "NullPointerException"!

Così, al fine di:

  1. Sono corretto con tutte le "traduzioni" Java-C?

  2. Perché funziona il codice?

  3. C'è qualche differenza tra l'utilizzo di malloc(n*sizeof(int)) e calloc(n,sizeof(int))?

Grazie in anticipo

risposta

32

Non è possibile assegnare memoria a un array. Un array ha una dimensione fissa, per l'intera durata della vita. Un array non può mai essere nullo. Un array non è un puntatore.

malloc restituisce l'indirizzo in un blocco di memoria che è riservato per il programma. Non è possibile "assegnare" quello (essendo il blocco di memoria) a un array, ma è possibile memorizzare l'indirizzo di questo blocco di memoria in un puntatore: fortunatamente, la sottoscrizione dell'array viene definita tramite puntatori, quindi è possibile "utilizzare puntatori come array" , per esempio

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

Quando si dichiara una matrice senza dimensione (cioè array[]), significa semplicemente la dimensione della matrice è determinata dalla lista di inizializzazione. Questo è

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

Provare a dichiarare un array senza una dimensione e senza un inizializzatore è un errore.


Il codice pthread_t tid[MAX_OPS]; dichiara un array chiamato tid di tipo pthread_t e di dimensioni MAX_OPS.

Se la matrice ha memoria automatica (vale a dire la dichiarazione è all'interno di una funzione e non statica, non globale), quindi ogni elemento di array ha un valore indeterminato (e causerebbe un comportamento non definito che tenta di leggere tale valore). Fortunatamente, tutto ciò che la chiamata alla funzione fa è che prende l'indirizzo del primo elemento dell'array come primo parametro e probabilmente lo inizializza (l'elemento) all'interno della funzione.


La differenza di calloc e malloc è che il blocco di memoria che calloc rendimenti è inizializzato a zero.Questo è;

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

La differenza tra

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

è che array ha memorizzazione automatica, (viene memorizzato sulla pila), ed è "pubblicato" dopo che passa nell'ambito. ptr, tuttavia, (viene memorizzato su heap), viene assegnato dinamicamente e deve essere free d dal programmatore.

+1

Il primo paragrafo presenta affermazioni pericolosamente ambigue. L'OP non stava cercando di assegnare memoria a un array, stava tentando di assegnare un (void *), il ritorno da malloc() a un array e se quell'array era stato un int * Array [i], probabilmente in un per il ciclo {}, funzionerebbe bene, ed è la base per il modo in cui gli array dinamici e multidimensionali vengono allocati dall'heap. Inoltre, C99 supporta array di dimensioni variabili allocati fuori dallo stack, una funzione utilizzata da pochi programmatori C, la maggior parte preferendo alloca(), me incluso. http://stackoverflow.com/q/1018853/2548100 – user2548100

+1

calloc() è praticamente solo memset (malloc (n * mysize), 0, (n * mysize)). Soprattutto perché C usa stringhe con terminazione null, calloc() è molto utile, specialmente quando si visualizzano le stringhe in un debugger, che in genere mostra la stringa solo fino al terminatore null. Se stai solo indicando C, usa calloc al posto di malloc, ti farà risparmiare molti errori di stringa C non terminati che potrebbero causare il crash del tuo programma. Per codice di produzione/rilascio, utilizzare calloc() solo quando è effettivamente necessario inizializzare il buffer/array/vettore su (_int8) 0. – user2548100

+7

Solo per concludere e per completezza, una matrice è un puntatore. In effetti, qualsiasi nome di matrice in C è esattamente, precisamente un puntatore alla base del primo byte del primo oggetto nell'array e niente di più. Per le persone che provengono da Java,.Net, ecc., È utile sapere che C mantiene il tipo di oggetti/variabili completamente separati dalla memoria allocata per tenerli. Questo è il motivo per cui puoi lanciare un puntatore come int, creare UNION, ecc. Molto, molto flessibile, ma pericoloso per i noobies. Quando si assegna un array int, la sua giusta archiviazione in una posizione. Puoi mettere tutto ciò che ti piace in quel magazzino. – user2548100

-2

Trovo utile quando si sta programmando in C (al contrario di C++) per indicare * array esplicitamente, per ricordare che c'è un puntatore che può essere spostato. Quindi vorrei iniziare da riformulare il vostro esempio come:

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

La prima rende chiaro che c'è qualcosa che si chiama serie, che punta a un blocco di memoria che contiene una matrice 0, 1 e 2. puo' essere spostato da qualche parte.

Il tuo prossimo codice: pthread_t tid [MAX_OPS];

In effetti causa un array con sizeof (pthread_t) * MAX_OPS da allocare. Ma non alloca un puntatore chiamato * tid. C'è un indirizzo della base dell'array, ma non puoi spostarlo altrove.

Il tipo ptherad_t è in realtà una copertina per un puntatore. Quindi tid sopra è in realtà una matrice di puntatori. E sono tutti allocati staticamente ma non sono inizializzati.

Il pthread_create prende la posizione all'inizio dell'array (&tid[0]), che è un puntatore, e assegna un blocco di memoria per contenere la struttura di dati pthread. Il puntatore è impostato per puntare alla nuova struttura dati e la struttura dati è allocata.

L'ultima domanda --- la differenza tra malloc(n*sizeof(int)) e calloc(n,sizeof(int)) è che il successivo inizializza ogni byte su 0, mentre il primo no.

+0

Quindi, se dichiaro: int array [] ha la memoria già allocata? È quindi la stessa cosa che dichiarare il puntatore e quindi usare malloc? grazie ancora – bluehallu

+0

@Hallucynogenyc: No, non è lo stesso. l'array int [size] viene assegnato fuori dallo stack. int array [] = malloc() si trova nell'heap. – Puppy

+2

In C, la prima di queste 3 righe è semplicemente * non valida *. Non verrà compilato. –

0

2 - Questa dichiarazione di matrice è statica:

pthread_t tid[MAX_OPS]; 

Non abbiamo bisogno di allocare blocco di memoria, invece di allocazione dinamica:

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

Non dimenticare di liberare la memoria:

3 - La differenza tra malloc e calloc è calloc allocare un blocco di memoria per un array e inizializza tutti i bit a 0.

+0

Quindi quale sarebbe la differenza tra il primo e il secondo? E perché stai lanciando a un puntatore la seconda linea? Scusa se suono stupido, ma questo è tutto nuovo per me ... – bluehallu

+0

Ok, ho appena visto perché stai lanciando. Eppure, c'è qualche differenza pratica tra la prima e la seconda riga a parte da cui puoi "spostare" il puntatore su qualsiasi cosa tu voglia? – bluehallu

+0

Una dichiarazione statica è più sicura di una dinamica, ma non è possibile riallocare il blocco di memoria per modificarne le dimensioni. –

1

C offre allocazione di memoria statica e dinamica: è possibile allocare gli array dallo stack o nella memoria eseguibile (gestita dal compilatore). Questo è proprio come in Java, puoi allocare un int nello stack o un intero sullo heap. Le matrici in C sono come qualsiasi altra variabile dello stack: vanno fuori ambito, ecc. Nel C99 possono anche avere una dimensione variabile, sebbene non possano essere ridimensionate.

La differenza principale tra {} e malloc/calloc è che gli array {} sono allocati staticamente (non hanno bisogno di essere liberati) e automaticamente inizializzati per te, mentre gli array malloc/calloc devono essere liberati esplicitamente e devi inizializzarli in modo esplicito. Ma naturalmente, gli array malloc/calloc non escono dal campo di applicazione e puoi (a volte) realloc().

+1

Gli array sono statici solo se fuori da qualsiasi funzione o contrassegnati in modo esplicito 'static'; altrimenti sono automaticamente –

3

Ti manca tre molto semplice e serrare argomenti C (e fuorviante!):

  • la differenza tra array e puntatori
  • la differenza tra l'allocazione statica e dinamica
  • la differenza dalle variabili che dichiarano sullo stack o sul mucchio

Se si scrive int array[] = malloc(3*sizeof(int)); si otterrebbe un errore di compilazione (qualcosa come 'identificatore': l'inizializzazione dell'array richiede le parentesi graffe).

Ciò significa che dichiara una matrice permette di inizializzazione solo statico:

  • int array[] = {1,2,3}; che riserva 3 interi contigui in pila;
  • int array[3] = {1,2,3}; che è lo stesso del precedente;
  • int array[3]; che riserva ancora 3 interi contigui sullo stack, ma non li (il contenuto sarà spazzatura casuale)
  • int array[4] = {1,2,3}; quando la lista di inizializzazione non inizializzare tutti gli elementi, il resto sono impostato su 0 (non inizializza C99 §6.7.8/19): in questo caso si otterrà 1,2,3,0

si noti che in tutti questi casi non si è allocazione nuova memoria, si sta solo utilizzando la memoria già impegnato in pila. Si verificherebbe un problema solo se lo stack è pieno (suppongo che sarebbe uno stack over ). Per questo motivo dichiarare int array[]; sarebbe sbagliato e privo di significato.

Per utilizzare malloc è necessario dichiarare un puntatore: int* array.

Quando si scrive int* array = malloc(3*sizeof(int)); si sta effettivamente facendo tre operazioni:

  1. int* array dice al compilatore di riservare un puntatore sullo stack (una variabile intera che contiene un indirizzo di memoria)
  2. malloc(3*sizeof(int)) assegna sul mucchio 3 interi contigui e restituisce l'indirizzo del primo
  3. = assegna copie che restituiscono il valore (l'indirizzo del primo intero che hai assegnato) alla variabile del puntatore

Quindi, per tornare alla tua domanda:

pthread_t tid[MAX_OPS]; 

è un array in pila, in modo che non ha bisogno di essere assegnati (se MAX_OPS è, diciamo, 16 poi sullo stack sarà essere riservato il numero di byte contigui necessari per adattarsi a 16 pthread_t). Il contenuto di questa memoria sarà inutile (le variabili di stack non vengono inizializzate a zero), ma pthread_create restituisce un valore nel suo primo parametro (un puntatore a una variabile pthread_t) e ignora qualsiasi contenuto precedente, quindi il codice è corretto.

+4

per 'int array [4]', sono tutti inizializzati. Quando l'elenco di inizializzazione non inizializza tutti gli elementi, il resto è impostato su 0/NULL (C99 §6.7.8/19). –

+0

Questo è confuso; "heap" e "allocazione dinamica" si riferiscono alla stessa cosa. "inizializzazione statica" significa inizializzare variabili statiche, il che non si verifica quando si parla di cosiddette variabili "stack". Il tipo di allocazione in 'int array [3];' all'interno di una funzione, è "allocazione automatica" (o "stack" in modo informale, alcuni sistemi non hanno uno stack), non "statico". –