« Programmation C/Types avancés » : différence entre les versions

Contenu supprimé Contenu ajouté
Alveric (discussion | contributions)
m + noinclude
Tavernierbot (discussion | contributions)
m Bot: Retouches cosmétiques
Ligne 1 :
<noinclude>{{Programmation C}}
</noinclude>
== Structures ==
<pre>
struct ma_structure {
Ligne 12 :
Déclare une structure (ou enregistrement) ''ma_structure'' composé de N champs, ''champ1'' de type ''type1'', ''champ2'' de type ''type2'', etc. On déclare, par la même occasion, M variables de type ''struct ma_structure''.
 
=== Accès aux champs ===
L'accès aux champs d'une structure se fait avec un point :
<pre>
Ligne 24 :
</pre>
 
=== Initialisation ===
Il y a plusieurs façons d'initialiser une variable de type structure :
#En initialisant les champs un à un :<br /><code>struct { char ch; int nb; float pi; } variable;<br /><br />variable.ch = 'a';<br />variable.nb = 12345;<br />variable.pi = 3.141592;</code><br /> Cette façon est néanmoins pénible lorsqu'il y a beaucoup de champs.
#À la déclaration de la variable :<br /><code>struct { char ch; int nb; float pi; } variable = { 'a', 12345, 0.141592 };</code><br /> Les valeurs des champs sont assignés dans l'ordre où ils sont déclarés. S'il manque des initialisations, les champs seront initialisés à 0. L'inconvénient c'est qu'on doit connaitre l'ordre où sont déclarés les champs, ce qui peut être tout aussi pénible à retrouver.
#Une extension de la norme ISO C99 permet d'initialiser certains champs à la déclaration de la variable, en les nommants :<br /><code>struct { char ch; int nb; float pi; } variable = { .pi = 3.141592, .ch = 'a', .nb = 12345 };</code></br> Les champs non initialisés seront mis à zéro.
 
=== Manipulation ===
Les structures se manipulent par valeur, comme les types atomiques du langage C et '''contrairement''' aux tableaux.
 
La seule opération prise en charge par le langage est la copie binaire, lors des affectations ou des passages de paramètres à des sous-fontions. Toutes les autres opérations sont à la charge du programmeur, notamment la comparaison d'égalité (C.f section suivante).
 
=== Alignement et bourrage (padding) ===
Il s'agit d'un concept relativement avancé, mais qu'il est bien de connaitre pour agir en connaissance de cause. Lorsqu'on déclare une structure, on pourrait naïvement croire que les champs se suivent les uns à la suite des autres en mémoire. Considérez la structure suivante&nbsp;:
 
Ligne 96 :
<pre>L'offset de 'champ2' vaut 4.</pre>
 
=== Pointeurs vers structures ===
Il est (bien entendu) possible de déclarer des variables de type pointeur vers structure :
<pre>
Ligne 114 :
</pre>
 
== Unions ==
Une union et un enregistrement se déclarent de manière identique :
 
Ligne 138 :
int type;
 
XAnyEvent xany;
XKeyEvent xkey;
XButtonEvent xbutton;
XMotionEvent xmotion;
XCrossingEvent xcrossing;
XFocusChangeEvent xfocus;
XExposeEvent xexpose;
/* ... */
XErrorEvent xerror;
XKeymapEvent xkeymap;
long pad[24];
};</pre>
 
Ligne 165 :
</pre>
 
== Définitions de synonymes de types (typedef) ==
 
Le langage C offre un mécanisme assez pratique pour définir des synoymes de types. Il s'agit de l'instruction <code>typedef</code>.
<pre>
typedef un_type synonyme_du_type;
</pre>
 
Contrairement au langage à typage fort (comme le C++), le C se base sur les types atomiques pour décider de la compatibilité entre deux types. Dit plus simplement, la définition de nouveaux types est plus un mécanisme d'alias qu'une réelle définition de type. Les deux types sont effectivement parfaitement interchangeable. À la limite on pourrait presque avoir les mêmes fonctionnalités en utlisant le préprocesseur C, bien qu'avec ce dernier vous aurez certainement beaucoup de mal à sortir de tous les pièges qui vous seront tendus.
 
=== Quelques exemples ===
<pre>
typedef unsigned char octet;
typedef double matrice4_4[4][4];
typedef struct ma_structure * ma_struct;
typedef void (*gestionnaire_t)( int );
 
/* Utilisation */
octet nombre = 255;
matrice4_4 identite = { {1,0,0,0}, {0,1,0,0}, {0,0,1,0}, {0,0,0,1} };
ma_struct pointeur = NULL;
gestionnaire_t fonction = NULL;
</pre>
 
Ligne 201 :
Les vétérans auront reconnu le prototype imbitable de l'appel système <code>signal()</code>, qui permet de rediriger les signaux (interruption, alarme périodique, erreur de segmentation, division par zéro, ...).
 
== Énumérations ==
<pre>
enum nom_enum { val1, val2, ..., valN };
Ligne 218 :
Ce qui est assez pénible en fait, puisqu'il faut à chaque fois se souvenir que le type <code>Booleen</code> est dérivé d'une énumération. Il est préférable de simplifier les déclarations, grâce à l'instruction ''typedef'' :
<pre>
typedef enum { Faux, Vrai } Booleen;
 
/* Pour l'utiliser */
Ligne 224 :
</pre>
 
== Type incomplet ==
 
Pour garantir un certain degré d'encapsulation, il peut être intéressant de masquer le contenu d'un type complexe, pour éviter les usages trop « optimisés » de ce type. Pour cela, le langage C permet de déclarer un type sans indiquer explicitement son contenu.
Ligne 237 :
Les différents champs de la structure n'étant pas connus, le compilateur ne saura donc pas combien de mémoire allouer. On ne peut donc utiliser les types incomplets qu'en tant que pointeur. C'est pourquoi, il est pratique d'utiliser l'instruction <code>typedef</code> pour alléger les écritures :
<pre>
typedef struct ma_structure * ma_struct;
 
/* Plus loin dans le code */