Fondamenti informatica
Utilità descrittori di formato
printf, scanf, fprintf, fscanf:
- printf("%d") o printf("%i") // Interi in rappresentazione decimale
- printf("%ld") // Long Int
- printf("%o") // Interi in rappresentazione ottale
- printf("%x") // Interi in rappresentazione esadecimale
- printf("%u") // Interi unsigned
- printf("%f") // Tipo float
- printf("%c") // Tipo char
- printf("%s") // Tipo stringhe
- printf("%p") // Tipo puntatore
Tipi di dato elementari e struct
Introduzione: abbiamo utilizzato esclusivamente tipi di dato elementari:
- int // Interi
- float // Float
- array[2] // Array
- *p // Puntatori
Ma nella pratica spesso vogliamo rappresentare strutture di dati più complessi.
Esempio: voglio rappresentare la coppia di coordinate (x, y), e se rappresento come 2 interi ho difficoltà di comprensione in quanto sono apparentemente scollegate le coppie, è complessa la gestione e ho difficoltà a dichiararle. Quindi utilizzo un altro strumento: le struct.
Esempio di (x, y) coppie con le struct:
struct Punto {
int x, y;
};
int main(void) {
struct Punto p1; // dichiarazione
struct Punto p[100];
p1.x = 100; // accesso ai campi con '.'
p1.y = p1.x;
p[10].y = 0;
}
Oppure ancora meglio utilizzare il typedef:
typedef struct __Punto {
int x, y;
} Punto;
int main() {
Punto p1; // dichiarazione
Punto p[100];
p1.x = 100; // accesso ai campi con '.'
p1.y = p1.x;
p[10].y = 0;
}
Per quanto riguarda invece l'utilizzo dei puntatori nelle struct posso usare l'operatore freccia:
typedef struct __Punto {
int x, y;
} Punto;
int main() {
Punto *p1; // dichiarazione
// accesso ai campi con '->'
p1->x = 100;
p1->y = p1->x;
}
È ammissibile che uno dei campi di una struct sia un puntatore allo stesso tipo di struct:
struct Punto {
int x;
int y;
struct Punto* ptr; // funziona solo coi puntatori!
};
Questo ci sarà utile nell'implementazione di tipi di dato ricorsivi: come liste, alberi, etc...
Mappa: generalità sui puntatori
I puntatori sono fondamentalmente delle variabili che rappresentano la locazione di altri elementi nel programma, come variabili, strutture, funzioni, array, file, ecc… Le variabili sono memorizzate nella memoria RAM del computer in un indirizzo specifico e per un numero di byte corrispondente alla loro dimensione.
Quindi in C l’operatore &, che abbiamo utilizzato nella funzione scanf, ci restituisce l’indirizzo di memoria di una variabile. Un indirizzo può essere assegnato solo a una speciale categoria di variabili denominate puntatori. Quindi i puntatori sono variabili abilitate a contenere un indirizzo.
Esempio di visualizzazione dell'indirizzo della variabile e del suo valore:
#include <stdio.h>
int main(void) {
int *pa, a = 10;
pa = &a;
printf("Indirizzo della variabile: %x ", pa);
printf("Valore variabile: %d ", *pa);
// oppure avrei potuto scrivere: printf("Valore variabile: %d ", a);
return 0;
}
Indirizzo della variabile: 62fe14
Valore variabile: 10
Esempio di visualizzazione del puntatore:
int main(void) {
int *pa, a;
pa = &a;
*pa = 10; // è equivalente ad a=10
printf("Il valore di a è: %d ", a);
return 0;
}
Il valore di a è: 10
Secondo esempio di visualizzazione del puntatore:
int main(void) {
int *pa, *pb, a;
pa = &a;
pb = pa;
*pb = 10;
printf("Il valore di a è: %d \n", a);
printf("Il valore di pa è: %x \n", pa);
printf("Il valore di pb è: %x \n ", pb);
return 0;
}
Il valore di a è: 10
Il valore di pa è: 62fe0c
Il valore di pb è: 62fe0c
Puntatori in C
Un puntatore è un tipo di dato, una variabile che contiene l'indirizzo in memoria di un'altra variabile. Si possono avere puntatori a qualsiasi tipo di variabile. I puntatori possono essere stampati con una printf con operatore di formato %p:
int a = 5;
int *pi;
pi = &a;
printf("indirizzo di a = %p\n", &a); /* stampa 0xA010 */
printf("valore di pi = %p\n", pi); /* stampa 0xA010 */
printf("valore di &*pi = %p\n", &*pi); /* stampa 0xA010 */
printf("valore di a = %d\n", a); /* stampa 5 */
printf("valore di *pi = %d\n", *pi); /* stampa 5 */
printf("valore di *&a = %d\n", *&a); /* stampa 5 */
I puntatori (come tutte le altre variabili) devono venire inizializzati prima di poter essere usati. È un errore dereferenziare una variabile puntatore non inizializzata.
Tipi di variabile puntatore
Il tipo di una variabile puntatore è puntatore a tipo. Il suo valore è un indirizzo. I tipi puntatore sono indirizzi e non interi:
int a; // Dichiarazione di Variabile A
int *pi; // Dichiarazione del puntatore pi
a = pi; // Assegnazione ad a il puntatore
/* compilando si ottiene un warning: assignment makes integer from pointer without a cast */
La funzione sizeof
La funzione sizeof restituisce l’occupazione in memoria in byte di una variabile.
int x = 10;
sizeof(int);
sizeof(x);
Operazioni con i puntatori
Sui puntatori si possono effettuare diverse operazioni:
- Dereferenziazione:
int *p, i;
i = *p;
- Assegnamento:
int *p, *q;
p = q;
- Di confronto:
if (p == q) ...
// oppure
if (p > q) ...
- Aritmetiche: incremento (++) o decremento (--), somma (+=) o sottrazione (-=) di un intero, sottrazione di un puntatore da un altro.
Generalità avanzate sugli array
Un array può essere definito con int vet[1] ma questa scrittura è equivalente a scrivere *(vet+1) utilizzando il puntatore. Il fatto che il nome dell’array non indichi l’array, ma l’indirizzo iniziale dell’area di memoria ad esso associata ha una conseguenza: è impossibile denotare un array nella sua globalità, in qualunque contesto. Questa è la spiegazione, ad esempio, del motivo per cui non si può assegnare un array ad un altro.
#include <stdio.h>
int main (void) {
int vet[10];
printf("\nStampa del Vettore con & %d", &vet); // Stampa l'indirizzo della memoria in cui sta l'array
printf("\nStampa del Vettore senza & %d", vet); // Stampa l'indirizzo della memoria in cui sta l'array
}
Stampa del Vettore con & 6487536
Stampa del Vettore senza & 6487536
Le stringhe
Una stringa di caratteri in C è un array di caratteri (array di char) terminato dal carattere '\0'. Una stringa può essere inizializzata come qualsiasi altro array elencando le singole componenti delle parole:
char s[5] = {'C', 'i', 'a', 'o', '\0'};
Oppure si può utilizzare la forma compatta:
char s[5] = "Ciao"; // Carattere di terminazione '\0' è automaticamente incluso in fondo
Lettura di una stringa
Una stringa può essere letta da tastiera e può essere stampata come ogni altro array:
char str[4];
scanf("%s", str); // ATTENZIONE! Si utilizza lo specificatore di formato %s NON bisogna usare il & per leggere la stringa
Stessa cosa si utilizza printf per la stampa della stringa a video terminale.
Funzioni utili per la manipolazione di stringhe (string.h)
La libreria che ci permette di manipolare con delle funzioni prefatte è string.h:
- int tolower(int c) // Restituisce c minuscola se il dato iniziale è maiuscolo
- int toupper(int c) // Restituisce c maiuscola se il dato iniziale è minuscolo
- char *strcpy(* stringa destinazione, * stringa da copiare);
- char *strcat(char *dest, const char *src);
- int strcmp(*stringa2, *stringa1) // Restituisce 0 se sono uguali altrimenti 1
Le strutture
Una struttura è una collezione finita di variabili di diverso tipo ognuna identificata da un nome:
struct <etichetta> {
<definizione-di-variabili>;
};
Esempio di struttura:
struct persona {
char nome[20];
int eta;
float stipendio;
};
Guardando l'esempio sopra, con l’etichetta si attribuisce un nome alla definizione della struttura. Nome, età e stipendio sono i campi (o membri) della struttura. ATTENZIONE: la definizione della struttura non provoca allocazione di memoria, ma introduce un nuovo tipo di dato.
Una variabile di tipo struttura può essere dichiarata contestualmente alla definizione della struttura. In questo caso si può anche omettere l’etichetta di struttura.
struct data {
int giorno;
int mese;
int anno;
} d1, d2;
d1 e d2 sono variabili di tipo struct data
Una volta definita una variabile struttura, si accede ai singoli campi mediante la notazione puntata:
struct punto {
int x, y;
} p1, p2;
// ...
p1.x = 10;
p1.y = -2;
p2.x = 5;
p2.y = 7;
Se ho un puntatore ad una struct, per accedere ai suoi campi posso usare l’operatore -> (freccia):
struct data {
int giorno, mese, anno;
};
struct data d, *p;
// ...
p->giorno = 15;
p->mese = 8;
p->anno = 2005;
Utilizzo dei puntatori nelle strutture
È possibile utilizzare il puntatore delle struttura in questo modo:
#include <stdio.h>
#include <malloc.h>
struct data {
int giorno, mese, anno;
} *p;
int main(void) {
p = malloc(sizeof(struct data));
p->giorno = 15;
p->mese = 8;
p->anno = 2005;
printf("%d", p->giorno);
}
15
Attenzione è importantissimo allocare prima la memoria dinamicamente altrimenti viene dato in output un errore di memory. Questo è possibile farlo pure senza puntatore:
#include <stdio.h>
struct data {
int giorno, mese, anno;
} p;
int main(void) {
p.giorno = 15;
printf("%d", p.giorno);
}
15
typedef
In C l'utente può introdurre nuovi tipi tramite una definizione di tipo:
typedef <TipoEsistente> <NuovoTipo>;
Esempio:
typedef int MioIntero;
MioIntero X, Y, Z;
int W;
Con le nozioni arrivate fino adesso siamo in grado di definire un nuovo tipo array e struttura, come segue:
typedef char string[20];
typedef struct {... } persona;
Ciò significa che non bisogna definire per esteso ogni volta che si definisce una nuova variabile come:
string s1, s2; // due stringhe di 20 caratteri
persona p1, p2; // due strutture "persona"
Le librerie standard
Generalità
La libreria standard del C è in realtà un insieme di librerie. Per usare una libreria bisogna includere il file header (.h) con #include:
#include <libreria.h>
Le librerie più conosciute
- #include <assert.h>: contiene la macro assert, utilizzata per identificare errori logici ed altri tipi di bug nelle versioni di debug di un programma.
- #include <limits.h>: contiene delle costanti definite che indicano le proprietà specifiche dell'implementazione dei tipi interi, come l'intervallo dei numeri rappresentabili (_MIN, _MAX).
- #include <math.h>: per le funzioni matematiche comuni.
- #include <stdio.h>: fornisce le funzionalità basilari di input/output del C. Questo file include il prototipo della venerabile funzione printf.
- #include <stdlib.h>: per eseguire un gran numero di operazioni, tra cui conversioni, numeri pseudocasuali, allocazione di memoria, controllo del processo, variabili d'ambiente, segnali, ricerca ed ordinamento.
- #include <string.h>: per manipolare le stringhe.
- #include <time.h>: per convertire tra vari formati di data e ora.
La libreria <stdio.h>
La libreria stdio.h contiene:
- Funzioni
- Definizione di tipi di dato
- Definizione di costanti
- Definizione di variabili
Le funzioni in C
Variabili locali all'interno della funzione
Al termine dell'esecuzione di una funzione le variabili locali vengono distrutte, quindi nulla è rimasto del lavoro svolto dalla funzione. Per questo motivo si utilizza il passaggio per riferimento. Un array non può essere un parametro formale, ma posso passare solo il puntatore all’array.
Cosa significa passaggio per riferimento? Si trasferisce un riferimento ai parametri attuali (cioè i loro indirizzi). Nel trasferimento per riferimento la funzione non dovrebbe mai alterare il valore del puntatore. Per questo motivo se a e b sono puntatori allora si deve scrivere:
int scambia(int *a, int *b) {
*a = *b;
}
Esempio di funzione scambia valori per riferimento:
#include <stdio.h>
void scambia(int *a, int *b) {
int t;
t = *a;
*a = *b;
*b = t;
}
int main(void) {
int a = 5;
int b = 7;
scambia(&a, &b);
printf("Valore di A = %d ", a);
printf("Valore di B = %d ", b);
}
Passaggio di variabili per riferimento o indirizzo
C è un linguaggio a basso livello e diversamente dal PHP o altri linguaggi simili, per il passaggio delle variabili in una funzione ha bisogno di utilizzare i puntatori alla memoria:
#include <stdio.h>
void scambia(int Val1, int Val2);
int main() {
int a = 5, b = 10;
scambia(a, b);
printf("a=%d, b=%d", a, b);
return 0;
}
void scambia(int Val1, int Val2) {
int ValAppoggio;
Val1 = ValAppoggio;
Val1 = Val2;
Val2 = ValAppoggio;
}
Se esegui questo programma vedrai che compilando ti stamperà a video i valori a=5 e b=10 quindi il programma non ha eseguito lo scambio. Utilizziamo i puntatori.
Cosa sono i puntatori?
I puntatori sono variabili "speciali" che ci permettono di passare l'indirizzo di locazione di memoria in cui si trova la variabile.
Variabili:
int a; // Definizione di Variabile normale
a = 3; // Definizione di Variabile con valore iniziale
Puntatore:
int *a; // Definizione di Puntatore
*a = 3; // Modifico il valore del puntatore in memoria (Ma non l'indirizzo)
a; // Accedo all'indirizzo del puntatore
&a; // Passo l'indirizzo del puntatore
Quindi basta mettere nella funzione per far sì che la funzione acceda alla locazione di &a e &b memoria e dove si trovano appunto le variabili e non al valore delle variabili. Però nell'implementazione della funzione bisogna inserire dei puntatori int *a, int *b.
In questo modo il codice funziona correttamente:
#include <stdio.h>
void scambia(int *Val1, int *Val2);
int main() {
int a = 5, b = 10;
scambia(&a, &b);
printf("a=%d, b=%d", a, b);
return 0;
}
void scambia(int *Val1, int *Val2) {
int ValAppoggio;
ValAppoggio = *Val1;
*Val1 = *Val2;
*Val2 = ValAppoggio;
}
a=10, b=5
Passaggio di un vettore per riferimento o indirizzo
Un vettore non è possibile passarlo per valore ma bisogna passarlo esclusivamente per riferimento o indirizzo, quindi bisogna utilizzare i puntatori. I puntatori sono fondamentalmente delle variabili che rappresentano la locazione di altri elementi nel programma, come variabili, strutture, funzioni, array, file, ecc…
Ripassiamo:
int v[DIM]; // Dichiarazione del Vettore
v[i] = 5; // Dichiarazione del Valore nel Vettore
v; // Indirizzo di loc. di memoria del Vettore
Adesso scriviamo un programma che passa un vettore in una funzione e stampa i valori del vettore di dimensione 50 minori di 10:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define DIM 50
int cerca(int *v); // Prototipo della funzione
int main() {
int vet[DIM]; // Dichiarazione del Vettore con dimensione DIM
int i, n; // Dichiarazione delle variabili di servizio
/* inizializziamo il generatore sull'ora attuale
dell'elaboratore time(0), in questo modo si hanno
valori diversi */
srand(time(0));
printf("Numeri Generati: ");
for (i = 0; i < DIM; i++) {
vet[i] = 1 + rand() % 100; // 50 numeri casuali tra 1 e 100
printf("%d - ", vet[i]); // Stampa numero generato
}
printf("\n Valore di Numeri Minori di 10: ");
n = cerca(vet); // Richiamo della funzione cerca
printf("%d", n); // Stampa
}
int cerca(int *v) {
// Funzione che conta i numeri nel vettore
int count = 0;
for (int i = 0; i < DIM; i++) {
if (v[i] < 10) {
count++;
}
}
return count;
}
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
-
Appunti Fondamenti di informatica
-
Appunti Fondamenti di informatica
-
Appunti Fondamenti di informatica
-
Appunti Fondamenti di informatica