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.