diff --git a/it/c.md b/it/c.md new file mode 100644 index 00000000..955ab9cd --- /dev/null +++ b/it/c.md @@ -0,0 +1,912 @@ +--- +contributors: + - ["Adam Bard", "http://adambard.com/"] + - ["Árpád Goretity", "http://twitter.com/H2CO3_iOS"] + - ["Jakub Trzebiatowski", "http://cbs.stgn.pl"] + - ["Marco Scannadinari", "https://marcoms.github.io"] + - ["Zachary Ferguson", "https://github.io/zfergus2"] + - ["himanshu", "https://github.com/himanshu81494"] + - ["Joshua Li", "https://github.com/JoshuaRLi"] + - ["Dragos B. Chirila", "https://github.com/dchirila"] + - ["Heitor P. de Bittencourt", "https://github.com/heitorPB/"] +translators: + - ["lele25811", "https://github.com/lele25811"] +filename: learnc-it.c +--- + +Ah, C. Ancora **il** linguaggio per il moderno calcolo ad alte prestazioni. + +C è il linguaggio di più basso livello che la maggior parte dei programmatori utilizzerà mai, +ma compensa ampiamente con la sua velocità pura. +Basta essere consapevoli della gestione manuale della memoria e C ti porterà ovunque tu abbia bisogno + +```c +// Una singola riga di commenti comincia con // - utilizzabile in C99 e superiori + +/* +I commenti multi-riga appaiono come questo. Funzionano anche in C89. +*/ + +/* +I commenti multi-riga non si nidificano /* fai attenzione */ // commento termina con questa linea +*/ // ...non con questa! + +// Costanti: #define +// Le costanti sono scritte in MAIUSCOLO per convenzione +#define DAYS_IN_YEAR 365 + +// Le enumerazioni sono anche sono modi per dichiarare costanti. +// Tutte le dichiarazioni devono finire con un punto e virgola +enum days {SUN, MON, TUE, WED, THU, FRI, SAT}; +// SUN ottiene 0, MON ottiene 1, TUE ottiene 2, etc. + +// Il valore delle enumerazioni può essere anche specificato +enum days {SUN = 1, MON, TUE, WED = 99, THU, FRI, SAT}; +// MON ottiene 2 automaticamente, TUE ottiene 3, etc. +// WED ottiene 99, THU ottiene 100, FRI ottiene 101, etc. + +// Importa le intestazioni con #include +#include +#include +#include + +// I nomi dei file tra dicono al compilatore dove guardare nel tuo sistema. +// di librerie per l'intestazioni. +// Per le tue intestazioni, usa le doppi virgolette invece di parentesi angolari +// e fornisci il percorso +#include "my_header.h" // file locale +#include "../my_lib/my_lib_header.h" // percorso relativo + +// Dichiara le segnature della funzione in anticipo in un file.h o nella parte superiore +// del tuo file .c +void function_1(); +int function_2(void); + +// Almeno, è necessario dichiarare un 'prototipo di funzione' prima del suo utilizzo in qualsiasi funzione. +// Normalmente, i prototipi sono nella parte superiore del file prima di qualsiasi definizione di funzione. +int add_two_ints(int x1, int x2); // prototipo di funzione +// Sebbene 'int add_two_ints(int, int);' è valido (non è necessario nominare gli argomenti), +// si consiglia di nominare anche gli argomenti nel prototipo per un'ispezione più semplice. + +// Prototipi di funzione non sono necessari se la definizione della funzione avviene prima +// qualsiasi altra funzione che chiama quella funzione. Comunque, è pratica standard +// aggiungere sempre il prototipo di funzione a un file di intestazione (*.h) e quindi #define +// il file sopra. Ciò impedisce ogni errore dove una funzione potrebbe essere chiamata +// prima che il compilatore sappia della sua esistenza, dando anche allo sviluppatore +// una intestazione pulita da condividere con il resto del progetto. + +// Il tuo punto di accesso al programma è una funzione chiamata 'main'. Il tipo di ritorno +// può essere qualsiasi, tuttavia molti sistemi operativi si aspettano un tipo di ritorno 'int' +// per l'elaborazione del codice di errore. +int main(void) { + // il tuo programma +} + +// Gli argomenti della riga di comando utilizzati per eseguire il programma vengono anche passati al main +// argc è il numero di argomenti: il nome del programma conta come 1 +// argv è un array di array di caratteri - contiene gli argomenti stessi +// argv[0] = nome del programma +// argv[1] = primo argomento, ecc +int main (int argc, char** argv) +{ + // stampa l'output usanto 'printf', per la formattazione di stampa + // %d è un intero, \n è un a capo + printf("%d\n", 0); // => Stampa 0 + + // prende un input utilizzando 'scanf' + // '&' viene utilizzato per definire la posizione + // dove vogliamo archiviare il valore di input + int input; + scanf("%d", &input); + + /////////////////////////////////////// + // Tipi + /////////////////////////////////////// + + // I compilatori che non sono uniformi a C99 richiedono che le variabili DEVONO essere + // dichiarate nella parte superiore del blocco corrente. + // I compilatori che sono uniformi a C99 permettono la dichiarazione delle variabili vicino + // al punto in cui viene utilizzato il valore. + // Per motivi di tutorial, le variaibli sono dichiarate dinamicamente sotto lo standard coforme a C99. + + // gli int (interi) sono generalmente di 4 byte (usa l'operatore `sizeof` per controllare) + int x_int = 0; + + // gli short sono generalmente di 2 byte (usa l'operatore `sizeof` per controllare) + short x_short = 0; + + // i char (letterali) sono definiti come le unità indirizzabili più piccole per un processore. + // Questo generalmente è 1 byte, ma per alcuni sistemi può essere più, + // (es. per TMS320 da TI è 2 byte). + char x_char = 0; + char y_char = 'y'; // i char sono citati con '' + + // i long sono generalmente di 4 o 8 byte; i lunghi long sono garantiti almeno per essere 8 byte + long x_long = 0; + long long x_long_long = 0; + + // i float sono generalmente 32 bit, numeri a virgola mobile + float x_float = 0.0f; // il suffisso 'f' indica 'floating' per floating point, quindi numeri a virgola mobile + + // i double sono generalmente 64 bit, numeri a virgola mobile + double x_double = 0.0; // i numeri reali senza nessun suffiso sono double + + // i tipi interi possono essere 'unsigned', sono firmati (maggiore o uguale a zero) + unsigned short ux_short; + unsigned int ux_int; + unsigned long long ux_long_long; + + // i char all'interno delle virgolette singole sono numeri interi nel insieme di caratteri della macchina. + '0'; // => 48 nel insieme di caratteri ASCII. + 'A'; // => 65 nel insieme di caratteri ASCII. + + // sizeof(T) ti dà la dimensione di una variabile con tipo T in byte + // sizeof(OBJ) produce la dimensione dell'espressione (variabile, letterale, ecc) + printf("%zu\n", sizeof(int)); // => 4 (sulla maggior parte delle macchine con parole a 4 byte) + + // Se l'argomento del operatore `sizeof` è un espressione, allora il suo argomento + // non viene valutato (trannte VLA, vedi sotto) + // Il valore che produce in questo caso è una costante a tempo di compilazione. + int a = 1; + // size_t è un tipo intero non firmato di almeno 2 byte usati per rappresentare + // la dimensione di un oggetto. + size_t size = sizeof(a++); // a++ non viene valutato + printf("sizeof(a++) = %zu where a = %d\n", size, a); + // la stampa "sizeof(a++) = 4 dove a=1" (su un'archiettura a 32 bit) + + // Gli array devono essere inizializzati con una dimensione fissa + char my_char_array[20]; // Questo array occupa 1 * 20 = 20 bytes + int my_int_array[20]; // Questo array 4 * 20 = 80 bytes + // (assumendo parole a 4 byte) + + // Puoi inizializzare un array di venti int dove sono tutti 0 così: + int my_array[20] = {0}; + // dove la parte '{0}' è chiamata 'Initializer array' (inizializzatore di array) + // Tutti gli elementi (se presenti) oltre quelli nell'inizializzatore sono inizializzati a 0: + int my_array[5] = {1, 2}; + // Quindi my_array ora ha cinque elementi, tutti tranne i primi due sono 0: + // [1, 2, 0, 0, 0] + // NOTE: se non dichiari esplicitamente la dimensione del array + // puoi inizializzarlo comunque sulla stessa riga + int my_array[] = {0}; + // NOTE: quando non si dichiara la lunghezza, la lunghezza è il numero + // degli elementi nell'inizializzatore. Con '{0}' my_array è ora di dimensioni: [0]. + // Per valutare le dimensioni dell'array in fase di esecuzione, + // dividere le dimensioni del byte per le dimensioni del byte del tipo di elemento: + size_t my_array_size = sizeof(my_array) / sizeof(my_array[0]); + // ATTENZIONE: È necessario valutare la dimensione prima di iniziare a passare l'array + // alle funzioni (vedi discussione successiva) perchè gli array vengono 'declassati' + // a puntatori grezzi quando vengono passati alle funzioni + // (quindi l'affermazione sopra produrra il risultato errato all'interno della funzione) + + // Indicizzare un array è come negli altri linguaggi + // o meglio, gli altri linguaggi sono come il C. + my_array[0]; // => 0 + + // Gli array sono immutabili, È solo memorial! + my_array[1] = 2; + printf("%d\n", my_array[1]); // => 2 + + // Nel C99 (e come caratteristica opzionale in C11), gli array a lunghezza variabile (VLA) + // possono essere dichiarati. La dimensione di tale array non deve essere specificata a tempo di compilazione. + printf("Enter the array size: "); // chiede al utente la lunghezza del array + int array_size; + fscanf(stdin, "%d", &array_size); + int var_length_array[array_size]; // dichiarazione di VLA + printf("sizeof array = %zu\n", sizeof var_length_array); + + // Example: + // > Enter the array size: 10 -> (Inserisci la lunghezza del array: 10) + // > sizeof array = 40 -> (sizeof array = 40) + + // Le Stringhe sono solo array di caratteri terminati da un byte null (0x00), + // rappresentato nelle stringhe come il carattere speciale '\0' + // (non dobbiamo includere il byte null nei letterali delle stringhe, + // il compilatore lo inserisce alla fine dell'array per noi). + char a_string[20] = "This is a string"; + printf("%s\n", a_string); // %s formattazione di una stringa + + printf("%d\n", a_string[16]); // => 0 + // i.e., byte #17 è 0 (come sono 18, 19, e 20) + + // Se noi abbiamo caratteri tra le vergolette doppie (""), quei caratteri sono un letterale. + // È di tipo `int` e *non* `char` (per motivi storici) + int cha = 'a'; // ok + char chb = 'a'; // ok lo stesso (implicitamente la conversione da int a char) + + // Array di più dimensioni: + int multi_array[2][5] = { + {1, 2, 3, 4, 5}, + {6, 7, 8, 9, 0} + }; + // accesso agli elementi: + int array_int = multi_array[0][2]; // => 3 + + /////////////////////////////////////// + // Operatori + /////////////////////////////////////// + + // versione corta per molteplici dichiarazioni: + int i1 = 1, i2 = 2; + float f1 = 1.0, f2 = 2.0; + + int b, c; + b = c = 0; + + // Aritmetica semplice + i1 + i2; // => 3 + i2 - i1; // => 1 + i2 * i1; // => 2 + i1 / i2; // => 0 (0.5, ma troncato è 0) + + // Hai bisogno di un cambio di tipo (cast) da `int` a `float` per ottenere un risultato in virgola mobile + (float)i1 / i2; // => 0.5f + i1 / (double)i2; // => 0.5 // Lo stesso per i double + f1 / f2; // => 0.5, più o meno epsilon + + // I numeri a virgola mobile sono definiti da IEEE 754, quindi non possono archiviare perfettamente + // valori esatti. Ad esempio, quanto segue non produce risultati previsti + // perchè 0.1 potrebbe effettivamente essere 0,09999999999 dentro il computer, + // e 0.3 potrebbe essere memorizzato come 0.300000000001. + (0.1 + 0.1 + 0.1) != 0.3; // => 1 (vero) + // e non è associato per ragioni di sopra menzionate. + 1 + (1e123 - 1e123) != (1 + 1e123) - 1e123; // => 1 (vero) + // questa notazione è la notazione scientifica per i numeri: 1e123 = 1*10^123 + + // È importante considerare che la maggior parte dei sistemi utilizza IEEE 754 per + // rappresentare i numeri in virgola mobile, Anche in python viene utilizzato per il calcolo scientifico + // alla fine chiama C che utilizza IEEE754. È mensionato in questo modo non per indicare che l'implementazione + // è scarsa, ma invece come avvertimento per quando si effettuano confronti in virgola mobile + // un po di errore (Epsilon) deve essere considerato. + + // Il modulo è presente, ma fai attenzione se gli argomenti sono negativi + 11 % 3; // => 2 come 11 = 2 + 3*x (x=3) + (-11) % 3; // => -2, come ci si aspetterebbe + 11 % (-3); // => 2 e non -2, ed è abbastanza intuitivo + + // Gli operatori di confronto sono probabilmente famigliari, + // ma non esiste nessun tipo di booleano in C. Usiamo invece 'int'. + // (C99 ha introdotto il tipo _bool fornito in stdbool.h) + // 0 è falso, qualsiasi altra cosa è vera. + // (il confronto tra operatori produce sempre 0 o 1 -> falso o vero) + 3 == 2; // => 0 (falso) + 3 != 2; // => 1 (vero) + 3 > 2; // => 1 + 3 < 2; // => 0 + 2 <= 2; // => 1 + 2 >= 2; // => 1 + + // C non è python - i confronti non sono a catena. + // ATTENZIONE: la riga seguente si compilerà, ma significa '(0 a) 2'. + // Questa espressione è sempre vera, perchè (0 a) potrebbe essere 1 o 0. + // In questo caso è 1, perchè (0 1). + int between_0_and_2 = 0 < a < 2; + // Invece utilizza: + int between_0_and_2 = 0 < a && a < 2; + + // La logica lavora sugli 'int' + !3; // => 0 (Logica not) + !0; // => 1 + 1 && 1; // => 1 (Logica and) + 0 && 1; // => 0 + 0 || 1; // => 1 (Logica or) + 0 || 0; // => 0 + + // Espressione di condizione ternaria ( ? : ) + int e = 5; + int f = 10; + int z; + z = (e > f) ? e : f; // => 10 "if e > f return e, else return f." + + // Incremento e decremento degli operatori: + int j = 0; + int s = j++; // Return j THEN incremento j. (s = 0, j = 1) + s = ++j; // Incremento j THEN return j. (s = 2, j = 2) + // stesso con j-- e --j + + // Operatori bitwise! + ~0x0F; // => 0xFFFFFFF0 (negazione bitwise, "complemento di 1", nel esempio il risultato per int a 32 bit) + 0x0F & 0xF0; // => 0x00 (bitwise AND) + 0x0F | 0xF0; // => 0xFF (bitwise OR) + 0x04 ^ 0x0F; // => 0x0B (bitwise XOR) + 0x01 << 1; // => 0x02 (bitwise spostamento a sinistra (di 1)) + 0x02 >> 1; // => 0x01 (bitwise spostamento a destra (di 1)) + + // Fai attenzione quando si spostano numeri interi: i seguenti sono indefiniti: + // - spostamento nel bit di segno di un numero intero (int a = 1 << 31) + // - spostamento a sinistra di un numero negativo (int a = -1 << 2) + // - spostamento di un offset che è >= la larghezza del tipo di LHS: + // int a = 1 << 32; // oltre la grandezza di a se l'intero è largo 32 bit + + /////////////////////////////////////// + // Strutture di controllo + /////////////////////////////////////// + + if (0) { + printf("I am never run\n"); + } else if (0) { + printf("I am also never run\n"); + } else { + printf("I print\n"); + } + + // Esistono i while loop innestati + int ii = 0; + while (ii < 10) { // qualsiasi valore inferiore a dieci è vero. + printf("%d, ", ii++); // il ii++ incrementa ii DOPO aver utilizzato il suo valore corrente. + } // => stampa "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " + + printf("\n"); + + int kk = 0; + do { + printf("%d, ", kk); + } while (++kk < 10); // il ++kk incrementa kk PRIMA di utilizzare il suo valore corrente. + // => stampa "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " + + printf("\n"); + + // Esistono anche i ciclo for + int jj; + for (jj=0; jj < 10; jj++) { + printf("%d, ", jj); + } // => stampa "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " + + printf("\n"); + + // *****NOTES*****: + // Cicli e funzioni devono avere un corpo. Se nessun corpo è necessario: + int i; + for (i = 0; i <= 5; i++) { + ; // usa le ';' per fungere da corpo (null statement) + } + // oppure + for (i = 0; i <= 5; i++); + + // ramificazione con più scelte: switch () + switch (a) { + case 0: // Le etichette devono essere *espessioni costanti* (come gli enums) + printf("Hey, 'a' equals 0!\n"); + break; // se non si rompe il flusso (break) continua sulle etichette successive + case 1: + printf("Huh, 'a' equals 1!\n"); + break; + // Stai attendo: senza 'break' l'esecuzione continua fino a che non viene + // raggiunta la prossima pausa + case 3: + case 4: + printf("Look at that.. 'a' is either 3, or 4\n"); + break; + default: + // se il valore di a non corrisponde a nessuna delle etichette + fputs("Error!\n", stderr); + exit(-1); + break; + } + /* + Usiamo il "goto" in C + */ + typedef enum { false, true } bool; + // per il C che non ha booleani come tipi di dato prima del C99 :( + bool disaster = false; + int i, j; + for(i=0; i<100; ++i) + for(j=0; j<100; ++j) + { + if((i + j) >= 150) + disaster = true; + if(disaster) + goto error; // esci da entrambi i cicli for + } + error: // questa è un etichetta a cui puoi "saltare" con `goto error` + printf("Error occurred at i = %d & j = %d.\n", i, j); + /* + https://ideone.com/GuPhd6 + Questo stamperà l'errore 'Error occured at i=51 & j=99.' + */ + /* + È generalmente considerato una cattiva pratica farlo, + tranne se davvero sai cosa stai facendo, vedi: + https://en.wikipedia.org/wiki/Spaghetti_code#Meaning + */ + + /////////////////////////////////////// + // Typecasting + /////////////////////////////////////// + + // Ogni valore in C ha un tipo, ma puoi lanciare un valore in un altro tipo + // se vuoi (con alcuni vincoli) + + int x_hex = 0x01; // Puoi assegnare variabili con caratteri esadecimali + // il binario non è nello standard, ma consentito da alcuni + // compilatori (x_bin = 0b0010010110) + + // I casting tra i tipi tenterà di preservare i loro valori numerici + printf("%d\n", x_hex); // => Stampa 1 + printf("%d\n", (short) x_hex); // => Stampa 1 + printf("%d\n", (char) x_hex); // => Stampa1 1 + + // Se si assegna un valore superiore a un tipo di massimo, il rollover senza preavviso. + printf("%d\n", (unsigned char) 257); // => 1 (Max char = 255 se char è lunga 8 bits) + + // Per determinare il valore massimo di un `char`, un char unsigned è un char senza sengno, + // rispettivamente, usa i macro char_max, schar_max e uchar_max da limiti.h + + // I tipi integrati possono essere cambiati (cast) in virgola mobile e vice versa + printf("%f\n", (double) 100); // %f formatta sempre un doppio... + printf("%f\n", (float) 100); // ...anche con un vigola mobile. + printf("%d\n", (char)100.0); + + /////////////////////////////////////// + // Puntatori (Pointers) + /////////////////////////////////////// + + // Un puntatore è una variabile chiamata per archiviare un indirizzo di memoria. + // La sua dichiarazione dirà il tipo di dato a cui è indicato. + // Puoi recuperare l'indirizzo di memoria dalle tue variabili, poi potrai utilizzarle. + + int x = 0; + printf("%p\n", (void *)&x); // Usa e recupera l'indirizzo di una variabile + // (%p formattazione di un puntatore, oggetto di tipo void *) + // => Stampa alcuni indirizzi in memoria; + + // I puntatori iniziano con * nella loro dichiarazione + int *px, not_a_pointer; // px è un puntatore ad un int + px = &x; // memorizza l'indirizzo di x in px + printf("%p\n", (void *)px); // => stampa un indirizzo di memoria + printf("%zu, %zu\n", sizeof(px), sizeof(not_a_pointer)); + // => Stampa '8, 4' su un tipico sistema a 64 bit + + // Per recuperare il valore all'indirizzo che un puntatore sta puntando + // metti * davanti alla referenza + // Note: si, potrebbe confondere il fatto che '*' si usa per entrambe, + // dichiarazione a puntatore e referenza ad esso. + printf("%d\n", *px); // => Stampa 0, la variabile di x + + // Puoi anche modificare il valore a cui punta il puntatore. + // dovremmo avvolgere la referenza tra parentesi perchè + // ++ ha una precedenza maggiore di *. + (*px)++; // Incrementa il valore che px punta di 1 + printf("%d\n", *px); // => Stampa 1 + printf("%d\n", x); // => Stampa 1 + + // Gli array sono un buon modo di allocare blocchi di memoria contigui + int x_array[20]; // dichiarazione di un array di dimensione 20 (non può essere cambiata la dimensione) + int xx; + for (xx = 0; xx < 20; xx++) { + x_array[xx] = 20 - xx; + } // Inizializza x_array a 20, 19, 18,... 2, 1 + + // dichiara un puntatore di tipo int e inizializzalo per indicare x_array + int* x_ptr = x_array; + // x_ptr ora indica il primo elemento nell'array (il numero intero 20). + // Funziona perchè gli array spesso decadono nei puntatori del loro primo elemento. + // Ad esempio, quando un array viene passato a una funzione o viene assegnato a un puntatore, + // decade in (implicitamente convertito in) un puntatore. + // Eccezioni: quando l'array è l'argomento dell'operatore `&` (indirizzo di): + int arr[10]; + int (*ptr_to_arr)[10] = &arr; // &arr non è un tipo`int *`! + // È un tipo 'puntatore all'array' (del decimo 'int'). + // o quando l'array è una stringa di caratteri utilizzata per l'inizializzazione di un array di char: + char otherarr[] = "foobarbazquirk"; + // oppure quando è un argomento di un operatore `sizeof` o `alignof`: + int arraythethird[10]; + int *ptr = arraythethird; // equivalente con int *ptr = &arr[0]; + printf("%zu, %zu\n", sizeof(arraythethird), sizeof(ptr)); + // probabilmente stamperà "40, 4" or "40, 8" + + // I puntatori sono incrementati e decrementati in base al loro tipo + // (questo si chiama puntatore aritmetico) + printf("%d\n", *(x_ptr + 1)); // => Stampa 19 + printf("%d\n", x_array[1]); // => Stampa 19 + + // E ancora possibile allocare dinamicamente blocchi di memoria contigui con la + // funzione di libreria standard malloc, che prende un argomento di tipo `size_t` + // che rappresenta il numero di byte da allocare (di solito dalla heap, sebbene questo + // potrebbe non essere vero su alcuni sistemi embedded - il C standard non dice nulla al riguardo). + int *my_ptr = malloc(sizeof(*my_ptr) * 20); + for (xx = 0; xx < 20; xx++) { + *(my_ptr + xx) = 20 - xx; // my_ptr[xx] = 20-xx + } // Inizializza la memoria a 20, 19, 18, 17... 2, 1 (come int) + + // Fai attenzione a passare i valori forniti dall'utente alla malloc! + // Se vuoi essere al sicuro, puoi usare calloc invece (che, a differenza di malloc, + // inizializza tutti zero in memorial) + int* my_other_ptr = calloc(20, sizeof(int)); + + // Nota che non esiste un modo standard per ottenere la lunghezza di un array allocato dinamicamente in C. + // Per questo motivo, se i tuoi array sono passati attraverso il programma per molto tempo, + // avrai bisogno di un altra variabile per tenere traccia del numero di elementi (dimensione) del array. + // Vedi la sezione delle funzioni per maggiori informazioni. + size_t size = 10; + int *my_arr = calloc(size, sizeof(int)); + // Aggiungi un elemento all'array + size++; + my_arr = realloc(my_arr, sizeof(int) * size); + if (my_arr == NULL) { + // Ricordati di verificare il fallimento di realloc! + return + } + my_arr[10] = 5; + + // la Memoria di dereferenziazione che non hai assegnato darà 'risultati imprevedibili' + // - Si dice che il programma invochi 'comportamento indefinito' + printf("%d\n", *(my_ptr + 21)); // => Stampa chi-sa-cosa? potrebbe anche andare in crash. + + // Quando hai finito con un blocco di memoria malloc, devi liberarlo, + // altrimenti nessun altro può usarlo fino a quando il programma non termina + // (questo è chiamato perdita di memoria (= memory leak)) + free(my_ptr); + + // Le stringhe sono array di char, ma solitamente sono rappresentate come + // puntatori-al-char (che è un puntatore al primo elemento dell'array). + // È una buona pratica usare `const chat *` quando si fa riferimento a una stringa di caratteri, + // poichè i caratteri di una stringa non devono essere modificati + // (ovvero 'foo' [0] = 'a' è illegale) + const char *my_str = "This is my very own string literal"; + printf("%c\n", *my_str); // => 'T' + + // Questo non è il caso se la stringa è un array + // (potenzialmente inizializzato con una stringa di caratteri) + // che risiede nella memoria scrivibile, come in: + char foo[] = "foo"; + foo[0] = 'a'; // questo è legale, foo ora contiene 'aoo'. + + function_1(); +} // fine della funzione main + +/////////////////////////////////////// +// Funzioni (Functions) +/////////////////////////////////////// + +// Sintassi della dichiarazione di una funzione: +// () + +int add_two_ints(int x1, int x2) +{ + return x1 + x2; // Utilizzare return per restituire un valore +} + +/* +Le funzioni sono chiamate per valore. Quando viene chiamata una funzione, +gli argomenti passati alla funzione sono copie di argomenti originali (tranne gli array). +Qualunque cosa tu fai l'argomento nella funzione non cambia il valore originale rispetto +a quando è stata chiamata. + +Usa i puntatori se te hai bisogno di modificare l'argomento originale +(gli array sono sempre passati come puntatori). + +Esempio: inversione della stringa. +*/ + +// Una funzione void in ritorna nulla +void str_reverse(char *str_in) +{ + char tmp; + size_t ii = 0; + size_t len = strlen(str_in); // `strlen()` è parte della libreria standard + // NOTE: la lunghezza ritornata da `strlen` NON + // include la terminazione con il NULL byte ('\0') + // nella versione C99 e superiori, puoi direttamente dichiamare le variabili nel controllo del ciclo + // nelle parentesi del loop e.g. `for (size_t ii = 0; ...` + for (ii = 0; ii < len / 2; ii++) { + tmp = str_in[ii]; + str_in[ii] = str_in[len - ii - 1]; // ii-iesimo chat dalla fine + str_in[len - ii - 1] = tmp; + } +} +//NOTE: string.h il file di intestazione ha bisogno di includere + +/* +char c[] = "This is a test."; +str_reverse(c); +printf("%s\n", c); // => ".tset a si sihT" +*/ +/* +come possiamo ritornare solo una variabile +per cambiare valori in più di una variabile possiamo utilizzare le referenze +*/ +void swapTwoNumbers(int *a, int *b) +{ + int temp = *a; + *a = *b; + *b = temp; +} +/* +int first = 10; +int second = 20; +printf("first: %d\nsecond: %d\n", first, second); +swapTwoNumbers(&first, &second); +printf("first: %d\nsecond: %d\n", first, second); +// i valori verranno scambiati +*/ + +// Restituire più valori. +// Il linguaggio C non permette di restituire più valori utilizzando l'istruzione return. +// Se si desidera restituire più valori, la funzione deve ricevere in ingresso le variabili +// in cui salvare i risultati. Queste variabili devono essere passate come puntatori, +// in modo che la funzione possa modificarne direttamente il contenuto. +int return_multiple( int *array_of_3, int *ret1, int *ret2, int *ret3) +{ + if(array_of_3 == NULL) + return 0; // ritorna il codice d'errore (falso) + + // mettiamo le varibili nel puntatore in modo da modificare il suo valore + *ret1 = array_of_3[0]; + *ret2 = array_of_3[1]; + *ret3 = array_of_3[2]; + + return 1; //return codice d'errore (vero) +} + +/* +Per qunato riguarda gli array, saranno sempre passati alla funzioni come indicatori. +Anche se allochi staticamente un array come `arr[10]` +Viene ancora passato come puntatore al primo elemento in qualsiasi chiamata di funzioni. +Ancora una volta non esiste un modo standard per ottenere le dimensioni di un array allocato dinamicamente in C. +*/ +// La grandezza deve essere passata! +// Altrimenti, questa funzione non ha modo di sapere quanto sia grande l'array. +void printIntArray(int *arr, size_t size) { + int i; + for (i = 0; i < size; i++) { + printf("arr[%d] is: %d\n", i, arr[i]); + } +} +/* +int my_arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; +int size = 10; +printIntArray(my_arr, size); +// stamperà "arr[0] is: 1" etc +*/ + +// Se si fa riferimento a variaibli esterne, fuori dalla funzione, +// è necessario utilizzare la parola chiave extern +int i = 0; +void testFunc() { + extern int i; //i qui ora sta usando la variabile esterna i +} + +// rendi le variabili esterne private per il file di origine con static: +static int j = 0; // altri file che utilizzano testFunc2() non possono accedere alla variabile j +void testFunc2() { + extern int j; +} +// La parola chiave `static` rende una variabile inaccessibile al codice al di fuori del +// unità di compilazione. (Su quasi tutti i sistemi, un'unità di compilazione è un file .c). +// `static` si può applicare sia alla variabili globali (all'unità di compilazione), +// funzioni e variabili e variabili di funzioni locali. +// Quando si usa `static` con variabili di funzioni locali, la variabile è effettivamente globale +// e conserva il suo valore tra le chiamate di funzione, ma è accessibile solo all'interno della funzione +// in cui è dichiarata. +// In più, le variabili statiche sono inizializzate a 0 se non dichiarate con qualche valore di partenza. +// **Puoi dichiarare funzioni statiche per renderle private** + +/////////////////////////////////////// +// Tipi definiti dal utente e strutture +/////////////////////////////////////// + +// Typedefs può essere utilizzato per creare alias di tipo +typedef int my_type; +my_type my_type_var = 0; + +// Le strutture sono delle raccolte di dati, i membri vengono allocati in memorial in sequenza, +// nell'ordine sono scritti: +struct rectangle { + int width; + int height; +}; + +// non è generalmente vero +// sizeof(struct rectangle) == sizeof(int) + sizeof(int) +// a causa del possibile padding tra i membri +// della struttura (necessario per motivi di allineamento) [1] + +void function_1() +{ + struct rectangle my_rec = { 1, 2 }; // Campo che può essere inizializzato immediatamente + + // Accesso alla struttura con . + my_rec.width = 10; + my_rec.height = 20; + + // Puoi dichiarare puntatori alle strutture + struct rectangle *my_rec_ptr = &my_rec; + + // Usa la dereferenziazione per impostare i membri del puntatore... + (*my_rec_ptr).width = 30; + + // ... o anche meglio: preferisci la -> per motivi di legibilità + my_rec_ptr->height = 10; // Stesso di (*my_rec_ptr).height = 10; +} + +// Puoi applicare typedef alla struttura per convenienza +typedef struct rectangle rect; + +int area(rect r) +{ + return r.width * r.height; +} + +// I Typedefs possono anche essere definiti a destra durante la definizione della struttura +typedef struct { + int width; + int height; +} rect; +// Come prima, fare questo significa che puoi scrivere +rect r; +// invece di dover digitare +struct rectangle r; + +// Se hai strutture di grandi dimensioni, puoi passarle `per puntatore` +// per evitare di copiare l'intera struttura: +int areaptr(const rect *r) +{ + return r->width * r->height; +} + +/////////////////////////////////////// +// Puntatori a funzioni +/////////////////////////////////////// +/* +In fase di esecuzioni, le funzioni si trovano su indirizzi di memoria noti. +I puntatori della funzione sono proprio come qualsiasi altro puntatore +(memorizzano solo un indirizzo di memoria), ma possono essere utilizzati +per invocare le funzioni direttamente (o le funzioni di callback). +Tuttavia, la sintassi della definizione può essere inizialmente confusa + +Esempio: usa str_reverse da un puntatore: +*/ +void str_reverse_through_pointer(char *str_in) { + // Definire una variabile puntatore della funzione, denominata f. + void (*f)(char *); // La segnatura dovrebbe corrispondere esattamente alla funzione target. + f = &str_reverse; // Assegna l'indirizzo per la funzione effettiva (determinato a tempo d'esecuzione) + // f = str_reverse; funzionerebbe anche: le funzioni decadono nei puntatori, simili agli array + (*f)(str_in); // Per chiamare la funzione attraverso il puntatore + // f(str_in); // Questa è una sintassi alternativa ma ugualmente valida per chiamarla +} + +/* +Finchè le segnature delle funzioni corrispondono, è possibile assegnare qualsiasi funzione allo stesso puntatore. +I puntatori della funzione sono generalmente scritti per semplicità e leggibilità come segue: +*/ + +typedef void (*my_fnp_type)(char *); + +// quindi utilizzato quando si dichiara la variabile del puntatore effettivo: +// ... +// my_fnp_type f; + + +///////////////////////////// +// Stampando caratteri con printf() +///////////////////////////// + +//Caratteri speciali: +/* +'\a'; // carattere di allarme +'\n'; // carattere di nuova riga +'\t'; // carattere di tabulazione (allinea il testo a sinistra) +'\v'; // tabulazione verticale +'\f'; // nuova pagina (form feed) +'\r'; // ritorno a capo (carriage return) +'\b'; // carattere di backspace (cancella il carattere precedente) +'\0'; // carattere NULL. Di solito viene utilizzato alla fine delle stringhe in C. +// Esempio: "hello\n\0". \0 viene usato per convenzione per indicare la fine della stringa. +'\\'; // barra inversa (backslash) +'\?'; // punto interrogativo +'\''; // apostrofo (singolo apice) +'\"'; // doppio apice +'\xhh'; // numero esadecimale. Esempio: '\xb' corrisponde al carattere di tabulazione verticale +'\0oo'; // numero ottale. Esempio: '\013' corrisponde al carattere di tabulazione verticale + +// Formattazione della stampa: +"%d"; // intero +"%3d"; // intero con una lunghezza minima di 3 cifre (allineato a destra) +"%s"; // stringa +"%f"; // numero in virgola mobile (float) +"%ld"; // numero long +"%3.2f"; // numero float con almeno 3 cifre prima della virgola e 2 dopo +"%7.4s"; // applicabile anche alle stringhe +"%c"; // carattere +"%p"; // puntatore. NOTA: è necessario effettuare un cast a (void *) prima di passarlo come argomento a `printf`. +"%x"; // numero esadecimale +"%o"; // numero ottale +"%%"; // stampa il carattere % +*/ + + +/////////////////////////////////////// +// Ordine di valutazione +/////////////////////////////////////// + +// Da sopra a sotto, il sopra ha la precedenza +//----------------------------------------------------------// +// Operatori | Associatività // +//----------------------------------------------------------// +// () [] -> . | da sinistra a destra // +// ! ~ ++ -- + = *(tipo) sizeof | da destra a sinistra // +// * / % | da sinistra a destra // +// + - | da sinistra a destra // +// << >> | da sinistra a destra // +// < <= > >= | da sinistra a destra // +// == != | da sinistra a destra // +// & | da sinistra a destra // +// ^ | da sinistra a destra // +// | | da sinistra a destra // +// && | da sinistra a destra // +// || | da sinistra a destra // +// ?: | da destra a sinistra // +// = += -= *= /= %= &= ^= |= <<= >>= | da destra a sinistra // +// , | da sinistra a destra // +//----------------------------------------------------------// + + +/******************************* File d'intestazione ********************************** + +I file d'intestazione sono una parte importante del C, lo permettono la connesione di +file sorgenti in C e possono semplificare il codice e le definizioni separandoli in file diversi. + +I file d'intestazione sono sintatticamente simili ai file di sorgente C ma risiedono in '.h' +Possono essere inclusi nel tuo file sorgente utilizzando il pre-processore con la direttiva #include 'Esempio.h' +dato che 'esempio.h' esiste nella stessa cartella come file c. +*/ + +/* Una protezione per evitare che l'header venga definito troppe volte. Questo */ +/* accade in caso di dipendenze circolari, quando il contenuto dell'header è */ +/* già stato definito. */ +#ifndef EXAMPLE_H /* Se EXAMPLE_H non è ancora stato definito. */ +#define EXAMPLE_H /* Definisce la macro EXAMPLE_H. */ + +/* Altri header possono essere inclusi negli header e quindi inclusi in modo */ +/* transitivo nei file che includono questo header. */ +#include + +/* Come per i file sorgente in C, le macro possono essere definite negli header */ +/* e utilizzate nei file che includono questo file header. */ +#define EXAMPLE_NAME "Dennis Ritchie" + +/* Anche le macro funzione possono essere definite. */ +#define ADD(a, b) ((a) + (b)) + +/* Nota le parentesi che racchiudono gli argomenti: sono importanti per evitare */ +/* che `a` e `b` vengano espansi in modo inaspettato. Ad esempio, considera */ +/* MUL(x, y) (x * y); MUL(1 + 2, 3) verrebbe espanso in (1 + 2 * 3), dando un */ +/* risultato errato. */ + +/* Le struct e i typedef possono essere usati per garantire coerenza tra i file. */ +typedef struct Node +{ + int val; + struct Node *next; +} Node; + +/* Anche le enumerazioni possono essere definite qui. */ +enum traffic_light_state {GREEN, YELLOW, RED}; + +/* I prototipi di funzione possono essere definiti qui per l'uso in più file, */ +/* ma è una cattiva pratica definire la funzione direttamente nell'header. */ +/* Le definizioni dovrebbero essere inserite in un file C separato. */ +Node createLinkedList(int *vals, int len); + +/* Oltre agli elementi sopra citati, altre definizioni dovrebbero essere lasciate */ +/* a un file sorgente C. Inoltre, non bisognerebbe includere troppi file o */ +/* definizioni in un unico header, ma organizzarli in più header separati o in */ +/* un file C. */ + +#endif /* Fine della direttiva preprocessor if. */ + +``` + +## Ulteriori letture + +È meglio procurarsi una copia di [K&R, aka "The C Programming Language"](https://en.wikipedia.org/wiki/The_C_Programming_Language). È il libro sul C, scritto da Dennis Ritchie, il creatore del linguaggio, e Brian Kernighan. Tuttavia, fai attenzione: è piuttosto datato e contiene alcune imprecisioni (o meglio, idee che oggi non sono più considerate valide) e pratiche che nel tempo sono cambiate. + +Un'altra buona risorsa è [Learn C The Hard Way](http://learncodethehardway.org/c/) (non è gratuito). + +Se hai una domanda, consulta le [compl.lang.c Frequently Asked Questions](http://c-faq.com). + +È molto importante utilizzare una corretta spaziatura, indentazione e mantenere uno stile di codifica coerente in generale. +Un codice leggibile è migliore di un codice "intelligente" o veloce. Per uno stile di codifica chiaro e ben strutturato, puoi fare riferimento al [Linux kernel coding style](https://www.kernel.org/doc/Documentation/process/coding-style.rst). + +[1] [Why isn't sizeof for a struct equal to the sum of sizeof of each member?](https://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member)