C/Vettori e puntatori/Vettori

Indice del libro

In linguaggio C gli array, o vettori, sono strutture di dati che contengono una serie di elementi. Ogni elemento di un array va considerato al pari di una variabile e contiene, quindi, un unico dato.

Caratteristiche modifica

In C, a differenza di alcuni linguaggi di programmazione ad alto livello, ogni array è caratterizzato da un unico tipo di dati e da un numero fisso di elementi (definito al momento della creazione dell'array).

Ad esempio, è possibile definire un array di interi (int) costituito da 20 elementi. Tale array non potrà contenere dati che non siano interi né potrà contenere più di 20 elementi. (vedi la sezione Buffer overflow)

Ogni elemento dell'array è identificato da un indice, cioè un numero intero che rappresenta la posizione di quell'elemento nell'array. Gli indici possibili vanno da 0 al numero di elementi dell'array diminuito di uno. Quindi il primo elemento sarà identificato dall'indice 0, il secondo dall'indice 1, il terzo dall'indice 2 e così via. In C non è possibile associare a un elemento di un array un indice che non sia un numero intero (cioè non è possibile creare array associativi).

Sintassi modifica

Per dichiarare un array (allocato nella memoria stack) in C si usa questa sintassi:

tipo nome[numero di elementi];

Per esempio il codice

 int mioarray[10];

definisce un array chiamato mioarray e composto da 10 elementi di tipo intero (int).

Per costruire un analogo array allocato dinamicamente nella memoria heap in C si deve operare così:

int *mioarray = (int *) malloc(sizeof(int) * dim);

dove dim può essere conosciuto anche a runtime.

Per accedere ad un elemento di un qualsiasi array si usa questa sintassi:

nome[indice]

Per esempio il codice

 mioarray[4] = 1;

modifica il 5º elemento dell'array mioarray inserendovi il valore 1.

Si può anche inizializzare alla dichiarazione:

 int arr[3]={1,2,3};

che produrrebbe un vettore come il seguente:

arr[0] 1
arr[1] 2
arr[2] 3


Per creare vettori multidimensionali si scrive questo:

 int arr[3][3];

Quest'istruzione produrrebbe un vettore come questo:

0 1 2
0 arr[0][0] arr[0][1] arr[0][2]
1 arr[1][0] arr[1][1] arr[1][2]
2 arr[2][0] arr[2][1] arr[2][2]

in generale un vettore n-dimensionale si dichiara:

tipo Nome_vettore[i1]...[iN];

dove i1 ... iN sono le dimensioni di ciascuna dimensione, che equivale al vettore monodimensionale:

tipo Nome_vettore[i1×...×iN];

ossia al vettore di dimensioni i1×...×iN.

Inizializzazione vettore multidimensionale modifica

Un vettore multidimensionale viene inizializzato con la seguente sintassi:

tipo V[i1]...[iN] = {
                     {...
                      {}n1,...,{}ni(N-1)
                     }21,...,{}2i1
                    }1;

dove con {}n si è indicato la coppia di graffe al cui interno ci sono gli inizializzatori relativi alla dimenione n. Per comprendere meglio ecco un esempio:

int V[2][2] = {
               {1,2},
               {3,4}
              };
int V[2][2][2] = {
                  {
                   {1,2},
                   {3,4}
                  },
                  {
                   {5,6},
                   {7,8}
                  },
                 };

Se gli ultimi inizializzatori sono saltati allora si sottintende 0; può essere usata una sintassi semplificata considerandolo come un vettore monodimensionale:

tipo v[i1]...[iN] = {elenco di tutti gli i1×i2×...×iN inizializzatori};

nel caso di una matrice di interi:

int M[2][3] = {1,2,3,
               4,5,6};

saltando gli ultimi inizializzatori si sottintende ossia:

int M[3][3] = {0,0,1,
               0,1,0,
               1};

che definisce la matrice :

0 0 1
0 1 0
1 0 0

Sfruttando questa caratteristica è possibile inizializzare a 0 tutti gli elementi di un vettore n-dimensionale con la seguente sintassi:

tipo V[i1]...[iN] = {0};

Vettori dinamici modifica

L'ultimo standard ISO (C99) dà la possibilità di dichiarare vettori di lunghezza variabile ossia la dimensione del vettore è una variabile. Ecco un esempio:

// C99, infatti questo commento è permesso in C99.

#include <stdio.h>

int main(void){
    int lun;

    scanf ("%d",&lun); // lettura di lun
    
    int Array[lun];    // dichiarazione all'interno del codice. C99.
// return 0 implicito, caratteristica C99.
}

Assegnamento modifica

È possibile assegnare un valore ad un elemento di un vettore. Si specifica l'indice tra parentesi quadre:

/* C89. Compatibile con vecchi compilatori. */
int main (void)
{
    int arr[3];

    arr[0] = 1;
    arr[1] = 2;
    arr[2] = 3;
    return 0;
}

Accesso modifica

Si accede ad un elemento di un vettore specificando il suo indice tra parentesi quadre:

 
/* C89. */
#include <stdio.h>

int main (void){
    int i, j, arr[3] = {7,8,9}, M[2][3] = {1,2,3,
                                           4,5,6};

    for(i=0; i<3; i++)
        printf("arr[%d] = %d ", i, arr[i]);
    printf("\nM =\n");
    for (i=0; i<2; i++){
        for (j=0; j<3; j++)
            printf("%d ",M[i][j]);
        printf("\n");
    }
 return 0;
}

Il risultato dell'esecuzione del programma è:

arr[0] = 7 arr[1] = 8 arr[2] = 9
M =
1 2 3
4 5 6

Array e puntatori modifica

In C c'è un legame molto stretto fra array e puntatori. Infatti il nome associato a un array rappresenta un puntatore al primo elemento dell'array.

Osservare il codice seguente:

int a[10];
int *p;
 
p = a; /* assegna al puntatore p l'indirizzo del primo elemento di a */
 
a[0] = 1;
p[0] = 1;
*a = 1;
*p = 1;
/* tutte assegnano 1 al primo elemento dell'array */
 
a[4] = 1; 
p[4] = 1; 
*(a + 4) = 1;
*(p + 4) = 1;
/* tutte assegnano 1 al quinto elemento dell'array */

Non è però valido scrivere:

a = p;

in quanto a rappresenta l'indirizzo dell'array ed è una costante.

Buffer overflow modifica

Quando si accede ad una posizione dell'array oltre i suoi limiti (per esempio accedendo all'11º elemento in un array da 10 elementi), si verifica il buffer overflow, cioè vengono letti o scritti dei dati in un'area di memoria che non appartiene all'array. I linguaggi C e C++, infatti, non prevedono nessun tipo di controllo sulle dimensioni delle array in fase di esecuzione.

Spesso il buffer overflow è generato da dati scorretti ricevuti in input e porta all'interruzione dell'esecuzione del programma con un errore. Il buffer overflow, inoltre, può essere sfruttato dai cracker per eseguire operazioni non consentite su un sistema. Infatti, creando appositamente dei dati da inviare al programma, è possibile generare un buffer overflow e modificare il comportamento del programma a proprio favore.