Tableaux, pointeurs et allocation dynamique

Formation

En Ligne

Prix sur demande

Appeler le centre

Avez-vous besoin d'un coach de formation?

Il vous aidera à comparer différents cours et à trouver la solution la plus abordable.

Description

  • Typologie

    Formation

  • Méthodologie

    En ligne

Grâce à cette formation vous pourrez acquérir les connaissances nécessaires qui vous permettrons d’ajouter des compétences à votre profil et obtenir de solides aptitude qui vous offriront de nombreuses opportunités professionnelles.

Questions / Réponses

Ajoutez votre question

Nos conseillers et autres utilisateurs pourront vous répondre

À qui souhaitez-vous addresser votre question?

Saisissez vos coordonnées pour recevoir une réponse

Nous ne publierons que votre nom et votre question

Les Avis

Les matières

  • Allocation

Le programme

Introduction du cours

Bien que les tableaux et les pointeurs soient souvent confondus, il s'agit de deux choses différentes.

Nous allons aborder ce sujet dans le but d'utiliser correctement chacun de ces types (pointeur et tableau) et de comprendre la différence entre ces deux notions.
Nous allons également voir certaines notions approfondies dans les manipulations des tableaux (les initialisations, les déclarations, les passages en paramètre pour les fonctions).

Sans plus tarder attaquons le vif du sujet en vous souhaitant une bonne lecture.

Conseils pour une bonne lecture :

  • Une bonne concentration.

  • Un projet "test" ouvert, pour coder au fur et à mesure que vous lisez.

  • Ne pas passer à l'étape suivante sans comprendre l'étape précédente.

  • Faites des exercices à chaque fin de partie du tutoriel.

  • Ne lisez pas le tutoriel en entier d'un seul coup.

  • Suspendez la lecture si vous vous sentez largués. Et reprenez la lecture plus tard.

Les pointeurs (rappel)Définition

A la déclaration d'une variable, un emplacement lui est accordé dans la mémoire. Cet emplacement possède une adresse. Cette adresse peut être stockée dans une variable de type pointeur.

Pour résumer un pointeur est une variable qui contient l'adresse mémoire d'une autre variable.

Déclaration

L'opérateur de déclaration de pointeur est l'astérisque '*', et il est caractérisé par le type de variable sur laquelle il va pointer.
Ainsi pour déclarer un pointeur on doit respecter la syntaxe suivante : type *nom_du_pointeur;

Si par exemple on souhaite déclarer un pointeur sur une variable de type int, on ferait comme ceci :

int * ptrint; Utilisations

La règle à retenir est la suivante :

  • Un pointeur doit toujours être initialisé avant utilisation.

Initialisation

Pourquoi il faut toujours initialiser les pointeurs ?

A la déclaration d'une variable quelconque, sa valeur ne peut pas être déterminée. Elle peut valoir "n'importe quoi". Les pointeurs étant des variables aussi, alors à la déclaration ils valent n'importe quoi :) . Cette valeur se réfère donc à un emplacement mémoire dont on ignore sa signification. Et qui ne nous est pas alloué, donc inutilisable.

Et comment initialiser un pointeur ?

L'initialisation peut avoir trois formes :

  • Avec la valeur NULL :

    int * ptrint = NULL;

    Le pointeur NULL souvent sous la forme (void*)0, est une macro déclarée dans stddef.h. Elle est utilisée pour des opérations exclusivement sur le type pointeur (affectation, comparaison...).

  • Avec l'adresse d'une de nos variables déclarées.

    int variable; int * ptrint = &variable;

    Rappelez-vous de l'opérateur '&', qui permet d'avoir l'adresse mémoire d'un objet. Il s'utilise toujours accompagné du nom de l'objet que l'on souhaite connaitre son adresse mémoire.
    Avec cette initialisation, on est sûr que l'espace mémoire que notre pointeur indique, nous est alloué.

  • En faisant une allocation dynamique (que nous allons voir plus loin dans ce tutoriel).

    int * ptrint = malloc(10);

Donc si vous déclarez un pointeur, ayez à l'esprit qu'il faudra l'initialiser tôt ou tard :) avec l'une des trois méthodes présentées ci-dessus, en fonction de ce que vous souhaitez en faire.
Autrement il y a de fortes chances pour que votre programme plante avec l'erreur "SEGFAULT".

Accéder à l'adresse pointée

L'astérisque '*', vous l'avez reconnue :) . C'est ce même opérateur qui est utilisé pour déclarer une variable de type pointeur et pour accéder à l'emplacement indiqué par notre pointeur.
Dans l'apprentissage de cette partie, les débutants confondent toujours quand est-ce qu'il faut utiliser l'étoile et quand est-ce qu'il ne faut pas l'utiliser.
Gardez à l'esprit que l'étoile, à la déclaration d'une variable de type pointeur, ne signifie pas qu'on accède à l'emplacement qu'il pointe, mais sert juste à dire au compilateur qu'il s'agit d'une déclaration de pointeur.
Mis à part ce cas là, toutes les autres utilisations de l'opérateur '*' suivi du nom d'un pointeur, signifient que c'est de l'emplacement pointé qu'il s'agit.

Exemple :

int * ptrint; //Déclaration du pointeur int variable; //Déclaration d'une variable ptrint = &variable; //Initialisation de notre pointeur *ptrint = 10; //On inscrit 10 à l'espace pointé par notre pointeur (en l'occurrence la variable). Argument de fonctions : passage par valeur

Lors de l'appel d'une fonction, on lui donne des valeurs sous forme de paramètres. Il existe donc le type de passages qu'on appelle par valeur, qui consiste à passer une copie de notre valeur à la fonction. Ainsi la fonction ne manipulera que cette copie de notre valeur, et tous les changements qui y seront apportés, ne seront pas pris en compte ailleurs.

Exemple :

void ma_fonction(int n) //Fonction appelée { n = 10; } int main (void) //Fonction appelante { int variable = 123; ma_fonction(variable); printf("La valeur de variable est %d\n",variable); return 0; }

Le résultat de ce code sera donc :

La valeur de variable est 123

Pourtant on a bien modifié la valeur envoyée à la fonction 'ma_fonction'. C'est ce qu'on appelle un passage par valeur :) .

Argument de fonctions : passage par adresse

Ce type de passage donnera un résultat différent de celui présenté précédemment. Il consiste à envoyer l'adresse mémoire de notre variable et non pas sa valeur. Ainsi on a accès à l'emplacement même de cette variable en mémoire. Donc les changements qu'on apportera à cet espace mémoire, seront des changements qu'on aura apportés à notre variable directement. Si je reprends le même exemple, ceci donnerait :

void ma_fonction(int * ptrn) { *ptrn = 10; //On inscrit la valeur 10 à l'emplacement mémoire indiqué par ptrn } int main (void) { int variable = 123; ma_fonction( &variable ); //On appelle la fonction printf("La valeur de variable est %d\n",variable); return 0; }

Et le résultat est :

La valeur de variable est 10

C'est ce qu'on appel un passage par adresse ;) .

Argument de fonctions : passage par référence

Le passage par référence tel que nous pouvons le voir dans d'autres langages (C++ par exemple) ne peut pas être réalisé en C; mais il peut être implémenté par utilisation du "passage par adresse".
Ce passage par adresse est un passage par valeur quelque part, car on transmet une copie de l'adresse de la variable à la fonction. Donc ne soyez pas choqué si on vous dit quelque part qu'il n'y a pas de passage par référence en C.

Les tableaux unidimensionnelsDéfinition

"Un tableau est une suite contigüe de données de même type dans la mémoire."

Beaucoup de gros mots dans cette phrase n'est-ce pas :) ?

Si nous les regardons de plus près :

Une suite contigüe :

Une suite contigüe signifie un ensemble d'éléments disposés les uns à la suite des autres sans être intercalés.

-Donnée1-

-Donnée2-

-Donnée3-

........

-DonnéeN-

Données de même type :

Ceci signifie que toutes les données qu'on va retrouver dans notre tableau vont être du même type :) .

Si nous prenons l'exemple ci-dessus, donnée1, donnée2 jusqu'à la donnéeN vont être exactement du même type.

Déclaration (syntaxe)

La déclaration d'un type tableau en C s'obtient par utilisation des crochets '[' ']', et en précisant le type de données qu'il y aura dedans ainsi que le nombre.

Le nombre de ces éléments sera appelé la taille de notre tableau, et leur type le type de notre tableau.

La déclaration sera donc sous la forme : typenom_du_tableau [ taille ];

Voici un exemple de déclaration d'un tableau de 10 entiers :

int tableau[10]; UtilisationInitialisation

Il existe certaines expressions réservées à l'initialisation lors de la déclaration d'un tableau.

int tableau[3]; tableau = ....;

Erreur très courante à l'utilisation des chaines de caractères.

Ces expressions sont :

  • L'utilisation des accolades {} :

    int tableau[4] = {15,2,14,23};

    Les cases de ce tableau qu'on vient de déclarer seront donc initialisées respectivement avec les valeurs 15, 2, 14 et 23. Vous remarquerez que le nombre de valeurs correspond à la taille de notre tableau (à savoir 4 éléments). Il est possible de n'initialiser qu'un certain nombre de cases dans ce tableau ainsi :

    int tableau[4] = {15,2};

    Ainsi nous aurons initialisé que les deux premières cases. Vous vous demandez peut être ce que deviennent les autres cases :) , elles sont toutes initialisées avec des 0 (zéros).

    En conséquence une initialisation à zéro de la première case uniquement, initialiserait tout notre tableau à zéro :

    int tableau[4] = {0};
  • L'utilisation des accolades {} avec un tableau dont la taille n'est pas spécifiée :

    int tableau[] = {15,2,14,23};

    Deux choses très importantes à noter dans ce type de déclarations :
    1_ Le tableau sera alloué pour contenir le nombre d'éléments présents entre les accolades (en l'occurrence 4).
    2_ Le tableau sera initialisé avec ces éléments.

    Par conséquent, il ne faut pas mélanger cette déclaration avec l'initialisation type {0} :

    int tableau[] = {0};

    Car cela ne va allouer qu'une seule case. Erreur très courante dans le cas des chaines de caractères.

    Que faire pour n'initialiser que certaines cases dans le tableau ?

    Seul le standard C99 permet de faire ce genre d'initialisations, contrairement au C90.

    int t[10] = {[indice]=5,6};

    indice ici doit être remplacé par une valeur constante indiquant l'indice qu'on souhaite initialiser avec la valeur 5, il est évidemment obligatoire que cet indice soit inférieur à la taille du tableau.

    Pour se familiariser avec ce type d'initialisations prenons quelques exemples :

    Exemple 1:

    int t[10] = {2,3,[4]=5,6,9};

    résultat :

    2

    3

    0

    0

    5

    6

    9

    0

    0

    0

    Exemple 2:

    int t[10] = {[4]=5,6,[9]=9};

    résultat :

    0

    0

    0

    0

    5

    6

    0

    0

    0

    9

    Exemple 3:

    int t[10] = {2,3,[1]=5,[5]=6,[3]=9};

    résultat :

    2

    5

    0

    9

    0

    6

    0

    0

Appeler le centre

Avez-vous besoin d'un coach de formation?

Il vous aidera à comparer différents cours et à trouver la solution la plus abordable.

Tableaux, pointeurs et allocation dynamique

Prix sur demande