Il linguaggio C
Utilizzo avanzato di array e puntatori
Gli array multidimensionali
Gli array di puntatori
I puntatori a puntatori
Le classi di memorizzazione
Durata fissa e durata automatica
L’ambito di visibilità
Fondamenti di Informatica I  a.a. 2008-09
1
Utilizzo avanzato di array e puntatori
Fondamenti di Informatica I  a.a. 2008-09
2
Gli array multidimesionali  1
Un array di array è un array multidimensionale e viene
dichiarato per mezzo di una sequenza di coppie di parentesi
quadre
/* x è un array di tre elementi costituiti
* da array di cinque elementi
*/
int x[3][5];
Anche se un array multidimensionale viene memorizzato
come una sequenza di elementi, può essere manipolato come
un array di array
Per accedere ad un elemento di un array multidimensionale
occorre specificare tanti indici quante sono le dimensioni
dell’array
Fondamenti di Informatica I  a.a. 2008-09
3
Gli array multidimensionali  2
Gli array multidimensionali sono memorizzati con precedenza
delle righe, cioè l’ultimo indice varia più velocemente
Esempio:
int ar[2][3] { {0,1,2},
{3,4,5}
};
Nell’inizializzazione, ogni riga di
valori è racchiusa fra parentesi
graffe (in questo caso, servono
per migliorare la leggibilità)
Fondamenti di Informatica I  a.a. 2008-09
1000
1004
1008
100C
1010
1014
ar[0][0]
0
1
2
ar[0][1]
3
4
5
ar[1][1]
ar[0][2]
ar[1][0]
ar[1][2]
1018
4
Gli array multidimensionali  3
L’accesso all’elemento ar[1][2] viene interpretato come *(ar[1]2),
ovvero *(*(ar1)2)
Poiché ar è un array di array, viene effettuato un doppio scaling:
…quando si valuta *(ar1), “1” rappresenta un array di tre interi (12
byte sulla macchina di riferimento)
…quando si valuta *(*(ar1)2), “2” rappresenta 2 interi (8 byte)
 complessivamente si ha uno spostamento di 20 byte rispetto all’indirizzo
base (si ottiene l’indirizzo esadecimale 1014)
Se vengono specificati meno indici rispetto alle dimensioni, il
risultato è un puntatore al tipo base dell’array; per esempio…
ar[1] è equivalente a &ar[1][0]
e fornisce come risultato un puntatore ad int
Lo standard ANSI non impone limiti al numero di dimensioni degli
array; è comunque richiesto di gestire almeno array a sei dimensioni
Fondamenti di Informatica I  a.a. 2008-09
5
L’inizializzazione di
array multidimensionali  1
In fase di inizializzazione di un array multidimensionale,
occorre specificare ogni riga tra parentesi graffe
Se i valori iniziali non sono sufficienti, ed il vettore è static,
gli elementi mancanti vengono inizializzati a zero
Esempio:
static int examp[5][3]  { {1,2,3},
{4},
{5,6,7}
};
( )
1
4
5
0
0
2
0
6
0
0
3
0
7
0
0
In modo analogo al caso dei vettori, se viene “parzialmente”
omessa la dichiarazione della dimensione di un array
multidimensionale, il compilatore la calcola sulla base del
numero dei valori iniziali specificati
Fondamenti di Informatica I  a.a. 2008-09
6
L’inizializzazione di
array multidimensionali  2
Nel caso degli array multidimensionali, infatti, può essere
omessa la dimensione dell’array più esterno, mentre è
obbligatorio specificare le altre
Esempio:
static int a_ar[][2]  {{1,1},{0,0},{1,2}};
produce un array di dimensione 32, perché sono presenti 6
valori iniziali
Esempio:
static int b_ar[][]  {{1,2,3},{4,5,6}}; /* SCORRETTO */
Non si può determinare se l’array è 32 o 23 (il
raggruppamento dei dati di inizializzazione non è
sufficiente!): specificare la seconda dimensione avrebbe
risolto ogni ambiguità
Fondamenti di Informatica I  a.a. 2008-09
7
Array multidimensionali come
argomenti di funzione  1
Per passare un array multidimensionale come argomento di
funzione è sufficiente specificarne il nome: il valore passato è un
puntatore all’elemento iniziale dell’array che è ancora un array
Nella funzione chiamata, l’argomento deve essere dichiarato in
modo appropriato
int f1()
{
static int ar[5][6];
………
f2(ar);
………
}
void f2(received_arg)
int received_arg[][6];
{
………
}
int (*received_arg)[6];
È possibile omettere la dimensione dell’array che viene passato, ma
è necessario specificare la dimensione di ogni elemento dell’array
Fondamenti di Informatica I  a.a. 2008-09
8
Array multidimensionali come
argomenti di funzione  2
Una modalità alternativa consiste nel passare esplicitamente un
puntatore al primo elemento e la dimensione dell’array
int f1()
{
static int ar[5][6];
………
f2(ar,5,6);
………
}
void f2(received_arg,dim1,dim2)
int **received_arg;
int dim1,dim2;
{
………
}
È un puntatore a un puntatore a int
Il vantaggio di questo approccio è la flessibilità: non occorre
conoscere a priori la dimensione degli elementi dell’array; occorre
però
calcolare
manualmente
l’aritmetica
degli
indici:
received_arg[x][y] è memorizzato all’indirizzo
received_arg  xdim2  y
Fondamenti di Informatica I  a.a. 2008-09
9
Esempio array multidimensionali  1
Scrivere una funzione che determina il tipo del risultato di
un’espressione binaria in base ai tipi degli operandi
include <stdio.h>
typedef enum {SPECIAL2, ILLEGAL, INT, FLOAT,
DOUBLE, POINTER, LAST} TYPES;
TYPES type_needed(type1,type2)
TYPES type1, type2;
{
static TYPES result_type[LAST][LAST]  {
/*
int
/*int*/
INT,
/*float*/
FLOAT,
/*double*/ DOUBLE,
/*pointer*/ POINTER,
};
TYPES result 
float
double
FLOAT,
DOUBLE,
FLOAT,
DOUBLE,
DOUBLE, DOUBLE,
ILLEGAL,
ILLEGAL,
pointer
POINTER,
ILLEGAL,
ILLEGAL,
SPECIAL
*/
La funzione riceve
due argomenti interi
che rappresentano i
tipi degli operandi e
fornisce un intero che
rappresenta il tipo
del risultato
result_type[type1][type2];
if (result  ILLEGAL)
printf(“Operazione scorretta su puntatori\n”);
return result;
Fondamenti
di Informatica I  a.a. 2008-09
}
10
Esempio array multidimensionali  2
La parte principale del programma è costituita dalla
dichiarazione ed inizializzazione dell’array result_type:
Ogni tipo di dati viene fatto corrispondere ad un valore intero
per mezzo della dichiarazione enum
In base alle modalità di definizione dell’array bidimensionale, i
due valori di ingresso sono indici, che individuano
univocamente l’elemento dell’array corrispondente al tipo del
risultato
La dichiarazione enum assicura che ogni costante venga
associata ad un unico valore intero e che LAST rappresenti il
numero totale di tipi (viene usato nella dichiarazione
dell’array)
Fondamenti di Informatica I  a.a. 2008-09
11
Esempio array multidimensionali  3
La stessa dichiarazione (almeno per il compilatore) avrebbe
potuto essere scritta per mezzo di interi
char result_type[4][4]  {0,1,2,3,1,1,2,1,2,2,2,1,3,1,1,2};
diminuendo sensibilmente la comprensibilità e la mantenibilità
del programma
Nel caso SPECIAL, l’operazione è corretta solo se i puntatori
riferiscono oggetti dello stesso tipo e l’operatore è il segno
meno: il risultato è un int
Fondamenti di Informatica I  a.a. 2008-09
12
Errori di accesso ad
array multidimensionali
ar[1,2]  0; /* Lecito, ma probabilmente scorretto */
ar[1][2]  0; /* Corretto */
Nella prima istruzione…
…la virgola viene interpretata come operatore, si valuta quindi
espressione1, che vale 1 ed il cui risultato non viene utilizzato,
e, successivamente, espressione2, che vale 2 (le due espressioni
sono costanti)
…si ottiene l’accesso ad ar[2]
Se ar è un array bidimensionale di int, ar[2] è un puntatore
ad int (costante)
 Viene segnalato un errore di incompatibilità di tipo:
fuorviante dato che la causa dell’errore è l’uso della virgola
Fondamenti di Informatica I  a.a. 2008-09
13
Gli array di puntatori  1
Si consideri la dichiarazione:
char *ar_of_p[5];
La variabile ar_of_p è un array di cinque elementi di tipo
puntatore e non un puntatore ad un array di cinque
elementi
L’operatore di accesso all’elemento di un array “[]” ha
precedenza superiore all’operatore di accesso all’indirizzo
contenuto in un puntatore
I puntatori non sono stati inizializzati, per cui puntano a
posizioni di memoria qualsiasi
Fondamenti di Informatica I  a.a. 2008-09
14
Gli array di puntatori  2
996
Esempio:
char *ar_of_p[5];
char c0 = ‘a’;
char c1 = ‘b’;
ar_of_p[0] = &c0;
ar_of_p[1] = &c1;
ar_of_p[0]
2000
1000
ar_of_p[1]
2001
1004
ar_of_p[2]
non definito
1008
ar_of_p[3]
non definito
100C
ar_of_p[4]
non definito
1010
1014
1FFF
c0
a
2000
c1
b
2001
2002
Fondamenti di Informatica I  a.a. 2008-09
15
Esempio array di puntatori  1
Gli array di puntatori vengono usati per gestire array di stringhe
Esempio: Realizzare una funzione che, dato un intero compreso fra
1 e 12, in ingresso, stampa il nome del mese corrispondente
include <stdio.h>
include <stdlib.h>
char *month_text(m)
int m;
{
static char *month[13]  {“Badmonth”, “January”, “February”, “March”, “April”, “May”,
“June”, “July”, “August”, “September”, “October”,
“November”, “December”
};
if (m>12)
{
printf(“Valore scorretto”);
exit(1);
}
return month[m];
}
Fondamenti di Informatica I  a.a. 2008-09
16
Esempio array di puntatori  2
La variabile month è un array di puntatori a char costituito da
13 elementi: come conseguenza dell’inizializzazione, ogni
puntatore fa riferimento all’elemento iniziale di una stringa
La motivazione dell’uso di un puntatore aggiuntivo con un
valore inutile consiste nel non voler effettuare sottrazioni
dall’indice: è comune non utilizzare l’elemento iniziale di un
array quando il valore dell’indice comincia logicamente da 1
Non definendo “Badmonth”, si dovrebbe cambiare l’istruzione
di ritorno al chiamante in
return month[m1];
Fondamenti di Informatica I  a.a. 2008-09
17
Esempio array di puntatori  3
Nota:
I caratteri che costituiscono
una stringa devono essere
consecutivi
Le stringhe corrispondenti ai
nomi dei mesi vengono
memorizzate dal compilatore
in qualunque posizione libera
della memoria
2000
‘F’
2011
‘a’
2001
‘e’
2012
‘d’
2002
‘b’
2013
‘m’
2003
‘r’
2014
‘o’
2004
‘u’
2015
‘n’
2005
‘a’
2016
month[0]
2000
1000
month[1]
2009
1004
month[2]
2011
1008
month[3]
2500
100C
month[4]
2800
1010
‘t’
2006
‘r’
2017
month[5]
3000
1014
‘h’
2007
‘y’
2018
month[6]
3006
1018
‘\0’
2008
‘\0’
2019
month[7]
300A
101C
‘J’
2009
month[8]
300F
1020
‘a’
200A
‘M’
2500
month[9]
4000
1024
‘n’
200B
‘a’
2501
month[10]
400A
1028
‘u’
200C
‘r’
2502
‘a’
200D
‘c’
4011
102C
2503
‘r’
200E
‘h’
2504
401A
1030
‘y’
200F
‘\0’
2505
‘\0’
2010
month[11]
month[12]
Fondamenti di Informatica I  a.a. 2008-09
‘B’
18
I puntatori a puntatori  1
I puntatori a puntatori sono costrutti usati in programmi
sofisticati: per dichiarare un puntatore a puntatore occorre
far precedere il nome della variabile da due asterischi
consecutivi
int **p;
dichiara p come puntatore ad un puntatore ad int
Per accedere al valore dell’int, è necessario utilizzare i doppi
asterischi:
j  **p;
assegna un intero a j
Fondamenti di Informatica I  a.a. 2008-09
19
I puntatori a puntatori  2
4 byte
Esempio:
int r  5;
int *q  &r;
int **p  &q;
r
5
99C
q
99C
1004
p
1004
100C
È possibile assegnare valori ad r come:
r  10;
/* Assegnamento diretto */
*q  10; /* Assegnamento con un livello di indirezione */
**p  10; /* Assegnamento con due livelli di indirezione */
Fondamenti di Informatica I  a.a. 2008-09
20
Esempio: Prodotto matricevettore
/*
**
**
**
**
*/
Funzione per il calcolo di Ab, con…
A: matrice mn di float (in input)
b: vettore di dimensione n di float (in input)
m, n: interi, numero di righe e colonne di A (in input)
x: vettore di float di dimensione m, risultato (in output)
float *prod_mv(a, b, m, n, x)
float a[][100], b[], x[]; /* ma, nella funzione, sono puntatori */
int m, n;
{
int i, j;
for (i0; im; i)
{
x[i]  0.0;
for (j0; jn; j)
x[i]  a[i][j]b[j];
}
return x;
}
Fondamenti
di Informatica I  a.a. 2008-09
21
Le classi di memorizzazione
Fondamenti di Informatica I  a.a. 2008-09
22
Introduzione  1
Nel linguaggio C, viene offerta la possibilità di condividere
variabili e di delimitare le porzioni di codice che sfruttano
tali condivisioni, mediante la definizione dell’ambito di
visibilità, o scope, delle variabili
L’ambito di visibilità è il termine tecnico che denota la parte
del testo sorgente C in cui è attiva la dichiarazione di un
nome
Inoltre, le variabili hanno una durata, che descrive il lasso
temporale di memorizzazione dei valori di una variabile:
Nel caso di variabili con durata fissa, i valori memorizzati
vengono mantenuti anche all’esterno dell’ambito di visibilità
Le proprietà di visibilità e di durata individuano la classe di
memorizzazione di una variabile
Fondamenti di Informatica I  a.a. 2008-09
23
Introduzione  2
Esempio
void func()
{
int j;
static int ar[]  {1,2,3,4}
………
}
Le variabili j ed ar hanno entrambe
visibilità a livello di blocco, perché
definite all’interno di un blocco:
possono essere referenziate solo
dalle istruzioni che appartengono al
blocco (il corpo della funzione func)
Le variabili j ed ar sono dette locali
La variabile j ha durata automatica, mentre ar ha durata fissa,
perché dichiarata static:
A j viene automaticamente allocata memoria ogni volta che viene
eseguito il blocco che la contiene (può avere indirizzi diversi per
esecuzioni diverse del blocco di codice)
ar viene allocato la prima volta che viene eseguito il blocco e mantiene
l’indirizzo originale per l’intera esecuzione del programma
Fondamenti di Informatica I  a.a. 2008-09
24
Durata fissa e durata automatica
Le variabili con durata fissa sono permanenti, mentre le
variabili con durata automatica sono allocate più volte
durante l’esecuzione del programma
Ad una variabile fissa viene associata una locazione di memoria
all’inizio del programma, che non cambia fino al termine dello
stesso
Ad una variabile automatica viene allocata memoria ogni volta
che si entra nel suo ambito di visibilità; se il codice che
appartiene all’ambito di visibilità della variabile viene rieseguito,
la variabile viene generalmente allocata altrove: non si
mantiene il valore della variabile fra due esecuzioni successive
Le variabili locali sono automatiche per default, ma possono
essere rese fisse se dichiarate static
La parola chiave auto definisce esplicitamente una variabile
automatica, ma è usata raramente perché ridondante
Fondamenti di Informatica I  a.a. 2008-09
25
L’inizializzazione delle variabili  1
Le variabili fisse vengono inizializzate una sola volta, mentre
quelle automatiche vengono inizializzate ogni volta che viene
eseguito il blocco che le contiene
void increment()
{
int j1;
static int k1;
j;
k;
printf(“j: %d\t k: %d\n”, j, k);
}
main()
{
increment();
increment();
increment();
La funzione increment() incrementa i
valori delle variabili j e k, entrambe
inizializzate a 1
Il
risultato
dell’esecuzione
del
programma è:
j: 2
j: 2
j: 2
k: 2
k: 3
k: 4
}
Fondamenti di Informatica I  a.a. 2008-09
26
L’inizializzazione delle variabili  2
Le variabili fisse vengono inizializzate di default al valore
zero, quelle automatiche non vengono inizializzate (e sono
normalmente allocate sullo stack)
Le variabili scalari automatiche possono essere inizializzate
con una qualunque espressione, con il solo vincolo che tutte
le variabili contenute nell’espressione siano state dichiarate
(e inizializzate) in precedenza
Le variabili con durata fissa possono essere inizializzate solo
per mezzo di espressioni costanti, ossia non contenenti nomi
di variabili
Fondamenti di Informatica I  a.a. 2008-09
27
L’uso di variabili con durata fissa
Le variabili con durata fissa sono comunemente impiegate per
tenere traccia del numero di volte che una funzione viene eseguita
e per modificarne il comportamento ad intervalli regolari
#define ODD 0
#define EVEN 1
void print_header(chap_title)
char *chap_title;
{
static char page_typeODD;
if (page_type  ODD)
{
printf(“\t\t\t\t\t%s\n\n”, chap_title);
page_type  EVEN;
}
else
{
printf(“%s\n\n”, chap_title);
page_type  ODD;
}
La variabile page_type agisce da
elemento di controllo: quando il
numero della pagina è dispari, la
funzione stampa
la stringa
puntata da chap_title sul lato
destro della pagina; se il numero
della pagina è pari, la stringa
appare spostata a sinistra
page_type deve essere fissa,
altrimenti si stamperebbe sempre
l’intestazione relativa alle pagine
dispari
}
Fondamenti di Informatica I  a.a. 2008-09
28
L’ambito di visibilità  1
L’ambito di visibilità di una variabile definisce la regione di
codice da cui è possibile accedere alla variabile
Esistono quattro tipi di ambiti di visibilità: programma, file,
funzione e blocco
L’ambito di visibilità a livello di programma implica che una
variabile è accessibile da tutti i file sorgente; le variabili con
ambito di visibilità a livello di programma sono variabili globali
L’ambito di visibilità a livello di file implica che una variabile è
accessibile dal punto in cui è dichiarata fino alla fine del file
sorgente in cui si trova
L’ambito di visibilità a livello di funzione implica che il nome di
una variabile è accessibile dall’inizio alla fine della funzione in
cui è dichiarata
L’ambito di visibilità a livello di blocco implica che una variabile
è accessibile dal punto in cui è dichiarata fino alla fine del
blocco in cui si trova
Fondamenti di Informatica I  a.a. 2008-09
29
L’ambito di visibilità  2
L’ambito di visibilità di una variabile è determinato dalla
posizione della dichiarazione:
Le variabili dichiarate all’interno
di un blocco hanno ambito di
visibilità a livello di blocco
Le variabili dichiarate all’esterno
di un blocco hanno ambito di
visibilità a livello di file se sono
static, oppure a livello di
programma
Solo le etichette dell’istruzione
goto hanno ambito di visibilità a
livello di funzione
Fondamenti di Informatica I  a.a. 2008-09
Ambito di visibilità a livello di programma
Ambito di visibilità a livello di file
Ambito di visibilità a livello di funzione
Ambito di visibilità a
livello di blocco
Le relazioni gerarchiche tra gli
ambiti di visibilità
30
L’ambito di visibilità  3
Esempio:
int i;
/* ambito di visibilità a livello di programma */
static int j; /* ambito di visibilità a livello di file */
int func(k) /* ambito di visibilità a livello di programma */
int k;
/* ambito di visibilità a livello di blocco */
{
int m; /* ambito di visibilità a livello di blocco */
start: ; /* ambito di visibilità a livello di funzione */
………
}
I parametri delle funzioni hanno ambito di visibilità a livello di
blocco: sono trattati come se fossero la prima dichiarazione di
variabile nel blocco di livello più alto contenuto nella funzione
Fondamenti di Informatica I  a.a. 2008-09
31
L’ambito di visibilità  4
Il C consente l’attribuzione dello stesso nome a variabili
diverse con ambiti di visibilità distinti
È anche possibile che variabili con lo stesso nome abbiano
ambiti parzialmente intersecati: la variabile con l’ambito di
visibilità più limitato preclude temporaneamente la visibilità
dell’altra
int j10;
/* ambito di visibilità a livello di programma */
main()
{
int j;
/* ambito di visibilità a livello di blocco:
nasconde la variabile globale j */
for (j0; j<5; j)
printf(“j: %d”, j);
}
Fondamenti di Informatica I  a.a. 2008-09
L’esecuzione del programma
produce:
j: 0
j: 1
j: 2
j: 3
j: 4
La
variabile
globale
j
mantiene inalterato il valore
iniziale 10
32
L’ambito di visibilità
a livello di blocco e funzione
Una variabile con ambito di visibilità a livello di blocco non
può essere acceduta dall’esterno del blocco in cui è dichiarata
consente di proteggere la variabile da effetti collaterali non
desiderati
riduce la complessità del programma, rendendolo più leggibile e
mantenibile
Variabili possono essere dichiarate all’interno di blocchi
innestati, nascondendo temporaneamente le variabili con lo
stesso nome eventualmente definite in blocchi più esterni
Le etichette dell’istruzione goto sono gli unici nomi con
ambito di visibilità a livello di funzione: devono avere nomi
univoci all’interno della stessa funzione, mentre nomi
coincidenti in funzioni diverse non originano conflitti
Fondamenti di Informatica I  a.a. 2008-09
33
L’ambito di visibilità
a livello di file e programma
Associare un ambito di visibilità a livello di file a una variabile
significa renderla referenziabile nella parte rimanente del file
in cui è definita
Se il file contiene più funzioni, tutte le funzioni che seguono la
dichiarazione della variabile sono in grado di referenziarla
Per dichiarare una variabile con ambito di visibilità a livello di
file occorre inserire la dichiarazione al di fuori delle funzioni e
usare la parola chiave static
Le variabili con ambito di visibilità a livello di programma,
dette variabili globali, sono visibili in tutti i file sorgente
(compreso quello in cui vengono dichiarate)
Una variabile globale deve essere dichiarata al di fuori delle
funzioni e non usando la parola chiave static
Fondamenti di Informatica I  a.a. 2008-09
34
I due significati di static
All’interno di un blocco, static attribuisce ad una variabile
durata fissa, anziché automatica
All’esterno di una funzione, static non è correlata alla durata
della variabile, ma ne controlla l’ambito di visibilità a livello
di file, anziché di programma
 La parola chiave static specifica sia l’ambito di visibilità che
la durata di una variabile:
All’interno di un blocco, le regole di visibilità del blocco sono
più stringenti di quelle a livello di file: la durata fissa è l’unico
effetto che si manifesta
All’esterno di una funzione, la durata è già fissa: l’ambito di
visibilità a livello di file è l’unico effetto che si manifesta
Fondamenti di Informatica I  a.a. 2008-09
35
Le variabili globali
L’uso delle variabili globali dovrebbe essere evitato perché aumenta
la complessità dei programmi e li rende difficilmente mantenibili
Le variabili globali possono portare a conflitti tra moduli se, per
errore, vengono scelti nomi uguali per variabili globali distinte
Quando occorre condividere dati tra procedure diverse, è buona
regola passare i dati come parametri o passare puntatori all’area di
memoria condivisa
Le regole di attribuzione di nomi alle variabili globali sono diverse: i
nomi globali devono essere riconoscibili non solo al compilatore, ma
anche al linker ed al binder : lo standard ANSI garantisce il
riconoscimento dei primi sei caratteri (tuttavia, nulla vieta di
aggiungere caratteri per rendere i nomi più significativi)
Fondamenti di Informatica I  a.a. 2008-09
36
Definizioni e allusioni  1
Finora, la dichiarazione di variabile corrispondeva
all’allocazione di memoria per quella variabile: l’allocazione di
memoria è, in realtà, il risultato di un solo tipo di
dichiarazione, detta definizione
Le variabili globali consentono un secondo tipo di
dichiarazione, detta allusione: non si alloca memoria, ma si
informa il compilatore che esiste una variabile del tipo
specificato definita altrove
main()
{
extern int f();
/* allusione a funzione */
extern int j;
/* allusione a variabile */
extern float f_array_of_f[]; /* allusione a variabile */
………
Le variabili globali seguono le stesse regole delle funzioni:
ogni volta che si utilizzano variabili definite in altro file, è
necessario dichiararle con allusioni
Fondamenti di Informatica I  a.a. 2008-09
37
Definizioni e allusioni  2
La parola chiave extern specifica che la variabile è definita
altrove
Le allusioni consentono al compilatore di effettuare i controlli
di tipo: per ogni variabile globale, possono essere presenti un
numero qualunque di allusioni, ma una sola definizione
Lo standard ANSI prevede che, per definire una variabile
globale, sia necessario inserire all’esterno di una funzione una
dichiarazione con inizializzazione: la presenza o meno della
parola chiave extern non ha alcun effetto
Se il valore iniziale fosse stato omesso, il compilatore avrebbe
prodotto un’allusione, se è specificato extern, o una
definizione di prova, che si trasforma…
…in una definizione effettiva, con inizializzazione a 0, se nel file
non compare altra dichiarazione
…in un’allusione, se nel file sorgente è contenuta una
definizione reale
Fondamenti di Informatica I  a.a. 2008-09
38
Lo specificatore register
La parola chiave register consente di suggerire al compilatore quali
variabili dovrebbero essere memorizzate nei registri
Il livello di supporto offerto dai compilatori allo specificatore
register è molto variabile: alcuni compilatori memorizzano
effettivamente tutte le variabili register in registri, fino a quando ce
ne sono disponibili, altri lo ignorano, altri lo interpretano per
determinare se è davvero proficuo memorizzare una data variabile
in un registro
Ad una variabile register non è assegnato alcun indirizzo di
memoria: anche se il suggerimento register non viene seguito dal
compilatore, se si tenta di accedere all’indirizzo della variabile, si
ottiene una segnalazione di errore
Sono candidati ideali per la memorizzazione register i contatori dei
cicli for, sui quali vengono effettuate molte operazioni,
temporalmente vicine
Fondamenti di Informatica I  a.a. 2008-09
39
Il modificatore di classe const
La parola chiave const (derivata dal C) indica che una
variabile non può essere modificata dopo l’inizializzazione:
const char str[9]  “Costante”;
str[0]  ‘a’; /* non ammesso */
La parola chiave const può essere impiegata in alternativa
alla direttiva #define
Fondamenti di Informatica I  a.a. 2008-09
40
Riepilogo delle
classi di memorizzazione
La semantica degli specificatori di classe di memorizzazione
Posizione della dichiarazione
Specificatore di classe
di memorizzazione
All’esterno di una
funzione
All’interno di una
funzione
Argomenti di una
funzione
auto o register
NON PERMESSO
Visibilità: blocco
Durata: automatica
Visibilità: blocco
Durata: automatica
static
Visibilità: file
Durata: fissa
Visibilità: blocco
Durata: fissa
NON PERMESSO
extern
Visibilità: programma
Durata: fissa
Visibilità: blocco
Durata: fissa
NON PERMESSO
nessuno specificatore
di classe
Visibilità: programma
Durata: fissa
Visibilità: blocco
Durata: automatica
Visibilità: blocco
Durata: automatica
Fondamenti di Informatica I  a.a. 2008-09
41
L’allocazione dinamica
della memoria  1
Alle variabili con durata fissa viene riservata memoria per l’intera
durata del programma, mentre alle variabili con durata automatica
la memoria viene allocata ogni volta che si esegue il blocco
relativo: in entrambi i casi si suppone di conoscere la quantità di
memoria da allocare nel momento i cui si scrive il codice sorgente
Tuttavia, talvolta l’occupazione di memoria dipende strettamente
dai dati in ingresso
In C, esistono quattro funzioni della libreria di runtime (stdlib.h)
che permettono l’allocazione dinamica della memoria
malloc()  alloca un numero specificato di byte in memoria e
restituisce un puntatore all’inizio del blocco allocato
calloc()  come malloc(), ma inizializza a zero i byte allocati; consente
di allocare la memoria per più di un oggetto alla volta
realloc()  cambia la dimensione di un blocco precedentemente
allocato
free()  libera la memoria che era stata allocata con malloc(), calloc()
o realloc()
Fondamenti di Informatica I  a.a. 2008-09
42
L’allocazione dinamica
della memoria  2
Esempio:
include <stdio.h>
include <stdlib.h>
main()
{
extern void bubble_sort();
int *list, j, sort_num;
printf(“Numero dei valori da introdurre:”);
scanf(“%d”, &sort_num);
list  (int *) malloc(sort_numsizeof(int));
for (j0; j<sort_num; j)
scanf(“%d”, listj);
bubble_sort(list, sort_num);
exit(0);
L’argomento di malloc() è la dimensione in
byte del blocco da allocare
Usando calloc(), l’istruzione di allocazione
della memoria sarebbe
list = (int *) calloc(sort_num, sizeof(int));
La funzione calloc() accetta due argomenti:
il primo è il numero di oggetti a cui
riservare memoria, il secondo è la
dimensione di ciascun oggetto
Le funzioni malloc() e calloc() memorizzano
gli elementi in modo contiguo in un singolo
blocco
}
Fondamenti di Informatica I  a.a. 2008-09
43
Scarica

Linguaggio C: utilizzo avanzato di array e puntatori