Objet
Un objet est une zone de la mémoire bornée qui existe pendant l’exécution du programme et dont le contenu peut représenter une valeur.
Lorsque cette zone est référencée, on peut lui attribuer un type.
Un objet non typé est par exemple la valeur
de retour de
malloc
. Pour accéder à cet objet
il faut passer par un
pointeur
.
Voila des exemples d’entités d’un programme C qui ne sont pas des objets
#define def
// def n'est pas un objet
enum mon_enum{
en1,
en2,
}
// on ne peut prendre ni l'adresse de mon_enum, ni en1, ni en2
int fonction() {
return 2;
}
// quand bien même une fonction occuperait de l'espace
// en memoire, qu'elle soit inlinée ou non,
// elle n'est pas considérée comme un objet.
// l'identifiant d'une fonction est quand à elle
// considéré comme un pointeur
Un programme C peut tout à fait choisir de ne pas créer d’objets si créer ou non ces objets ne change pas le comportement visible du programme.
Ce n’est pas parceque le code source déclare un objet que celui-ci existera forcément à l’exécution
inline void fun()
{
const int a = 42;
return a;
}
Ici on déclare un objets a, mais cela ne garantit
à l’exécution que cet objet existe bien. Le compilateur
pourrait remplacer chaque occurence de l’appel à la
fonction fun()
par la constante littérale 42
, et n’alouer
de mémoire ni pour la fonction fun
ni pour la
variable a
qu’elle déclare.
Si l’on souhaite réelement forcer la création d’un objet
on peut soit demander au programme de nous fournir un
pointeur
sur celui-ci et que le comportement
du programme soit affecté par ou déclarer l’objet avec le
qualificateur volatile
.
par le programme :
#include <stdio.h>
inline void fun()
{
const int a = 42;
printf("adresse de a %p\n", &a); // avoir un effet de bord necessitant de connaitre l'adresse de l'objet
volatile const b = 12; // le qualificateur volatile empeche toute optimisation de suppression d'objet
return a + b;
}
Le qualificateur
inline
ne garantit pas que la
fonction sera supprimée à l’exécution.
Des objets peuvent être qualifiés avec un type ou non. Ils peuvent être imbriqués. Un objet peut être scalaire, ou bien être composé de plusieurs objets, tel un tableau ou une structure, dans ce cas on parle d’un objet composé.
#include <stdlib.h>
struct obj {
int a;
char * str;
}
int main()
{
void * plage = malloc(1000);
plage + 10 = (struct obj) {12, "toto"};
plage + 10 + sizeof (struct obj) = 12;
memset(plage + 10, sizeof (struct obj), 0);
plage + 10 + sizeof (struct obj) = 0;
free(plage);
}
Nous créons d’abord avec plage
un objet non typé de 1000
octets. Ce malloc
réserve de la mémoire dans une page
allouée par le systeme d’exploitation via un système lui même
similaire (ou identique) à malloc, dans un objet crée en dehors
de notre programme.
On y écrit ensuite un objet de type struct obj
à 10 octets dedans, puis un objet de type int
à sa suite.
On “efface” ou “détruit” la structure en y écrivant
la valeur zero et on efface ou “détruit”
l’entier en y écrivant la valeur zero.
Destruction
Le C ne spécifie pas la destruction des objets, mais leur alloue une durée de vie . Le comportement d’un programme qui référence un objet ayant dépassé cette durée est indéfini .
On détruit rarement un objet en C mais on le libère pour que la mémoire qu’il occupe puisse être utilisé par un autre objet.
Si on souhaite réellement effacer la mémoire occupée
par un objet, par exemple si cette mémoire contenanit
des données sensibles (mot de passe ou autre), il est
déconseillé d’y écrire une valeur quelconque via une
assignation ou
memset
si on ne fait pas emploi de cette
valeur dans la suite du programme : le compilateur peut ignorer
cette instruction si celle-ci ne modifie pas le
comportement visible du programme, et donc les données
peuvent perdurer malgré le souhait du code source.
La suppression de données sensibles peut être effectivement
obtenue par la fonction
posix
bzero
,
ou depuis le C11 par la fonction
memset_s
.
Si rendre la valeur non lisible en dehors des limites
du programme est critique, on y écrit parfois
des valeurs aléatoires ou anodines plutot que simplement 0
.
Faire cela permet en outre d’accélérer la découverte de
lectures hors mémoire
.
Opacité
Un objet peut être opaque. Il est généralement présent dans la signature de fonctions définies par la librairie standart et sert à conserver l’état interne d’un processus nécesitant un appel successif de fonctions liées à ce processus.
Ce processus peut être la lecture ligne à ligne d’un fichier, la conversion d’une séquence d’octets vers son index dans la table unicode .
Un objet opaque ne peut pas être un tableau , si c’est le cas cela est précisé.
Chaque implémantation est libre de le définir comme il l’entend donc un programme qui essaye d’accéder directement à la valeur d’un objet opaque aura un comportement indéterminé .
En pratique les objets opaques sont exposés
via un symbole de
préprocesseur
ou un objet
défini via
typedef
. Un objet opaque de
la librairie standart a généralement le suffixe _t
. Ce suffixe
est réserve aux types définis par la librairie standart.
FILE
ou mbstate_t
sont des exemples d’objets opaques.