C Programmazione

malloc in linguaggio C

malloc in linguaggio C
Puoi venire qui per due motivi: vuoi allocare dinamicamente i contenuti o vuoi saperne di più su come funziona malloc. In entrambi i casi, sei nel posto giusto! L'allocazione dinamica è un processo che accade spesso ma generalmente non lo usiamo noi stessi: la stragrande maggioranza dei linguaggi di programmazione gestisce la memoria per te poiché è un lavoro duro e se non lo fai correttamente, ci sono implicazioni per la sicurezza.

Tuttavia, se stai eseguendo C, C++ o codice assembly o se implementi un nuovo modulo esterno nel tuo linguaggio di programmazione preferito, dovrai gestire tu stesso l'allocazione dinamica della memoria.

Cos'è l'allocazione dinamica?? Perché ho bisogno di malloc?

Bene, in tutte le applicazioni, quando crei una nuova variabile - viene spesso chiamato dichiarare una variabile - hai bisogno di memoria per memorizzarlo. Poiché il tuo computer è ai giorni nostri, può eseguire più di un'applicazione alla volta e quindi, ogni applicazione dovrebbe dire al tuo sistema operativo (qui Linux) che ha bisogno di quella quantità di memoria. Quando scrivi questo tipo di codice:

#includere
#includere
#define DISK_SPACE_ARRAY_LENGTH 7
void getFreeDiskSpace(int statsList[], size_t listLength)
ritorno;

int main()
/* Contiene lo spazio libero su disco degli ultimi 7 giorni. */
int freeDiskSpace[DISK_SPACE_ARRAY_LENGTH] = 0;
getFreeDiskSpace(freeDiskSpace, DISK_SPACE_ARRAY_LENGTH);
restituire EXIT_SUCCESS;

L'array freeDiskSpace ha bisogno di memoria, quindi dovrai chiedere a Linux l'approvazione per ottenere un po' di memoria. Tuttavia, poiché è ovvio durante la lettura del codice sorgente che avrai bisogno di un array di 7 int, il compilatore lo chiede automaticamente a Linux e lo allocherà nello stack. Ciò significa fondamentalmente che questa memoria viene distrutta quando si restituisce la funzione in cui è dichiarata la variabile. Ecco perché non puoi farlo:

#includere
#includere
#define DISK_SPACE_ARRAY_LENGTH 7
int* getFreeDiskSpace()
int statsList[DISK_SPACE_ARRAY_LENGTH] = 0;
/* PERCHÉ LO STIAMO FACENDO?! statsList sarà DISTRUTTO! */
restituisce l'elenco delle statistiche;

int main()
/* Contiene lo spazio libero su disco degli ultimi 7 giorni. */
int *freeDiskSpace = NULL;
freeDiskSpace = getFreeDiskSpace();
restituire EXIT_SUCCESS;

Vedi più facilmente il problema ora? Quindi, vuoi concatenare due stringhe. In Python e JavaScript, dovresti fare:

nuovoStr = str1 + str2

Ma come sai, in C non funziona così. Quindi, per creare un URL, ad esempio, devi concatenare due stringhe, come il percorso dell'URL e il nome di dominio domain. In C, abbiamo strcat, giusto, ma funziona solo se hai un array con spazio sufficiente.

Sarai tentato di conoscere la lunghezza della nuova stringa usando strlen, e avresti ragione. Ma allora, come chiederesti a Linux di riservare questa quantità sconosciuta di memoria?? Il compilatore non può aiutarti: lo spazio esatto che vuoi allocare è noto solo in fase di esecuzione. È esattamente lì che ti serve l'allocazione dinamica e malloc.

Scrivere la mia prima funzione C usando malloc

Prima di scrivere il codice, una piccola spiegazione: malloc ti consente di allocare un numero specifico di byte per l'utilizzo dell'applicazione. È davvero semplice da usare: chiami malloc con il numero di byte di cui hai bisogno e restituisce un puntatore alla tua nuova area che Linux ti ha riservato.

Hai solo 3 responsabilità:

  1. Controlla se malloc restituisce NULL. Ciò accade quando Linux non ha abbastanza memoria da fornire.
  2. Libera le tue variabili una volta inutilizzate. Altrimenti sprecherai memoria e rallenterà la tua applicazione.
  3. Non usare mai la zona di memoria dopo aver liberato la variabile.

Se segui tutte queste regole, tutto andrà bene e l'allocazione dinamica ti risolverà molti problemi. Poiché scegli quando liberare la memoria, puoi anche restituire in sicurezza una variabile allocata con malloc. Solo, non dimenticare di liberarlo!

Se ti chiedi come liberare una variabile, è con la funzione free. Chiamalo con lo stesso puntatore di quello che malloc ti ha restituito e la memoria viene liberata.

Lascia che te lo mostri con l'esempio concat:

#includere
#includere
#includere
/*
* Quando si chiama questa funzione, non dimenticare di controllare se il valore restituito è NULL
* Se non è NULL, devi chiamare free sul puntatore restituito una volta che il valore
* non è più utilizzato.
*/
char* getUrl(const char* const baseUrl, const char* const toolPath)
size_t finalUrlLen = 0;
char* finalUrl = NULL;
/* Controllo di sicurezza. */
if (baseUrl == NULL || toolPath == NULL)
restituisce NULL;

finalUrlLen = strlen(baseUrl) + strlen(toolPath);
/* Non dimenticare '\0', quindi il + 1. */
finalUrl = malloc(sizeof(char) * (finalUrlLen + 1));
/* Seguendo le regole di malloc... */
if (finalUrl == NULL)
restituisce NULL;

strcpy(finalUrl, baseUrl);
strcat(finalUrl, toolPath);
restituisce finalUrl;

int main()
char* googleImages = NULL;
googleImages = getUrl("https://www.Google.com", "/imghp");
if (googleImages == NULL)
restituisce EXIT_FAILURE;

puts("URL strumento:");
puts(googleImages);
/* Non è più necessario, liberalo. */
gratuito(googleImages);
googleImages = NULL;
restituire EXIT_SUCCESS;

Quindi vedi un esempio pratico per l'utilizzo di allocazioni dinamiche. Innanzitutto, evito insidie ​​come dare il valore di ritorno getUrl direttamente alla funzione puts. Quindi, mi prendo anche il tempo per commentare e documentare il fatto che il valore restituito dovrebbe essere liberato correttamente. Controllo anche i valori NULL ovunque in modo che qualsiasi cosa inaspettata possa essere catturata in sicurezza invece di arrestare l'applicazione.

Infine, mi prendo la massima cura di liberare la variabile e quindi impostare il puntatore su NULL. Ciò evita di essere tentati di utilizzare - anche per errore - la zona di memoria ora liberata. Ma come puoi vedere, è facile liberare una variabile.

Potresti notare che ho usato sizeof in malloc. Permette di sapere quanti byte sta usando un carattere e chiarisce l'intento nel codice in modo che sia più leggibile. Per char, sizeof(char) è sempre uguale a 1, ma se usi invece un array di int, funziona esattamente allo stesso modo. Ad esempio, se devi prenotare 45 int, fai semplicemente:

fileSizeList = malloc(sizeof(int) * 45);

In questo modo, vedi rapidamente quanto vuoi allocare, ecco perché consiglio sempre il suo utilizzo.

Come funziona malloc sotto il cofano?

malloc e free sono, infatti, funzioni incluse in tutti i programmi C che parleranno con Linux per tuo conto. Semplificherà anche l'allocazione dinamica perché, all'inizio, Linux non ti consente di allocare variabili di tutte le dimensioni.

Linux fornisce infatti due modi per ottenere più memoria: sbrk e mmap. Entrambi hanno dei limiti e uno di questi è: puoi allocare solo quantità relativamente grandi, come 4.096 byte o 8.192 byte. Non puoi richiedere 50 byte come ho fatto nell'esempio, ma non puoi nemmeno richiedere 5.894 byte.

Questo ha una spiegazione: Linux deve mantenere una tabella in cui indica quale applicazione ha riservato quale zona di memoria. E anche questa tabella usa lo spazio, quindi se ogni byte avesse bisogno di una nuova riga in questa tabella, sarebbe necessaria una grande quota di memoria. Ecco perché la memoria è suddivisa in grandi blocchi di, ad esempio, 4.096 byte, e proprio come non puoi comprare 2 arance e mezzo in una drogheria, non puoi chiedere mezzo blocco.

Quindi malloc prenderà questi grandi blocchi e ti darà una piccola fetta di questi blocchi di memoria ogni volta che lo chiami. Inoltre, se hai liberato poche variabili, ma non abbastanza da giustificare la liberazione di un intero blocco, il sistema malloc potrebbe mantenere i blocchi e riciclare le zone di memoria quando chiami di nuovo malloc. Questo ha il vantaggio di rendere malloc più veloce, tuttavia la memoria riservata da malloc non può essere utilizzata in nessun'altra applicazione, mentre il programma attualmente non la sta utilizzando in realtà.

Ma malloc è intelligente: se chiami malloc per allocare 16 MiB o una grande quantità, malloc probabilmente chiederà a Linux blocchi completi dedicati solo a questa grande variabile usando mmap. In questo modo, quando chiami gratis, è più probabile che eviti quello spreco di spazio. Non preoccuparti, malloc sta facendo un lavoro molto migliore nel riciclaggio rispetto agli umani con la nostra spazzatura!

Conclusione

Penso che ora tu capisca meglio come funziona tutto questo. Naturalmente, l'allocazione dinamica è un argomento importante e penso che possiamo scrivere un libro completo sull'argomento, ma questo articolo dovrebbe farti sentire a tuo agio con il concetto sia in generale che con consigli pratici sulla programmazione.

Giochi rimasterizzati in HD per Linux che non hanno mai avuto una versione Linux prima
Molti sviluppatori ed editori di giochi stanno realizzando remaster HD di vecchi giochi per prolungare la vita del franchise, per favore i fan richied...
Come utilizzare AutoKey per automatizzare i giochi Linux
AutoKey è un'utilità di automazione desktop per Linux e X11, programmata in Python 3, GTK e Qt. Utilizzando la sua funzionalità di scripting e MACRO, ...
Come mostrare il contatore FPS nei giochi Linux
I giochi Linux hanno avuto una grande spinta quando Valve ha annunciato il supporto Linux per il client Steam e i loro giochi nel 2012. Da allora, mol...