IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

FAQ C et erreurs courantes

Ce ou ces sujets font partie d'une FAQ en cours d'écriture, vos retours sont les bienvenus.

Il est de coutume d'utiliser l'opérateur sizeof pour retrouver la taille d'un tableau dont la taille est fixe et connue à la compilation :

N'oubliez surtout pas de diviser par la taille d'un élément ou vous obtiendrez une trop grande taille qui conduira à des dépassements.

Par exemple pour le tableau int tableau [] = {1, 2, 3, 4, 5};, l'instruction suivante :

 
Sélectionnez
sizeof(tableau)

vous retournera la taille totale du tableau en mémoire = le nombre d'éléments du tableau * la taille de chaque élément = 5 * sizeof(int) qui pour un int de 32 bits (= 4 bytes) sera égal à 5 * 4 = 20 au lieu de 5 !!!

Nous divisons par conséquent ce terme par sizeof(*tableau) ou sizeof(tableau[0]) tous deux égaux à sizeof(int).

 
Sélectionnez
int tableau [] = {1, 2, 3, 4, 5};
size_t taille = sizeof(tableau) / sizeof(*tableau); // = 5 
/*ou*/ taille = sizeof(tableau) / sizeof(tableau[0]);

char nom [] = "Jean"; // {'J', 'e', 'a', 'n', '\0'}
taille = sizeof(nom) / sizeof(*nom); // = 5
taille = sizeof(nom); // = 5, sizeof(char) = 1, on peut donc l'omettre

char const *noms[] = {"Jean", "Martin", "Alexandre", "Jack"};
taille = sizeof(noms) / sizeof(*noms); // = 4

Vous pouvez, pour faciliter la lecture et l'écriture de votre code, définir une macro :

 
Sélectionnez
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

utilisée comme suit :

 
Sélectionnez
int tableau [] = {1, 2, 3, 4, 5};
size_t taille = ARRAY_SIZE(tableau); // = 5 

float tab[ARRAY_SIZE(tableau)]; // tableau de même taille 

char const * noms[] = {"Jean", "Martin", "Alexandre", "Jack"};
taille = ARRAY_SIZE(noms); // = 4

Mais ceci ne fonctionne que sur des tableaux statiques, dont la taille est connue à la compilation, et absolument pas sur des pointeurs. En effet, quelque soit le pointeur utilisé, sizeof(un_pointeur) ne vous retournera jamais que 4 ou 8 (bytes) selon que vous soyez sur une architecture 32 ou 64 bits.

 
Sélectionnez
int *tab = malloc(20 * sizeof(*tab));

Vous ne pourrez JAMAIS obtenir ce 20 avec sizeof, il vous faut conserver cette taille dans une autre variable. Quel que soit le nombre d'éléments alloué, on aura toujours :

 
Sélectionnez
sizeof(tab)  == sizeof(int *)
sizeof(*tab) == sizeof(int)
 
Sélectionnez
char const *str = "Hello World!";

Vous ne pourrez JAMAIS obtenir la longueur de cette chaîne avec sizeof, il vous faut utiliser la fonction strlen (et encore, seulement lorsque la mémoire est initialisée et contient une chaîne terminée par un '\0').

Je vous renvoie au sujet La mémoire et les pointeurs pour mieux comprendre, ce faisant vous obtiendrez systématiquement la taille de la zone bleu et non celle en rouge.

Lorsque vous passez un tableau en paramètre d'une fonction, celui-ci est implicitement converti en pointeur (« decay » en anglais) et seule l'adresse du premier élément est passée. Ainsi, pour chaque catégorie toutes ces écritures sont équivalentes :

Tableau 1D de int
Sélectionnez
void fonction(int tab1D[10]);
void fonction(int tab1D[]);
void fonction(int *tab1D);
Autrement dit : un int [10] est implicitement convertible vers un pointeur int *.
Tableau 2D de int
Sélectionnez
void fonction(int tab2D[10][20]);
void fonction(int tab2D[][20]);
void fonction(int (*tab2D)[20]);
Autrement dit : un int [10][20] est implicitement convertible vers un pointeur int (*)[20].

Mais PAS vers un int ** !!! Cf. ce sujet.
Tableau 1D de pointeurs sur int
Sélectionnez
void fonction(int * tab1Dp[10]);
void fonction(int * tab1Dp[]);
void fonction(int **tab1Dp);
Autrement dit : un int * [10] est implicitement convertible vers un pointeur int **.
Tableau 2D de pointeurs sur int
Sélectionnez
void fonction(int * tab2Dp[10][20]);
void fonction(int * tab2Dp[][20]);
void fonction(int * (*tab2Dp)[20]);
Autrement dit : un int * [10][20] est implicitement convertible vers un pointeur int * (*)[20].

Et peu importe la forme que vous utilisez, ce sont ces pointeurs que vous avez en entrée, pas des tableaux.
Ainsi, même si dans chaque première ligne vous mentionnez la taille (ici 10) de la première dimension, vous ne pouvez pas la récupérer à l'aide de sizeof. Il vous faudra obligatoirement passer cette taille en paramètre, ou utiliser une constante #define.

Et vu qu'il s'agit de pointeurs, le compilateur ne pourra pas vérifier l'exactitude de la première dimension du tableau passé. Il ne pourra vérifier que les dimensions suivantes :

 
Sélectionnez
void fonction(int tab2D[10][20]);
 
Sélectionnez
int tab1[5][20];
fonction(tab1); // Pas d'avertissement du compilateur,
                // mais probablement un UB dû à un accès hors limite

int tab2[10][5];
fonction(tab2); // Avertissement du compilateur
Créé le 13 décembre 2017  par Winjerome

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2022 Winjerome. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.