
































Studia grazie alle numerose risorse presenti su Docsity
Guadagna punti aiutando altri studenti oppure acquistali con un piano Premium
Prepara i tuoi esami
Studia grazie alle numerose risorse presenti su Docsity
Prepara i tuoi esami con i documenti condivisi da studenti come te su Docsity
Trova i documenti specifici per gli esami della tua università
Preparati con lezioni e prove svolte basate sui programmi universitari!
Rispondi a reali domande d’esame e scopri la tua preparazione
Riassumi i tuoi documenti, fagli domande, convertili in quiz e mappe concettuali
Studia con prove svolte, tesine e consigli utili
Togliti ogni dubbio leggendo le risposte alle domande fatte da altri studenti come te
Esplora i documenti più scaricati per gli argomenti di studio più popolari
Ottieni i punti per scaricare
Guadagna punti aiutando altri studenti oppure acquistali con un piano Premium
Il documento descrive in modo chiaro e dettagliato tutti gli argomenti necessari al fine di imparare il linguaggio di programmazione C. Nel documento troverete esempi di codici spiegati nel minimo dettaglio. Ottenendo la padronanza di tutto quello che è scritto all'interno del file potrete diventare esperti programmatori del linguaggio C e C++. Il linguaggio C è alla base di qualsiasi altro linguaggio di programmazione, le conoscenze minime che un programmatore deve avere.
Tipologia: Appunti
1 / 40
Questa pagina non è visibile nell’anteprima
Non perderti parti importanti!

































Una variabile identifica una porzione di memoria destinata a contenere dei dati, che possono essere modificati durante l’esecuzione del programma. -Devono essere dichiarate prima di essere utilizzate; -Formato -> tipo nome; -La sua dichiarazione può essere anche combinata con la sua inizializzazione -> tipo nome = valore_inziale; NOME DI UNA VARIABILE -> deve rispettare alcune regole: deve iniziare con una lettera o un underscore, può essere seguito da una qualsiasi combinazione di lettere (maiuscole o minuscole), underscore e cifre da 0 a 9, non dev’essere una parola riservata (tipo int int). CASE SENSITIVE: le lettere maiuscole e minuscole sono diverse.
I valori a doppia precesione con tipo double sono costituti da 8 byte. Il formato è simile al formato di tipo float con la differenza che il numero viene rappresentato con un esponente a 11 bit in eccesso 1023 e con una mantissa a 52 bit, più 1 bit più significativi implicito. Il tipo double contien 64 bit: 1 per il segno, 11 per l’esponente e 52 per la mantissa (parte decimale del numero). Questo formato specifica un intervallo di valori inclusi approssimativamente tra 1,7E-308 e 1,7E+308 con almeno 15 cifre di precisione. DEBUGGER Un Debugger C è un programma che consente agli sviluppatori di esaminare il codice sorgente delle loro applicazioni in C, individuare eventuali errori e correggerli. Esso consente di eseguire il passo per passo dell'esecuzione del codice, ispezionare le variabili e impostare dei punti di interruzione per sospendere l'esecuzione del programma e investigare lo stato del sistema. Ciò rende possibile individuare e correggere gli errori nel codice in modo più efficiente. Esempio : Supponiamo di avere un codice C che calcola la media di alcuni numeri. Il codice sembra funzionare correttamente, ma quando si esegue il programma, si ottiene un risultato imprevisto. Utilizzando un debugger C, si può impostare un punto di interruzione all'interno della funzione di calcolo della media, esaminare il valore delle variabili in quel punto e capire perché il risultato non è quello atteso. In questo caso si potrebbe scoprire che è stato dimenticato di inizializzare una variabile che influisce sul risultato finale. CICLO FOR – ciclo enumerativo (definito) WHILE-DO O DO-WHILE – ciclo indefinito. Le due forme differiscono nel fatto che la do-while controlla alla fine se deve essere ripetuto o se può continuare con il codice; mentre il while-do controlla all’inizio per cui se la condizione del ciclo non è rispettata neanche una volta il ciclo viene saltato.
Ma alcuni variabili, comunque, come “float totaleScontrino = 0” ora passandoci sopra con il mouse avrà valore 0. Con il debugger si possono tenere in considerazione molte cose: STACK, REGISTER (CPU), MEMORY ECC. Può essere scomodo però passare il mouse su ogni valore delle variabili. Quando abbiamo idea di che righe controllare si possono aggiungere alla finestre dei watches cioè delle variabili osservate. VIEW – WATCH EXPRESSION -> si scrivere il nome della variabile che servirà metterà sotto controllo. Variabile messa perennemente in tempo reale sotto controllo. Metter sotto controllo più variabili locali in un colpo solo – “LOCAL VARIABLES”. La finestra WATCH EXPRESSION non è un copione di variabili locali, perché alcune potrebbero essere anche globali. Poi non è WATCH VARIABLES ma EXPRESSION quindi si può chiedere di calcolare espressioni più complicate che vanno comunque ad utilizzare le variabili. totaleScontrino = 0 //variabile totaleScontrino * 1.2 = 0 //espressione, sempre 0 perché variabile = 0 Ora mandando in esecuzione il programma si vedrà in tempo reale l’esecuzione. Si può cambiare anche in tempo reale il valore di una variabile direttamente dalla finestra di WATCH EXPRESSION. ORA MOSTRO PROGRAMMA CON ERRORE In esecuzione se è stato risposto no (‘n) perché mi richiede l’inserimento? Si utilizza il DEBUGG come prima. SI utilizzano i break point per far fermare il controllo del debug fino al punto dove si vuole che arrivi il controllo. In questo caso il problema è il fatto delle lettere se maiuscole o minuscole perché C e case sensitive. Il programma non è capace di capire che la volontà dello user è la stessa indifferentemente se la lettere è maiuscola o minuscola. NON È UN ERRORE VERO E PROPRIO È UN CASO NON GESTITO. PER RENDERE IL PROGRAMMA PIU INTELLIGENTE: -Si aggiunge il riconoscimento anche della lettere maiuscola con operatori AND o OR. -Seconda possibilità usare la funzione toupper che converte in maiuscolo una lettera (necessaria la libreria <ctype.h>). SINTASSI -> risposta2=toupper(risposta2); // tolower – funzione per conversione in minuscolo ALTRO CONTROLLO SUI NUMERI INSERITI IN QUANTO COSI SI POSSONO INSERIRE NUMERI NEGATIVI E GRANDI: si possono usare condizioni if if(costoProdotto <=0 || costoProdotto>100){ printf(“Errore!”); }
FUNZIONI -> unità di programmazioni più piccole facilmente riutilizzabili. Nel main vengono richiamate le funzioni dichiarate precedentemente al main. Quando il compilatore arrivare all’esecuzione del cout<<PerimetroRettangolo(3,5) o printf in C -> effettua un salto e va ad eseguire il codice della funzione richiamata. SOTTOPROGRAMMI -> possibilità di creare un file esterno tipo “mieFunzioni.h” che fa riferimento al mio “mieFunzioni.cpp” posso riutilizzare i pezzi di codici; e se c’è un errore posso andare a risolvere direttamente l’errore all’interno di questo file. Float PerimetroRettangolo (float base, float altezza){ } Float -> perché il perimetro potrebbe essere un numero a virgola mobile. Tra parentesi vengono indicati i valori che devono essere indicati alla funzione e tramite questi svolge determinate funzioni (dichiarate all’interno della stessa) per restituire il risultato voluto. VANTAGGI -> riduzione complessità (non un unico programma, più pulizia del codice, se c’è un problema si risolve prima), riutilizzo del codice (NB – punto più importante), si fanno meno errori e si corregge l’errore UNA volta sola, codice più leggibile. ESEMPIO -> separare con una cornicetta (“-----“) i risultati che vengono visualizzati sullo schermo. Usando le funzioni. void cornicetta(){ /* void -> no valori di ritorno, perché stampa solo dei valori ma non da un valore come risultato*/ for(int i =1; i<=60; i++){ printf(“c”); printf(“\n”); } void main(…){ cornicetta(); cornicetta(); …… Come mostrato nella slide a destra -> si può chiedere anche direttamente all’utente che tipo di carattere usare.
float totaleScontrino = 0, costoProdotto = 0; char risposta; do{ char risposta2; totaleScontrino = 0; do{ costoProdotto = leggiReale(0,100); totaleScontrino += costoProdotto; printf(“\nCi sono altri prodotti? (s/n) “); scanf(“ %c”,&risposta2); }while (risposta2 != ‘n’); printf(“\nQuesto cliente paga: %0.2f!\n”, totaleScontrino); printf(“\nCi sono altri clienti? (s/n) “); scanf(“ %c”,&risposta); }while (risposta !=’n’); return 0; } CREAZIONE FUNZIONE PER SCAMBIARE VALORE DI DUE VARIABILI: Essendo una funzione che deve effettuare solo scambio si utilizza il tipo void. #include <stdio.h> #include <stdlib.h> #include <crtdbg.h> void scambia(int *x, int *y){ int aiuto = x; //si memorizza una variabile intera (derefernziata) in una varibile intera /QUANDO CON UN PUNTATORE SI VUOLE LAVORARE CON L’INTERO PUNTATO SI DEVE DEREFENZIARE IL PUNTATORE (PASSARE DALL’ INDIRIZZO ALL’OGGETTO VERO E PRORPIO – SI FA USANDO UN )/ *x = *y; //memorizza nella varible intera nellallocazione puntata da x memorizza il valore che è contenuto nella //posizione puntata da y y = aiuto; //nella varibile intera y memorizza il valore interno della variabile (già intera) ‘aiuto’ } int main(){ int a = 100; int b = 500; /scambia(a,b); in questa maniera viene passato il valore BY VALUE, non è la varibile ‘a’ che viene passata alla
funzione scambia. Quindi la funzione non può lavoare direttamente sulla variabile a, ma viene PASSATO IL VALORE che viene copiato nel corrispondnete parametro della funzione. Cioè in partenza X ha il valore della variabile ‘a’ ma SOLO IL VALORE NON LA VERA VARIABILE, QUALSIASI MODIFICA FATTA ALLA X NON SI RIPERCUOTE SULLA VARIABILE ESTERNA LA STESSA COSA PER ‘b’ E Y./ /per effettuare lo scambio si usano i PUNTATORI – QUINDI x non è una varibile intera ma un puntatoread una varibile intera -> quindi l’indirizzo di ‘a’ viene copiato in x e lo stesso per ‘b'. La funzione tramite l'indirizzo che è univoco in tutte la RAM delle variabili esterne usando x nella maniera corretta lavora sulla variabile esterna/ /QUANDO CON UN PUNTATORE SI VUOLE LAVORARE CON L'INTERO PUNTATO SI DEVE DEREFENZIARE IL PUNTATORE (PASSARE DALL' INDIRIZZO ALL'OGGETTO VERO E PRORPIO)/ scambia(&a,&b); / siccome i due parametri formali della funzione due puntatori a interi, quindi quando la funzione viene richiamata serve CHIAMARE UNA VARIABILE PUNTATORE AD INTERO (quindi l'indirizzo di una varibile intera) - BY REFERNCE */ printf("%d ---- %d", a, b); } *NELLE FUNZIONI I PARAMETRI SI POSSONO SPECIFICARE COME TIPO &(REFERENCE) INVECE CHE (PUNTATORE). void scambia(int &x, int &y){ int aiuto = x; x = y; y = aiuto; } & -> si usa come puntatore “mascherato” per evitare tutti questi *. Cosi i due parametri formali non sono interi, ma indirizzi di interi. Si può lavorare come se fossero variabili normali. Anche nel richiamo della funzione non serve più *. PROTOTIPI -> siccome c’è la possibilità di richiamare le varie funzioni di tra di loro (in base allo stile di programmazione) ci potrebbe essere il problema di dover spostare il blocco della funzione in modo da tale da farla “vedere” dalla funzione richiamante. Per evitare ciò si usano i prototipi -> cioè delle dichiarazioni anticipate delle funzioni che seguiranno, cosi che possano essere “viste” globalmente. Si tratta di riproporre l’intestazione della funzione quindi senza riporre poi il blocco con la parentesi graffa – se si usano parametri di default tipo int i = 0, serve specificarli nei prototipi ma non serve rispecificarli quando si richiama la funzione vera e propria (altrimenti si ricade in messaggi d’errore del compilatore). ARRAY
Per dare una grandezza corretta si può definire ad inizio programma una COSTANTE che poi verrà utilizzata come grandezza per i vari vettori, cosi che in futuro se c’è bisogno di cambiamenti si dovrà effettuare solo un cambio. const int MAX_PROD = 200; //prodotti AL MASSIMO 200 non sono per forza 200 int numProd = 0; float costi [MAX_PROD]; Per non vanificare utilizzo costante importante poi utilizzare nei vari cicli o come limiti nelle ricerche le costanti stesse. La costante non può essere modificata, singolarmente, nelle varie funzioni (NB – compilatore mostra messaggio d’errore). LA COSTANTE È DI SOLA LETTURA. Siccome non sempre tutti gli elementi raggiungono sempre il limite imposto dalla COSTANTE (200 in questo esempio) è utile se non necessario mantenere un contatore degli elementi utilizzati per un certo vettore, lo si può chiamare LIMITE LOGICO contrapposto al LIMITE FISICO (200 prodotti). Per evitare spreco risorse di calcoli e possibili errori.
Il linguaggio C offre soltanto i tipi di dato essenziali , in particolare solo quelli atomici : un intero non può essere ulteriormente scomponibile, come un float , un boolean; ma la stringa invece permette questo. Ad esempio, la stringa “ciao” è scomponibile nei codici numerici che corrispondono ai suoi singoli caratteri, quindi i suoi codici ASCII. Il C originale non offre il tipo di dato string , offre il tipo di dato essenziale atomico char che corrisponde ad un singolo carattere. ( char c = ‘A’;). Usare ‘ ‘ e non “ “ perché determinano una stringa che oltre il codice ASCII della lettere comprende anche il carattere NULL (\0), quindi per il singolo carattere usare gli apici ‘. Si può rappresentare la lettera A (FARE ATTENZIONE SE MAIUSCOLA O MINUSCOLA) con il suo codice ASCII char c = 65; Nel vecchio C per ottenere una stringa era quello di dichiarare un vettore di caratteri : in realtà è lo stesso modo con il quale il C++ organizza le stringhe solo che ci dà un modo di accesso alle stringhe che è più comodo, ma essenzialmente si può lavorare in C++ come si lavorava con il C. COME VIENE RAPPRESENTATA LA STRINGA CIAO IN UN VETTORE? c i a o
->il vettore deve avere tanti elementi quanti sono i caratteri della stringa + 1 (NB). Perché l’unico modo per il C per sapere quando è terminata una stringa è di andare alla ricerca di questo carattere terminatore finale o meglio il carattere NULL (carattere con tutti i bit a 0). Quindi per la string “ciao” avremo bisogno un vettore di char s [5] (il vettore andrà da 0 a 4 come posizioni). Dichiarare GLOBALMENTE char s [5]; (si può assegnare direttamente da qua come char s[5] = “ciao” ;) Nel main assegnare a stringa s la stringa “ciao”, quindi durante il funzionamento del programma -> ci sono una serie di funzioni usate per manipolare le stringhe strcpy (s, “Roma”); //permette di copiare un vettore di caratteri la stringa indicata come secondo parametro nel primo parametro (ATTENZIONE SEMPRE A DIMENSIONE VETTORE).
Se si supera il limite di caratteri imposto del vettore (il C non ci avviserebbe) -> non c’è nessun errore di logica e semantica stiamo solo sporcando una zona di memoria (con i caratteri eccedenti) in tot cella di RAM che non erano state predisposte per il vettore s. Alla fine, è stato aggiunto la variabile di tipo string -> che permette di dichiarare variabili di tipo stringa ed usarli con la stessa semplicità degli altri tipo di dato. Per cui non solo sarà possibile inizializzare la stringa come prima -> string s = “ciao”; Ma sarà molto più semplice cambiare il suo valore nel main, ad esempio, scrivendo -> s = “sono una stringa” cosi che non serva neanche preoccuparsi della grandezza. Il C/C++ proibisce l’assegnamento diretto tra array quindi se si vogliono confrontare o lavorare con due stringhe si dovranno usare costrutti del tipo strcpy o strcmp, non si può semplicemente usare s1 = s2 dove hanno valore: char s1 [] = “stringa corta”; char s2 [] = “stringa più 1”; Nessun problema per le variabili di tipo string dove l’assegnamento diretto è consentito -> (PER QUANTO RIGUARDA IL C++) string s3 = “patate”, s4 = “cipolle”; s3 = s4; cout<<s3; -> cipolle //output FINE PARTE SULLE STRINGHE #define MAX_PROD 200 int numProd = 0; float Costi[MAX_PROD]; char prodotti[MAX_PROD]; siamo a questo punto. Ora c’è una differenza mentre un vettore di float occupa tutto lo spazio necessario per memorizzare quei tot float (200 in questo caso), gli oggetti sono creati dinamicamente (LE STRINGHE SONO OGGETTI NON SEMPLICI VARIABILI ). Per cui il vettore prodotti è un vettore di 200 puntatori a stringhe; quindi, può contenere l’indirizzo di 200 stringhe non 200 stringhe – lo spazio occupato è molto inferiori soprattutto se la lunghezza delle stringhe che verranno memorizzate è abbastanza lunga. Per sapere la dimensioni in byte di una variabile è sizeof(); Lo spazio occupato del vettore in sé è fisso, quindi 200 puntatori quindi 200 indirizzi, per cui ogni indirizzo 4 byte il vettore rimarrà sempre di 800 byte come memoria occupata. Rimarrà di 800 byte si andrà a collegare ad ogni elemento del vettore una stringa. Ma la stringa verrà memorizzata in una zona di memoria a parte (allocata da altra parte) e nella caselle di prodotti [0] (ad esempio) verrà memorizzato l’indirizzo del primo byte della stringa (‘p’). La stessa cose per le altre stringhe. ESEMPIO STRINGA -> char s [MAX_PROD] = “string moltoooooo lungaaaaaa”;
int main() { char strings[5][100]; input_strings(strings, 5); return 0; } In questo esempio, la funzione input _ strings accetta due parametri: un array di stringhe chiamato "arr" e un intero "size" che rappresenta il numero di stringhe da inserire. La funzione utilizza un ciclo for per richiedere all'utente di immettere una stringa per ogni indice dell'array. La funzione scanf viene utilizzata per memorizzare l'input dell'utente come stringa nell'indice corrispondente dell'array. Nella funzione principale, input _ strings viene chiamato con un array di 5 stringhe e 5 come dimensione. CONTROLLO DI FLUSSI I controlli di flusso in C sono costrutti utilizzati per controllare il flusso di esecuzione del programma. Ci sono tre tipi principali di controlli di flusso in C: le istruzioni di selezione, le istruzioni di iterazione e le istruzioni di trasferimento. 1- Istruzione di selezione : -> L'istruzione if-else consente di eseguire un'azione solo se una determinata condizione è vera. L'esempio seguente mostra come utilizzare l'istruzione if-else per stampare "Il numero è positivo" se una variabile x è positiva, altrimenti stampare "Il numero è negativo" ESEMPIO: int x = 5; if (x > 0) { printf("Il numero è positivo"); } else { printf("Il numero è negativo"); } -> L'istruzione switch-case è utilizzata per eseguire un'azione diversa in base al valore di una variabile. L'esempio seguente mostra come utilizzare l'istruzione switch-case per stampare un messaggio in base al valore della variabile "scelta" ESEMPIO: int scelta = 2; switch (scelta) { case 1: printf("Hai scelto l'opzione 1"); break; case 2: printf("Hai scelto l'opzione 2"); break; case 3: printf("Hai scelto l'opzione 3"); break; default: printf("Opzione non valida"); break; } 2- Istruzioni di iterazione: -> L'istruzione for è utilizzata per eseguire un'azione ripetutamente per un numero specifico di volte. L'esempio seguente mostra come utilizzare l'istruzione for per stampare i numeri da 1 a 10:
for (int i = 1; i <= 10; i++) { printf("%d ", i); } -> L'istruzione while è utilizzata per eseguire un'azione ripetutamente finché una determinata condizione è vera. L'esempio seguente mostra come utilizzare l'istruzione while per stampare i numeri da 1 a 10: int i = 1; while (i <= 10) { printf("%d ", i); i++; } -> L'istruzione do - while è simile all'istruzione while, ma l'azione viene eseguita almeno una volta, anche se la condizione non è vera. Ad esempio, si vuole chiedere all'utente di inserire un numero finché non viene inserito un numero maggiore di 100: int num; do { printf("Inserisci un numero:"); scanf("%d", &num); } while (num <= 100); 3- Istruzioni di trasferimento : -> L'istruzione break consente di interrompere un ciclo o un'istruzione switch. Ad esempio, si vuole interrompere un ciclo for quando la variabile i raggiunge il valore 5: for (int i = 1; i <= 10; i++) { if (i == 5) { break; } printf("%d ", i); } -> L'istruzione continue consente di interrompere l'iterazione corrente di un ciclo e passare alla successiva. Ad esempio, si vuole stampare solo i numeri pari da 1 a 10: for (int i = 1; i <= 10; i++) { if (i % 2 != 0) { continue; } printf("%d ", i); } -> L'istruzione return consente di terminare l'esecuzione di una funzione e restituire un valore. Ad esempio, si vuole creare una funzione che restituisca il doppio di un numero passato come argomento: int doppio(int num) { return num * 2; } -> L'istruzione goto consente di trasferire il controllo a un'altra parte del codice identificata da un'etichetta. L'utilizzo di goto è generalmente sconsigliato in quanto può rendere il codice difficile da seguire e manutenere:
Esempio è possibile anche raggruppare più operatori ternari assieme int x = 5; int result = (x > 0)? ((x < 10)? 100 : 200) : -1; Se x è maggiore di 0 e minore di 10, al risultato verrà assegnato il valore 100. Se x è maggiore di 0 ma non inferiore a 10, al risultato verrà assegnato il valore 200. Se x non è maggiore di 0, al risultato verrà assegnato il valore -1. STRUCT (TYPEDEF) Prima di spiegare le struct, affronteremo la spiegazione di “typedef” in generale -> La keyword typedef viene utilizzare per creare un alias per un tipo. Consente di dare un nome diverso a un tipo esistente, rendendo il codice più leggibile. Sintassi di base -> typedef tipo_esistente nome_nuovo_tipo; struct point { //struct che rappresenta un punto nello spazio 3D int x; int y; int z; }; Si potrebbe creare un typed per rendere più facile fare riferimento a questa struct, poi nel codice. typedef struct point Point; Cosi è possibile utilizzare il tipo Point invece di struct point: Point p1; p1.x = 3; p1.y = 4; p1.z = 5; typedef può essere utilizzato anche con tipi di base -> typedef unsigned int UINT; /* unsigned int -> tipo di variabile intera che può contenere solo valori non negativi. La parola chiave “unsigned” viene utilizzare per specificare che la variabile può contenere solo valori positivi o zero./ Adesso UINT può essere utilizzato come alias per unsigned int. Un caso d'uso comune per typedef è la creazione di un nome più significativo per un tipo di puntatore. Ad esempio, ciò avviene spesso con i puntatori a funzioni: typedef int (funzione)(int, int); Questo crea un alias per un puntatore a una funzione che prende due argomenti int e restituisce un int. È importante notare che typedef crea solo un alias, non crea un nuovo tipo. Ciò significa che, ad esempio, sizeof(Point) e sizeof(struct point) restituiranno lo stesso valore e qualsiasi operazione che può essere eseguita su struct point, può essere eseguita anche su Point, poiché è solo un alias.
Consistono in un insieme di variabili raggruppati in uno stesso tipo. Si creano quindi dei contenitori e ci si inseriscono le variabili che a senso del programma lo meritano. Le variabili contenute in una struttura sono quasi sempre contenute tutte quante in celle di memoria, blocchi di memoria, contigui. Un punto importante sono i termini di definizione e utilizzo. Prima una struct si definisce e poi si utilizz a -> la definizione avviene all’interno del codice e a tempo di compilazione (ovvero quando compilo il programma tutto quello che per il linguaggio C formano la struct sono all’interno del programma, però non c’è nessun dato. Cioè quando il programma verrà eseguito non ci saranno i dati caricati in memoria alla prima linea, avrò semplicemente la struct che è definita come quando si scriveva il programma e poi in memoria procederò a caricare i dati che mi interessano, inserendoli in struttura). Esempio struct struct_artist{ char stage_name[20]; char full_name[50]; int year; }; /* Definita una struct, con tre campi e non è in nessuna variabile: Per utilizzare la struct serve utilizzarla come una variabile. / struct struct_artist artista; //stiamo chiedendo al programma C di creare lo spazio in memoria per contenere la struct CODE -> struct struct_artist{ char stage_name[20]; char full_name[50]; int year; }artista; //STRUTTURA SEMPLFICATA CHE OFFRE IL C PER DICHIARARE L'OPERATORE PUNTO int main(){ /L'operatore punto, usato per accedere alla struct, viene usato ed è supportato da C ci permette di accedere alle variabili normalmente. Il punto serve A REFERENZIARE a quale variabile si accede. */ strcpy(artista.stage_name, "Eminem"); strcpy(artista.full_name, "Luca Rossi"); artista.year = 1972; printf("%s, %s, %d\n", artista.full_name , artista.stage_name, artista.year); return 0; }
Per tornare al discorso di prima si può riempire la struttura con dati diversi però il codice scritto per stampare la stessa struttura è identico ( NB – UTILIZZIBILITA’ E POTENZA DELLE STRUTTURE PERCHE’ SONO UN CONTENITORE DI DATI. LE FUNZIONI FUNZIONERANNO INDIPENDENTEMENTE DAL SUO CONTENUTO ). STRUTTURE ANNIDATE -> PRECISAZIONE -> “struct struct_artist” -> equivale al TIPO DELLA STRUTTURA: struct è un costruttore e struct_artist è il nome della struttura. #include <stdio.h> #include <string.h> struct struct_artist{ char stage_name[20]; char full_name[50]; int year; }artista; struct struct_track{ /* si può mettere all'interno di una struttura un riferimento ad un'altra struttura - NB In particolare, LA STRUCT ARTISTA; / struct struct_artist artista; char title[100]; float duration; }traccia; int main(){ / ORA inizializziamo artista, inizializziamo traccia e all'interno di traccia metteremo artista / strcpy(artista.stage_name, "Eminem"); strcpy(artista.full_name, "Luca Rossi"); artista.year = 1972; strcpy(traccia.title, "Black Magic"); traccia.duration = 60 * 2 + 54; traccia.artista = artista; /QUESTI DUE ARTISTI SONO DIVERSI -> IL PRIMO è l'artista all'interno della traccia E IL SECONDO è la definizione della struct artista (aka struct_artist)*/ printf("%s, %f - (%s, %s, %d)\n", traccia.title , traccia.duration, traccia.artista.full_name, traccia.artista.stage_name, traccia.artista.year); return 0; } PER RENDERE IL NOSTRO PROGRAMMA CAPIBILE SI POTREBBE FARE UNA LISTA DI TRACCE Si può fare quindi un array di strutture struct struct_track{ struct struct_artist artista; char title[100]; float duration; }album[20]; Il tutto si gestisce con un ciclo nel main (in questo caso) -> int main(){
strcpy(artista.stage_name, "Eminem"); strcpy(artista.full_name, "Luca Rossi"); artista.year = 1972; for(int i = 0; i < 20; i++){ strcpy(album[i].title, "Black Magic"); album[i].duration = 60 * 2 + 54; album[i].artista = artista; printf("%s, %f - (%s, %s, %d)\n", album[i].title , album[i].duration, album[i].artista.full_name, album[i].artista.stage_name, album[i].artista.year); } return 0; } UNION In C una union è un tipo di dati definito dall’utente che consente di memorizzare diversi tipi di dati nella stessa posizione di memoria. È simile alla struct, ma la principale differenza è che una struct assegna memoria per tutti i suoi membri, mentre una union assegna solo memoria per il membro più grande. Esempio di una union in C -> union data { int i; float f; char str[20]; } var; In questo esempio l’union “data” ha tre membri: un intero, un numero in virgola mobile e un array di caratteri. Poiché la memoria assegnata per una union è uguale alla memoria richiesta dal suo membro più grande, in questo caso str , tutti i membri condivideranno la stessa posizione di memoria. var.i = 10; printf("Valore intero: %d\n", var.i); var.f = 220.5; printf("Valore a virgola mobile: %f\n", var.f); strcpy(var.str, "Ciao, Unione"); printf("Valore stringa: %s\n", var.str); In questo esempio, assegniamo prima un valore intero alla union, quindi un valore a virgola mobile e infine un valore di stringa. Quando stampiamo i valori, possiamo vedere che il valore precedente viene sovrascritto dal nuovo valore. È importante notare che quando si utilizza una union, è responsabilità del programmatore garantire che i dati memorizzati siano del tipo corretto e tenere traccia di ciò che è attualmente memorizzato nella union, poiché la posizione di memoria è condivisa tra tutti i membri.