Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Specificatori opzionali:
#include <stdio.h>
#include <limits.h>
int main(void) {
printf("SHRT_MIN=%d\n", SHRT_MIN);
printf("SHRT_MAX=%d\n", SHRT_MAX);
printf("INT_MIN=%d\n", INT_MIN);
printf("INT_MAX=%d\n", INT_MAX);
printf("LONG_MIN=%ld\n", LONG_MIN);
printf("LONG_MAX=%ld\n", LONG_MAX);
printf("USHRT_MAX=%u\n", USHRT_MAX);
printf("UINT_MAX=%u\n", UINT_MAX);
printf("ULONG_MAX=%lu\n", ULONG_MAX);
return 0;
}
Valutazione cortocircuitata = se c’è un’operazione logica falsa, non controllo anche le altre
(quando c’è && o | | ), rende più efficiente le valutazioni
CAST:
Nel programma (da vedere sulle slide) cels1 ha i valori corretti, cels2 ha sempre 0 perché fahr,
cels1 e cels2 sono stati dichiarati come interi, mentre cels2 doveva essere float.
Le operazioni su C sono definite per tipi omogenei (=tutti int, tutti float..).
Se faccio un’operazione tra tipi diversi, C prima rende omogenea (promuovendo le variabili —>
cambiando il tipo delle variabili, prendendo i tipi più piccoli e trasformandoli nei tipi più grandi)
l’espressione e poi fa l’operazione.
Cast = mettere il nuovo tipo tra parentesi prima della variabile ed è un comando che dice a C di
convertire quella determinata variabile.
( <tipo> ) <espressione>
i= (int)
Scritto così: (double) ((fahr - 32) * 5) / 9 solo la parte in rossa è soggetta al cast
Scritto così: (double) (((fahr - 32) * 5) / 9) non ha la virgola perché è intero/intero e la conversione è
avvenuta dopo la divisione (troppo tardi, ormai la parte decimale è stata scartata).
Siccome long arriva a 9miliardi di miliardi, se nell’esempio visto prima con la somma di 2 miliardi +
2 miliardi, non vado più in overflow, a meno che non inserisca il risultato nella variabile long. Per
risolvere questo problema facciamo un cast, per far sì che sia una somma tra long e non tra interi.
#include <stdio.h>
#define BIG 2000000000 //2 miliardi
int main(void) {
int b = BIG, c = BIG;
long a;
a = b + (long) c;
printf("%ld\n", a); return 0;
}
Bisogna cercare di non mettere mai dei numeri nel proprio codice (è meglio inserirli nelle costanti).
Quando facciamo la compilazione prima di -o mettere anche -Wall (genera tutti i warnings) e -
pedantic.
04/12/17
I puntatori
Si chiama così perché permette di indicare (puntare) a qualcosa che sta in memoria (ad esempio
un’altra variabile).
• realizzare il passaggio di parametri per riferimento (la funzione può cambiare il valore del
parametro).
double *pd
int *pi; pi
5
pi = &v pi v
b 5 pi 5 v
*pi = 9 vuol dire “prendo il numero 9 e lo inserisco nella locazione di memoria di pi (cambio il
valore di v, anche senza nominare v). Siccome *pi è di tipo intero posso assegnarlo a un altro tipo
intero.
pi 9 v
Quando lo uso negli assegnamenti (int b= *pi) vuol dire deferenzia il puntatore.
[imm]
Se non inizializzo la variabile avrò dei bit “a caso”, stessa cosa con i puntatori: se non li inizializzo
avranno degli indirizzi di memoria assegnati casualmente, e quindi non sappiamo dove sta
puntando.
Passaggio per valore (by value)= trasferisco una copia del parametro a una variabile. La funzione
che invoco con questo valore non cambia il valore delle variabili del main.
Passaggio per riferimento (by reference)= invoco una funzione passando per una variabile di
riferimento, quindi la funzione può cambiare valore.
Quando invoco una funzione nello stack (=struttura dati) viene si alloca una zona di memoria
(record di attivazione)
Stack:
• LIFO (Last In, First Out) = l’ultima cosa che metto è la prima cosa che tolgo
push(A) —> A
push(B) —> BA
pop —> BA
pop —> A
pop —>
Simulazione della struttura dello stack perché esco dalle funzioni partendo dall’ultima inserita.
Quando faccio il passaggio per valore nel record di attivazione della funzione viene messo una
copia della variabile. Quindi la funzione che invoco opera sulla copia della variabile che sta nel
record di attivazione. Quando finisco di eseguire quella funzione il record di attivazione viene
distrutto e tutte le modifiche fatte sulla variabile vengono perse (perché sono state fatte sulla
copia all’interno del record).
Esempio:
Quando arrivo a return 0, val vale 6 e m vale 5 (perché abbiamo lavorato sulla sua copia).
Passaggio per riferimento: possibilità di cambiare il valore delle variabili nel main. In C possiamo
simulare il passaggio per riferimento usando i puntatori.
Invece che passare direttamente la variabile possiamo passare l’indirizzo della variabile.
Esempio:
ARRAY E PUNTATORI:
con int a[5] non ho solo 5 celle ma ce n’è anche una extra che contiene l’indirizzo dell’array (del
primo elemento dell’array).
valore di int a[5]: a == &a [0] (prendo l’array di a, accedi al primo intero e ricava il suo indirizzo).
La relazione tra array e puntatori è che quando scrivo un array è come se scrivessi un puntatore.
Non sono però la stessa cosa, perché a un puntatore posso assegnare un valore ma un array non
posso assegnare nuovi valori.
quando ho una funzione che ha come parametro un array, per C è identico a scrivere un
parametro come int*a.
Esempio:
#include <stdio.h>
#include <ctype.h> //per il prototipo di toupper
Con le stringhe in scanf non metto la & perché non posso modificare il valore del char. Non serve
neanche quando passiamo un vettore.
11/12/17
Scope, strutture
Scope = visibilità, porzione di codice in cui l’identificatore è definito e ha senso. Se usiamo una
variabile al di fuori del suo scope è come se non fosse definita.
Nello stesso scope non posso definire la variabile per due volte ma posso farlo nello stesso
blocco di codice.
Se l’identificatore si riferisce a una funzione inizia dal punto in cui viene definita e che termina alla
fine del file sorgente. Posso richiamarla dal punto in cui la dichiaro fino alla fine del codice.
#include<stdio.h>
return 2*x-3*y;
}
int a= f(3,5);
printf(“%d”, a);
return 0;
2° modo —> lascio la definizione in un’altro punto e metto prima il prototipo della funzione f:
#include<stdio.h>
int a=f(3,5);
printf(“%d”, a);
return 0;
return 2*x+3*y
☛ include serve a includere i prototipi delle funzioni (tra cui ad esempio il prototipo di printf)
STRUTTURE
struct <etichetta> {
definizione-di-variabili
};
Esempio:
struct studente {
char nome[20];
float media;
};
non definiamo una variabile ma un tipo. Struct studente possiamo applicarlo a più variabili.
Si possono definire anche degli array, (es. struct studente elenco ]50]) ogni elemento è di tipo
“struct studente”.
Posso anche combinare strutture + puntatori (posso avere dei puntatori a strutture)
si usa ->
Come accedere a d:
1: dichiaro la struttura;
2: se voglio che si allochi la memoria definisco le variabili del tipo definito prima;
C cerca di allineare i dati alla parola di memoria per avere un trasferimento più efficiente.
se un intero coincide con una word allora basta una lettura. Se si accavalla tra 2 words allora ci
sarà bisogno di 2 letture.
➤posso assegnare variabili di un tipo struttura a variabili dello stesso tipo di struttura ma non
posso confrontarle.
Esempio:
struct data {
};
x.anno=x.anno+1;
struct data d;
d.giorno=11;
d.mese=12;
d.anno=2017;
return 0;
Con puntatori:
struct data {
};
void annosuccessivo(struct data *x) { // se modifico questo, modifico la copia del parametro
x->anno=x->anno+1;
struct data d;
d.giorno=11;
d.mese=12;
d.anno=2017;
return 0;