Pointeurs et tableaux

Un pointeur peut être utilisé comme un tableau

Le programme suivant alloue sur le tas 3 cases contenant chacune un entier.

int* p = malloc(3*sizeof(*p));
p[0] = 1
p[1] = 3
p[2] = 35

p[0] et *p sont synonymes.

  • Que pensez-vous de ce programme ?
int* p = malloc(3*sizeof(*p));
p[0] = 1
p[1] = 3
p[2] = 35
p[10] = 5

malloc c'est acheter une zone mémoire. Là, avec p[10] = 5, on écrit là où on a pas acheté.

  • Qu'est ce qui est moche dans int* p = malloc(3*sizeof(*p)); ?

Le fait d'avoir 3 qui est volant. Si c'est une constante, on peut faire par exemple :

#define NB_POTATOES 3
int* p = malloc(NB_POTATOES*sizeof(*p));

Un tableau est quasiment comme un pointeur

Un tableau statique ou un tableau déclaré dans une fonction est considéré comme un tableau : il a une taille et sizeof(tableau) renvoie le nombre d'octets utilisés pour stocker tout le tableau.

Mais si on passe un tableau à une fonction, ça devient un pointeur. Les signatures suivantes sont équivalentes :

    void f(char* p)
    void f(char p[])
    void f(char p[3])

Dans f, sizeof(p) renvoie 8 octets (64 bits).

Exercice : tableau dynamique

realloc permet de réallouer une zone mémoire. Voir TD.

Arithmétique des pointeurs

Addition d'un pointeur et d'un entier

Considérons le pointeur p tableau de 3 int :

avec

     p++

on avance de 1 case int dans le tableau (i.e. de 4 octets dans la plage mémoire) :

De la même manière, on peut faire p += 5 et on avance de 5 cases du tableau (5 fois 4 octets).

Différence de pointeurs

    int* p;
    int* q;
    ...
    int nbCasesTableauEntrePetQ = q - p;

Sur le dessin, q - p vaut 2 (il s'agit de 2 cases int).

La différence de pointeurs est un nombre relatif. Dans l'exemple p - q vaut -2.

Marrant !

int a[3] = {0, 1, 42};

Que vaut a[2] ? 42

Que vaut 2 [a] ? 42 aussi mais on ne va pas arrêter le module pour autant.

En fait a[i] est une macro pour *(a+i).

Pointeurs vers un tableau 2D

Prenons l'exemple d'un tableau 2D d'entiers. Une façon de faire est de considérer un pointeur de pointeurs vers des entiers :

int **p;

Exercice à faire en TD

  • Implémenter une fonction qui alloue un tableau 2D de taille n x n.

Un tableau 1D énorme qui simule un tableau 2D

typedef char* array2d_t;

array2d_t array2d_create(size_t sizeX, size_t sizeY) {
    char *array = malloc(SIZEX * SIZEY * sizeof(char));
    return array;
}

void array2d_free(array2d_t A) {
    free(A);
}

char array2d_get(array2d_t A, size_t x, size_t y) {
    return A[x*SIZEY + y];
}

void array2d_free(array2d_t A) {
    free(A);
}

Un tableau 1D de pointeurs vers des tableaux 1D

typedef char** array2d_t;

array2d_t array2d_create(size_t sizeX, size_t sizeY) {
    char **array = malloc(SIZEX * sizeof(char *));
    for (int i = 0; i < SIZEX; i++) 
        array[i] = malloc(SIZEY * sizeof(char *));
    return array;
}

void array2d_free(array2d_t A) {
    for (int i = 0; i < SIZEX; i++) 
        free(array[i]);
    free(A);
}

/*
array2d_t A = array2d_create(64, 32);
... on utilise A[x][y]...
array2d_free(A);
*/

Un tableau 1D de pointeurs vers des endroits dans un seul tableau 1D

typedef char** array2d_t;

array2d_t array2d_create(size_t sizeX, size_t sizeY) {
    char **array = malloc(SIZEX * sizeof(char *));
    array[0] = malloc(SIZEX*SIZEY);
    for (int i = 1; i < SIZEX; i++)
        array[i] = array[0] + (SIZEY * i);
    return array;
}

void array2d_free(array2d_t A) {
    free(A[0]);
    free(A);
}

/*
array2d_t A = array2d_create(64, 32);
... on utilise A[x][y]...
array2d_free(A);
*/

Mot clé Const

En C, le mot clé const s'applique sur ce qu'il y a à sa gauche. Il veut dire "je suis une constante".

Pointeur vers un (ou des) caractères en lecture seule

    char const *  pointer1 = malloc(1);
    *pointer1 = 'a'; //non car le char est une constante
    pointer1 = malloc(1); //yes

Par convention, on met souvent le mot-clé const tout à gauche :

    const char *  pointer1 = malloc(1);
    *pointer1 = 'a'; //no
    pointer1 = malloc(1); //yes
    const char* p = malloc(4);
    p[0] = 'a'; // no
    p[1] = 'a'; // no
    p = malloc(4); // yes

Pointeur en lecture seule sur des caractères

Dans l'exemple suivant, le "je suis une constante" s'applique sur l'étoile, autrement dit sur le pointeur.

    char* const q = malloc(4);
    q[0] = 'a'; //yes
    q = NULL; // non car on ne peut pas modifier le pointeur

Pointeur en lecture seule sur des caractères en lecture seule

    const char* const r = malloc(4);
    r[0] = 'a'; //no
    r = NULL; //no

Réallocation

void * realloc( void * pointer, size_t memorySize );

Calloc

void* calloc( size_t num, size_t size );
int* A = calloc(NB_PATATES, sizeof int);

## Exercices

  • Implémenter un tableau dynamique