Indice
- 0. Introduzione: download
- 1. Concetti base
- 2. Gestione dell’output
- 3. Gestione della variabili e costanti
- 4. Gestione dell’input
- 5. Uso degli operatori
- 6. Istruzione di selezione
- 7. Istruzioni di iterazione
- 8. ISTRUZIONI DI SALTO
- 9. Funzioni
- 10. Variabili automatiche, esterne, statiche e registro
- 11. Gestione dei vettori
- 12. Gestione dei puntatori
- 13. Programmazione time
Il linguaggio di programmazione C è stato sviluppato intorno tra il 1969 e il 1973 da Dennis Ritchie per l’uso del sistema operativo UNIX, pur non essendo vincolato a nessuna macchina o sistema operativo, diventando uno dei linguaggi di programmazione più diffusi al mondo.
La sua fama è dovuta al fatto che sia un linguaggio di programmazione di uso e portata generale, oltre che alla sua efficienza.
Gli antenati del C possono essere riuniti nel seguente elenco:
- Algol 60 – 1960 (Comitato Int.)
- CPL – 1963 (Cambridge)
- BCPL – 1967 (Cambridge)
- B – 1970 (Thompson)
- C – 1972 (Ritchie)
L’Algol presentava una regolarità nella sintassi, struttura nei moduli ma era estremamente complesso. Il CPL (Combined Programming Language) e il BCPL migliorarono alcune caratteristiche dell’Algol, senza riuscire a renderlo più semplice. Il linguaggio B era molto legato alla struttura hardware. Ritchie ha amalgamato le migliori caratteristiche dei predecessori sviluppando, in questo modo, il Linguaggio C.
Nel corso degli anni il Linguaggio C ha subito continue modifiche, questo, insieme all’aumento della popolarità e allo sviluppo di compilatori da terze parti, ha creato la necessità di definire il linguaggio in maniera più precisa e aggiornata di quella della prima edizione. Nel 1983, infatti, l’American National Standard Institue (ANSI) ha fondato un comitato con l’obiettivo di produrre una “definizione del C chiara e indipendente dalla macchina”. Il risultato è lo standard ANSI per il C.
Lo standard formalizza l’assegnamento fra strutture e le enumerazioni, fornisce una nuova forma di dichiarazione delle funzioni che permette il controllo incrociato fra definizioni e loro uso, specifica una libreria standard, numerose funzioni utilizzate sia in ingresso che in uscita dei dati, una nuova gestione della memoria e delle stringhe. Tutto questo e altro, continuando a stabilire quali aspetti del linguaggio devono rimanere indipendenti dalla macchina.
Gli standard definiti negli anni sono stati i seguenti:
- C89
- C99 – Adottato nel 2000 e prevedeva l’aggiunta dell’Header, oltre che il miglioramento di funzioni e librerie
- Successivamente furono pubblicati altri due standard con lo scopo di supportare al meglio il lavoro del compilatore e del linker
- C11 – Nel 2011 e standardizza il modello della memoria per supportare al meglio la programmazione multi thread
In quest’ottica, nel 2018, si è arrivati all’ultimo standard, il C18, che non introduce nuove caratteristiche rispetto ai precedenti ma è mirato a risolvere difetti dello standard precedente.
Il nostro Team utilizza il principalmente linguaggio C per scrivere il codice relativo a tutto il sistema di controllo e di interfaccia grafica del ROV. Gli script dedicati alla gestione di sistemi più specializzati vengono formulati invece in MatLab e Python.
Nicole Mella
Fonti: Storia del linguaggio C – html.it, Laboratorio di Algoritmi e Strutture Dati – Aniello Murano (seconda lezione), Il linguaggio C: principi di programmazione e manuale di riferimento – Di Brian W. Kernighan, Dennis M. Ritchie, Linguaggio C – Wikipedia
0. Introduzione: download
Installare:
https://sourceforge.net/projects/mingw/files/

https://www.codeblocks.org/downloads/binaries/

1. Concetti base
IDE: ambiente di sviluppo integrato. Ambiente di sviluppo realizzato per aggregare tutti gli strumenti di sviluppo che servono per realizzare il programma.

In basso verranno visualizzati i possibili errori.
STRUTTURA DEL PROGRAMMA

COMANDI LEZIONE:
// | Commento su riga singola |
/* */ | Commento su riga multipla |

Int main (parametri che si vogliono passare all’inizio dell’esecuzione).
Se inseriamo “void” stiamo inserendo nessun parametro da richiamare.
2. Gestione dell’output



Putchar(‘variabile‘) | Restituisce a schermo il singolo carattere. Si può utilizzare anche il linguaggio ASCII, ad esempio possiamo inserire all’interno del comando “97” che corrisponde al carattere “a” in ASCII. |



Printf(“testo”) | Trasforma i valori all’interno delle virgolette in caratteri e li mostra a schermo. In particolare, li traduce in stringe. All’interno del comando si possono specificare la tipologia di variabile che andrà ad essere utilizzata. |
%d, %i | Numero intero |
%c | Carattere |
A virgola mobile | %f |
Stringa | %s |

Possiamo estendere il ragionamento con:




Puts(“testo”) | Riporta il “testo” direttamente in stringa. Si preferisce al printf solo per questioni di sicurezza. |

Si vede all’interno del codice il \n che è un simbolo che corrisponde al ” a capo”.
Puts comprende intrinsecamente già il comando di escape (azione speciale letto dal compilatore)
\a | : Segnale acustico o visivo d’allarme |
\b | : Sposta il cursore indietro di una posizione |
\f | : Sposta il cursore all’inizio della successiva pagina logica |
\v | : Tabulazione verticale |
\n | : Sposta il cursore alla riga successiva |
\r | : Sposta il cursore all’inizio della riga corrente |
\t | : Tabulazione orizzontale |
\\ | : Visualizza il carattere \ |
\” | : Visualizza il carattere “ |
\’ | : Visualizza il carattere ‘ |
\! | : Visualizza il carattere ! |
\? | : Visualizza il carattere ? |
\0 | : Carattere nullo delle stringhe |
SPECIFICHE DI CONVERSIONE (facoltative)
Tra il simbolo % e il carattere di conversione:
- Segno – che segnala l’allineamento a sinistra.
- Un numero che specifica l’ampiezza del campo.
- Un punto che separa l’ampiezza del campo dalla specifica del grado.
- Un numero che segnala il grado di precisione.

3. Gestione della variabili e costanti
Variabile: È un contenitore di dati situato in una porzione di memoria (una o più locazioni 0di memoria) con il compito di contenere valori. Suscettibili di modifica nel corso dell’esecuzione di un programma.
Costante: È un dato non modificabile situato in una porzione di memoria (una o più locazioni di memoria) Che può contenere un valore. ma non è permesso modificarlo nel corso dell’esecuzione di un programma.
Variabili locali o private del main:
Sono utilizzabili solo all’interno della funzione main.

Variabili che scompaiono a fine richiamo.
DICHIARAZIONE:


Costanti:
– A differenza delle variabili hanno valori predefiniti fissi per l’intera durata del programma.
– Vengono utilizzate per valori matematici che rimangono sempre costanti (es. lt).
– Si possono dichiarare in molteplici modi a seconda dell’uso da fare.
Diverse modalità di definizione delle costanti:
Nel preprocessore: Per poter definire le costanti è necessario definire diverse librerie:


In questo modo le costanti saranno definite per tutto il programma.
Corpo principale: basta inserire precedentemente “const“: la considera immutabile per tutto il resto del programma.


Enumerazioni: lista di valori costanti eseguibile con un tag opzionale


Se non viene dichiarato nulla le costanti si auto definiranno partendo dalla prima con il valore 0, le seguenti sanno “valore precedente +1”. In alternativa è possibile dare in ingresso un valore iniziale.

In alternativa è possibile dare un qualsiasi valore ad ogni costante.
CONVERSIONI IMPLICITE ED ESPLICITE TRA I TIPI DI DATO
Per eseguire operazioni è necessario che ogni dato sia dello stesso tipo.
Nella conversione dei dati è possibile avere una perdita di informazioni oppure aggiungere variabili.
Vi sono le “conversioni implicite”.
Conversioni implicite:
- Conversioni aritmetiche che avvengono nei calcoli.
Regole di massima:
- Se uno degli operandi è long double, l’altro si converte in long double;
- Altrimenti se uno degli operandi è double. l’altro Si converte in double;
- Altrimenti se uno degli operando è float, l’altro si converte in float;
- Altrimenti char e short si convertono in int;
- Se uno degli operandi è long, l’altro si converte in long

Conversioni esplicite
Conversioni esplicite sono esposte in qualsiasi operazione:


4. Gestione dell’input



Getchar() | Legge il carattere successivo e lo salva come intero. Le parentesi servono per salvare i dati un dato all’interno. Il valore restituito è in ASCII. Può essere usato come comando di stop a fine programma. |




La funzione scanf() ritorna come valore intero il numero di variabili a cui è assegnato un valore.
Nel caso fosse inserito in Input qualcosa di scorretto la funzione restituirà 0.

La stringa di composizione char *format può avere più di una specifica di conversione, e tra le specifiche i caratteri di tabulazione o spazi vuoti vengono ignorati. Altrimenti se si inseriscono caratteri specifici devono essere poi riportati durante l’input di tastiera.

L’asterisco consente di non assegnare il valore inserito da tastiera a una variabile; quindi, è possibile inserire un qualsiasi carattere (come nell’esempio) in mezzo agli altri valori interi. Mentre il valore indicato tra la % e il carattere di conversione indica l’ampiezza massima che possono essere inseriti (in questo esempio 2 per i giorni e mesi e 4 per l’anno).
III valore d’ampiezza deve essere sempre maggiore di 0.

Scanf() | Formato analogo al printf in materia di input. I comandi di printf e scanf sono leggermente diversi quindi è bene ricordarli dalla tabella. Ricordare di specificare sempre il tipo di dato in ingresso e porre prima della variabile la e commerciale “&”. |
MODIFICATORI DI AMPIEZZA
- h: Davanti agli specificatori d. i, u indica short int o unsigned short int.
- hh: Davanti agli specificatori d, i, u indica signed char o unsigned char.
- I: Davanti agli specificatori d. i, u indica long int o unsigned long int.
- II: Davanti agli specificatori d, i, u indica long long int o unsigned long long int.
- L: Davanti agli specificatori a. e. f. g. A, E. F. C indica long double.

5. Uso degli operatori
OPERATORE DI ASSEGNAZIONE (=)
Possibile multi-assegnazione
Permette di assegnare a una variabile un’espressione.
Per espressione si intende tutto ciò che restituisce un valore: operazioni
aritmetiche, funzioni.

OPERATORI ARITMETICI (+,-,*,/.,%)
Permettono di fare calcoli tra operandi, e sono:
- Addizione: +;
- Sottrazione: -;
- Moltiplicazione: *;
- Divisione: /;
- Resto della divisione: %;


La percentuale mi restituisce il modulo del resto della divisione in questo caso:
12 | / | 5 |
Resto | 2 | 2.4 |
0 |
ASSEGNAZIONI COMPOSTE
- addizione e assegnamento: += ;
- sottrazione e assegnamento: -=;
- prodotto e assegnamento: *=;
- divisione e assegnamento: /=;
- modulo e assegnamento: %=;
- AND bit per bit e assegnamento*: &=;
- OR bit per bit inclusivo e assegnamento*: |=;
- OR bit per bit esclusivo e assegnamento*: ^=;
- Shift a sinistra e assegnamento*: <<=;
- Shift a destra e assegnamento*: >>=;
Mi permettono di aggiungere ad una variabile un valore prefissato.

OPERATORI UNARI IN INCREMENTO/DECREMENTO
Incremento: variabile++ (suffisso); ++variabile (prefisso)
Decremento: variabile– (suffisso); –variabile (prefisso)

Simili a quelli visti in precedenza solo che hanno come valore fissato 1.
Importante è l’ordine di posizionamento dell’operatore (nel primo caso ha prima incrementato il valore e successivamente ha assegnato il valore, nel secondo caso ha prima assegnato il valore e poi ha incrementato il valore).
OPERAZIONI RELAZIONALI
Permettono di mettere in relazione due operandi e restituire come valore 0 nel caso sia falsa oppure 1 nel caso sia vera.
Equivalente del booleano
Relazioni:
- <, > (maggiore, minore);
- <=, >= (maggiore uguale, minore uguale);
- == (uguaglianza);
- != (disuguaglianza);

OPERATORI LOGICI
Consentono di collegare logicamente due o più relazioni:
- AND: &&
- OR: ||
- NOT: !


Attenzione: l’And ha priorità rispetto all’Or.
FACOLTATIVI: OPERATORI MANIPOLATORI DI BIT
Operatori applicabili solo a operandi di tipo intero:
- AND bit per bit: &;
- OR (inclusivo) bit per bit: |;
- OR (esclusivo) bit per bit: ^;
- shift a sinistra <<;
- shift a destra >>;
- complemento a 1 ~;
PRIORITÀ DEGLI OPERATORI

6. Istruzione di selezione
IF/ELSE
Istruzione di selezione singola IF:

Per eseguire le istruzioni appartenenti all’if la condizione posta deve essere vera, altrimenti non entra al suo interno.

Istruzione di selezione doppia IF/ELSE:

L’ else (“altrimenti”) gestisce la condizione contraria, quindi falsa, dell’if.


Espressione condizionale:


SWITCH
Istruzione di selezione multipla SWITCH:
il valore del case può essere numerico o un carattere tra apici (come ‘\n’, ‘c’, ‘ecc.)


Default: non soddisfa nessuna delle condizioni precedenti.
Il break serve ad interrompere l’esecuzione del programma.

ESERCIZI IF-ELSE



7. Istruzioni di iterazione
CICLO WHILE

Se la condizione è vera verranno eseguiti i comandi all’interno del corpo del ciclo.


Inserire anche una condizione che ti permetta di concludere il programma altrimenti si potrebbe entrare in un ciclo infinito.
CICLO FOR
Sia il while che for sono definiti cicli pre-condizionali, perché prima di eseguire i comandi interni al corpo controllano se la condizione è vera o falsa.

- Campo 1: dedicato alla dichiarazione di variabili contatori e/o inizializzazione di variabili create in precedenza.
- Campo 2: dedicato alla condizione del ciclo che verrà controllata a ogni ripetizione.
- Campo 3: dedicato alle operazioni (per esempio di incremento/decremento) da effettuare a ogni ripetizione.

Descrizione tipo di dato size_t:
- In genere raccomandato per rappresentare gli indici di un array.
- Su alcuni compilatori rappresenta unsigned int e su altri unsigned long.
- Questo tipo di dato è definito nell’intestazione <stddef.h> spesso già incluso in <stdio.h>; se usando questo dato riscontrate un errore basta includere la libreria <stddef.h>.


CICLO DO-WHILE

Il do-while a differenza delle due istruzioni precedenti è post-condizionale: valuta dopo l’esecuzione del corpo la condizione.
Essendo che il corpo del ciclo viene eseguito sempre almeno una volta questa istruzione risulta ottimale per controllare gli input inseriti da tastiera.

ISTRUZIONE DI ITERAZIONE
Sono state specificate le modalità consigliate di utilizzo dei diversi comandi.


INDICAZIONI ALL’USO DELLE ITERAZIONI:
- Evitare la creazione di cicli infiniti senza vie d’uscita.
- Non superare il terzo livello d’annidamento.
- Le variabili dichiarate nell’intestazione del ciclo for non si possono usare al di fuori del ciclo.
- Prevenire gli errori off-by-one nelle condizioni usando i connettivi relazionali più adatti.
- I cicli while e for con una sola istruzione possono omettere le parentesi graffe. Se un ciclo non ha comandi nel corpo si inserisce solo un:

ESERCIZI ITERAZIONI




8. ISTRUZIONI DI SALTO
BREAK

Serve a “rompere” la sequenza di comandi per uscire dalla struttura in cui è presente.
Si può utilizzare in:
- Iterazioni while, for, do-while.
- Istruzione switch.

Di fatto è una interruzione di comando.
CONTINUE

Serve a saltare i comandi successivi a questa istruzione, ma non esce dalla struttura di controllo, ne toma a capo.
Si può utilizzare in:
- Iterazioni while, for, do-while.

La possiamo interpretare come una funzione salta comandi.
GOTO
“teletrasporta il comando tramite delle etichette”.
Estremamente sconsigliato salvo alcuni casi:
https://wiki.sei.cmu.edu/confluence/plugins/servlet/mobile?contentId=87152289#content/view/87152289
RETURN

Serve a saltare i comandi successivi a questa istruzione, ma non esce dalla struttura di controllo, ne torna a capo.
Si può utilizzare in:
- Iterazioni while, for, do-while.

9. Funzioni
È un blocco di codice contenente dichiarazioni e istruzioni finalizzato a compiere determinate azioni.
Il suo scopo è suddividere un programma in parti più piccole, così da migliorare la chiarezza e rigidità.
Benefici dell’uso delle funzioni:
- Modularità: il programma è scomposto in piccole unità di elaborazione che agiscono come moduli, ciascuno con il proprio scopo.
- Riuso: ogni funzione può essere riutilizzata anche in diversi programmi.
- Mancanza di codice duplicato: grazie alle funzioni non serve riscrivere un pezzo di codice più volte.
- Occultamento algoritmico: la funzione “occulta” il modo in cui svolge un determinato compito.
DEFINIZIONE

- Tipo ritorno: indica il tipo di dato ritornato dalla funzione al suo chiamante.
- Nome funzione: deve essere unico e non confondersi con quello di altre parole chiavi. Inoltre, deve rispettare le stesse regole delle variabili:
- Non inserire numeri prima del nome, es: 3potenza, 45somma, ecc.
- Evitare possibilmente i trattini bassi prima del nome, es: potenza, radice, ecc.
- Parametri: sono i valori delle variabili che si vogliono far passare dalla funzione chiamante alla funzione chiamata, per poter lavorare con essi.
- parametri per valore: è una copia del valore che si vuole trasferire all’interno del parametro (che è una variabile automatica per la funzione).
- parametri per indirizzo
- Se non si vogliono passare parametri alla funzione si inserisce void.
- {…}: corpo della funzione dove è presente la serie di comandi che la funzione dovrà eseguire una volta chiamata.
- Return: serve a far tornare nel punto in cui la funzione è stata chiamata, con il calore che verrà assegnato nel main. Non è necessario che la funzione faccia tornare un valore esistono anche funzioni che non riportano alcun valore (queste sono funzioni a return void).
GENERAZIONE DELLA FUNZIONE
PROGRAMMA COMPLETO

È necessario dichiarare la funzione all’inizio: prototipo di funzione.
Una volta creata la funzione può essere chiamata quando si vuole.
Se si tratta di una funzione la si può assegnare a una variabile (anche usare nei calcoli), oppure anche come argomento del printf.
Se si tratta di una “procedura” allora basta soltanto richiamarla e inserire i suoi parametri. NON la si può assegnare a nessuna variabile o utilizzare come argomento del printf.
ALTRE MODALITA’: LIBRERIE
- Funzioni del linguaggio: si possono reperire e utilizzare tramite la direttiva #include <nomelibreria.h> che permette di richiamare la libreria all’interno del programma.
- Funzioni definite dal programmatore: vengono create dal programmatore e possono essere utilizzate in più programmi che ne richiedono l’uso.

PILA DELLE CHIAMATE E RECORD DI ATTIVAZIONE
Ritorno della funzione
Per comprendere come la funzione sappia tornare al punto di ritorno dobbiamo considerare una struttura che funziona dietro le quinte del nostro programma: pila o stack.
Ogni volta che aggiungiamo un nuovo elemento questo viene messo in cima, in cima agli elementi precedenti, questa operazione prende il nome di push. Quando invece viene rimosso un elemento dalla cima l’operazione prende il nome di pop.


Questa pila prende il nome di “pila di chiamata dele funzioni”, detta anche “pila di esecuzione del programma”.
Esempio: la funzione uno richiama la funzione due che a sua volta richiama la funzione tre le funzioni sono state aggiunte alla pila tramite l’operazione push.
L’ultimo elemento è chiamato “record di attivazione” che contiene l’indirizzo di ritorno che serve alla funzione chiamata per tornare alla funzione chiamante.

Se la funzione chiamata torna alla funzione chiamante senza dover chiamare altre funzioni allora tramite l’operazione pop il record di attivazione della chiamata viene estratto e trasferito nella funzione sottostante.

Oltre a tenere traccia dei movimenti, il record di attivazione ha il compito di memorizzare le variabili e farle esistere finche esiste la funzione.
Ovviamente la memoria è limitata e si può presentare un errore in cui il numero di chiamate è maggiore di quello supportato detto anche “stack overflow”.

10. Variabili automatiche, esterne, statiche e registro
Variabili automatiche

Proprietà:
- Durata in memoria automatica: il compilatore alloca uno spazio di memoria, e lo dealloca quando finisce il blocco di codice nella quale è contenuta la variabile.
- Sono visibili e utilizzabili solo nel codice di blocco a cui appartengono.
Se dichiariamo una variabile all’inizio del main, questa sarà disponibile per tutto il resto del blocco del codice, al contrario se la dichiaro nell’intestazione del ciclo questa sarà disponibile solo all’interno di questo.
Svantaggi delle variabili interne:
- Una volta concluso il blocco di codice, come per esempio una funzione, la variabile locale perde il suo contenuto.
- Se si vogliono passare tante variabili come parametri di una funzione il codice diventa difficile da leggere.
Variabili esterne

Sono dichiarate al di fuori delle funzioni e quindi sono disponibili per tutte le funzioni costantemente e si mantengono in memoria.
Proprietà:
- Durata in memoria dura fino alla fine del programma.
- Sono visibili e utilizzabili in qualsiasi blocco di codice.
Dichiarazione:
Si utilizza extern per importare la variabile in una specifica funzione. Per le funzioni dello stesso file non è obbligatorio l’uso di extern (per migliore leggibilità è meglio inserirlo).
È sconsigliabile abusarne, non è mai per esempio ogni funzione in grado di modificare contenuto della variabile diventa complicato trovare i possibili errori.
Variabili statiche

Proprietà:
- Se applicata a una funzione o variabile esterna indica la loro privatizzazione in quel relativo file, cioè non sono usufruibili da altri file esterni.
- Se applicata a una variabile interna, essa viene resa statica, ossia permane in memoria per tutta la durata del programma.
Variabili registro

Proprietà:
- Indica al compilatore che la variabile deve essere ottimizzata venendo memorizzata nei registri della CPU.
- Il compilatore può ignorare questa richiesta e trattare la variabile register come una semplice variabile automatica auto.
- Questo attributo Si può solo applicare alle variabili automatiche e ai parametri formali delle funzioni.
11. Gestione dei vettori
Vettore monodimensionale
Il problema delle variabili è la gestione di un solo valore per volta (tranne le costanti per cui il valore non cambia), tramite i vettori siamo in grado di gestire un insieme di valori in un’unica variabile.
Vettore: Gruppo di allocazioni di memoria contingue, aventi tutte lo stesso tipo di dato (assegnato).
Il vettore monodimensionale è così definito:


La cui rappresentazione è la stessa che è presente per le variabili:
- Tipo_dato: in cui dobbiamo specificare che tipologia di dato stiamo usando (int, bool etc),
- Nome: che ci permetterà di richiamare il vettore all’interno del blocco di codice
- Dimensione: Si definisce una costante simbolica tramite la direttiva #define nel preprocessore. Questa costante sarà utilizzabile in tutto il programma in modo da poter gestire il vettore in ogni circostanza.
Possiamo andare a richiamare qualsiasi valore all’interno del vettore in questo modo:

Possiamo generare un vettore vuoto semplicemente dichiarandolo nel modo seguente:

Possiamo definire la dimensione del vettore tramite l’uso delle librerie imponendo:


Vettori bidimensionali e tridimensionali
Bidimensionali: matrici

Tridimensionali: matrici di matrici

Inizializzare i vettori
Come detto in precedenza, possiamo dichiara un vettore nullo:

Possiamo specificare ogni singolo valore all’interno del vettore:

Se non vengono specificati tutti i valori, verranno semplicemente riempiti con degli zeri.

In alternativa posso andare a specificare le posizioni all’interno del vettore:

Non è obbligatorio specificare la dimensione del vettore, ma specificare i valori e di conseguenza il vettore avrà quella dimensione:

Vettori costanti e dinamici (VLA)
COSTANTI

I valori con cui si inizializza un vettore possono essere modificati costante NON durante l’esecuzione del programma.

DINAMICI
V: variable
L: lenght
A: array
La dimensione del vettore è definita durante il programma.

Risulta vantaggioso da un punto di vista di memoria (un vettore non usato completamente risulta più pesante).
Vettori come parametri di funzione
Come passare i vettori come parametri:

Con i vettori VLA:

Si contraddistinguono dall’asterisco al posto della dimensione.
12. Gestione dei puntatori
VARIABILI E PUNTATORI
Variabile: cella di memoria contenente un certo spazio che varia a seconda del tipologia di dato. Ogni variabile ha un suo indirizzo di memoria che serve al computer dove memorizzare il dato ed è qui che entra in gioco il “puntatore”: variabile al cui interno è contenuto l’indirizzo di un’altra variabile.
- *: operatore di indirezione
- &: operatore di indirizzamento o indirizzo

Se vogliamo vedere dove la variabile test è stata salvata ci basterà eseguire il seguente codice:

Creare un puntatore, significa andare a creare una variabile facendo uso dell’operatore di indirezione:

Quello che abbimao realizzato è creare in memoria uno spazio anche per l’indirizzo.
Possiamo visualizzare diverse informazioni nel seguente modo:

Importante sta anche nella definizione dei puntatori:
- Aggiungere “Ptr” al nome della variabile.
- Buona norma inizializzarli fin da subito con l’indirizzo di una variabile, in alternativa se non si punta a nulla si inserisce NULL, contenuta all’interno della libreria <stddef.h> NULL (possiamo usare anche 0 ma è sconsigliato).
PARAMETRI DI FUNZIONE PER RIFERIMENTO
Due tipologie di parametri:
- Passaggio per valore: si crea una variabile locale della funzione contenente il valore della variabile passata.

- Passaggio per riferimento: si passa l’indirizzo della variabile con la quale si vuole lavorare

- Possiamo evitare l’uso della & nel caso di scanf se usiamo i puntatori:

RELAZIONE PUNTATORI-VETTORI E ARITMETICA DEI PUNTATORI
Qualsiasi operazione realizzata tramite l’accesso ad un vettore tramite indice è ottenibile tramite i puntatori (l’impiego dei puntatori è più veloce seppur più complessa).
Possiamo muoverci all’interno del vettore tramite il puntatore:

Come si vede dal programma per poterci muovere all’interno del programma possiamo fare “puntatore++” o “puntatore–“.
Attenzione all’aritmetica dei puntatori:
se sommiamo al puntatore +4 non avremo una somma 4 ma una somma 16 (come nell’esempio), questo è dovuto al fatto che il puntatore tiene in considerazione il tipo di dato e di conseguenza lo spazio richiesto in memoria.
Avviene la seguente somma:
indirizzo attuale + (4*dimensione)
Nel caso degli interi:
indirizzo attuale + (4*4)
Nel caso dei caratteri:
indirizzo attuale + (4*1)
Problemi per quanto riguarda la sottrazione tra due puntatori:

Se per esempio l’indirizzo di ptrl è 3000 e l’indirizzo di ptr2 è 3008, allora:

Quindi si ottiene la differenza di posizioni.
Un altro modo di muoversi tra le celle specificando la posizione di una cella specifica è tramite la notazione “puntatore offset”:
Base + Offset:

Valgono le stesse regole viste prima per l’aritmetica dei puntatori.

Piccola parentesi: dimensione di un vettore.
sizeof: È un operatore unario che consente di visualizzare la dimensione in byte dalla variabile o tipo di dato indicato.

VETTORI DI PUNTATORI E ARGOMENTI DEL MAIN
i puntatori possono essere memorizzati in vettori e l’uso più comune è quello di utilizzare un array di stringhe dove ogni elemento dell’array è una stringa, ma nel c una stringa è essenzialmente un puntatore primo carattere.
Esempio di un vettore array di 4 elementi:

Per la gestione di ogni carattere invece necessito di un secondo contatore:

Adesso che abbiamo visto come gestirlo lo si può impiegare anche nei parametri di funzione del main.
Il main è a tutti gli effetti una funzione e che finora abbiamo messo nelle sue parentesi tonde void, perché non usiamo i parametri, ma in realtà se servono si possono usare così da passare dei valori esterni all’interno del nostro programma.
Dichiariamo quindi:

la variabile intera argc conta quanti sono i parametri passati mentre il vettore stringa argv memorizza in sequenza.
Dal promt dei comandi o terminale vediamo il programma e passate dei parametri che vengono memorizzati in argv e, per evitare che valutiate anche come parametro il nome del programma partita dal contatore uguale a u, perché zero è il nome del programma stesso, infatti, se non si passa nulla e lo si avvia solamente come parametro viene preso l’intero percorso in cui si trova il programma.
PUNTATORE A VOID
I puntatori possono esser assegnati ad altri puntatori rispettando il tipo di dato. L’unica eccezione è void. Il puntatore a void può rappresentare qualunque tipo di puntatore. A tutti i tipi di puntatore è possibile assegnare un puntatore void e a un puntatore void è possibile assegnare un puntantore di qualsiasi tipo.
13. Programmazione time
STRINGHE
Per poter esprimere le stringhe si usa un vettore di tipo char.

Vettore str di tipo carattere 20 byte allocati in memoria.
Nel vettore possono esserci fino a 20 caratteri: 1 per byte.
A fine stringa viene automaticamente inserito il carattere terminatore ‘\0’. Tale carattere segnala che la stringa è terminata e che non si deve visualizzare il contenuto delle celle successive a essa.
Oltre al carattere terminatore ‘\0’ sono presenti caratteri casuali che non hanno a che vedere con la stringa inserita in input.

Nell’effettivo ogni volta che si inserisce un nuovo contenuto all’interno del vettore, bisogna arrivare a stampare fino a ‘\0’. Essendo che corrisponde a O, basta inserire solo str[i] come condizione senza specificare !=‘\0’.
Attenti a NON superare lo spazio dedicato al vettore!



È buona pratica specificare tra la % e s il numero massimo -1 di caratteri da ricevere in input (cioè, definire la larghezza di campo). In questo modo rimane libera sempre almeno una cella per l’inserimento di ‘\0’.

La funzione fgets consente di leggere una stringa, delimitata da una dimensione massima, attraverso lo stream specificato.
Per indicare lo stream della tastiera si usa stdin.
Attenzione a NON utilizzare la funzione gets, poiché deprecata e potenzialmente dannosa.

fgets conclude la lettura della stringa quando riceve l’invio a capo l\n’ (considerandolo appartenente alla stringa) oppure quando riceve EOF:

End Of File è una costante simbolica con valore intero negativo (generalmente, ma non sempre, -1).

COMANDI DELLA LIBRERIA STDIO.H
Introduzione a nuovi comandi
sprintf(): è l’equivalente printf(), ma salva l’output all’interno di un vettore.

sscanf(): è l’equivalente scanf(), ma riceve l’input da un vettore e trasferisce i valori alle variabili singolarmente.

COMANDI LIBRERIA STDLIB.H
strtod(): da stringa a double
strtol (): da stringa a long int
strtoul(): da stringa a unsigned long int

COMANDI LIBRERIA STRING.H
strcpy(dest,sorg): Il primo parametro è il vettore destinazione, mentre il secondo è la sorgente.

strncpy(dest,sorg,n): Vengono copiati solo i primi n caratteri della stringa sorg.

strcat(dest,sorg): Concatena a dest la stringa sorg.

strncat(dest,sorg,n): Concatena a dest solo n caratteri della stringa sorg.

strcmp(str1, str2): Confronta le due stringhe inserite come parametri:
- restituisce 0 se le stringhe sono uguali.
- Un valore maggiore di 0 se strl < str2.
- Un valore minore di 0 se strl > str2.

strncmp(str1, str2, n): Confronta solo i primi n caratteri di entrambe le stringhe e ritorna i valori maggiori, minori o uguali a 0, nella stessa maniera di strcmp.

Strchr(str, c): localizza la prima occorrenza del carattere c nella stringa str.
Strpbrk(str1, str2): localizza la prima occorrenza dei caratteri di str2 nella stringa str.
Strrchr(str, c): localizza l’ultima occorenza del carattere c nella stringa str.

strerror(valore); Visualizza un messaggio di errore a seconda del valore ricevuto come parametro. La lista degli errori è presente nella libreria errno.h.
strlen(str); Conta il numero di caratteri presenti fino ad arrivare a ‘\0’.
Edoardo Brienza
Lascia un commento