Petite question sur la mémoire en C

Le problème exposé dans ce sujet a été résolu.

Bonjour,

Dans le code suivant, deux fonctions (fill et fill_2) sont proposées pour remplir la variable list[], déclarée ligne 30.
De ce que j’ai compris, la fonction fill est préférable parce que l’autre fonction induit une fuite de mémoire irrécupérable.

Ce que je ne comprends pas, c’est pourquoi la fonction fill fonctionne.
La valeur contenue dans temp n’est-elle pas automatiquement effacée au retour de la fonction ?

#include <stdio.h>
#include <stdlib.h>

typedef struct AB
{
    int a, b;

} AB;

void fill(AB* list)
{
    AB temp;
    temp.a = 47;
    temp.b = 23;

    list[0] = temp;
}

void fill_2(AB* list)
{
    AB* temp = malloc(sizeof(AB));
    
    temp->a = 47;
    temp->b = 23;
    list[0] = *temp;
}

int main(void)
{
    AB list[1];
    fill_2(list); // à tester aussi avec : fill(list);

    printf("list.a : %d\nlist.b : %d\n", list[0].a, list[0].b);
    return EXIT_SUCCESS;
}

Lu’!

Lorsque tu fais l’opération en ligne 16, le contenu de la structure temp est copié dans la cellule list[0]. Donc oui, après l’exécution de la fonction, la mémoire stack est dépilée et temp est "libérée" (avec des gros guillemets), mais c’est pas grave puisqu’on a copié son contenu là où c’était nécessaire.

La valeur contenue dans temp n’est-elle pas automatiquement effacée au retour de la fonction ?

Si. Mais avant la destruction de temp, tu en fais une copie dans list[0]. Qu’il vaudrait mieux écrire *list par ailleurs (bien que ce soit équivalent), pour éviter toute confusion sur le fait que tu manipules qu’un seul élément et non pas un tableau.

Rajout: grillé, mais c’est pas grave, je poste quand même

+2 -0

C’est un peu compliqué. Pourquoi NULL ? Mais oui, c’est vrai.

The result of the logical negation operator ! is 00 if the value of its operand compares unequal to 00, 11 if the value of its operand compares equal to 00. The result has type int. The expression !E is equivalent to (0==E).

Norme C99 (! n1258 cf: https://ddg.gg?q=!%20n1256)

Bon comme la définition de NULL est un peu complexe, c’est pas évident de répondre, mais avec d’autres paragraphes de la norme on conclut que dans tous les cas où la sizeof T <= sizeof(void*) alors oui !E est équivalent à E==0 (ou E==NULL).

Bon, j’ai cherché un contre exemple rapidement. J’ai trouvé ça si ton compilateur supporte __int128 (c’était la première fois que j’utilisais ce type d’ailleurs).

#include <stdio.h>
#include <stdint.h>

int main(int argc, char* argv[]) {

    printf("%zu\n", sizeof(__int128));
    printf("%zu\n", sizeof(void*));

    unsigned __int128 a = 1;
    a <<= 64;

    printf("%p\n", (void*)a);


    if( a == NULL )
        puts("est NULL");
    if( !a )
        puts("est 0");

    return 0;
}

BTW:

void fill_h(AB* list) {
    *list = (AB){.a = 47, .b = 23};
}

Plus lisible, plus claire, plus simple.

+0 -0

Bon comme la définition de NULL est un peu complexe, c’est pas évident de répondre, mais avec d’autres paragraphes de la norme on conclut que dans tous les cas où la sizeof T <= sizeof(void*) alors oui !E est équivalent à E==0 (ou E==NULL).

Bon, j’ai cherché un contre exemple rapidement. J’ai trouvé ça si ton compilateur supporte __int128 (c’était la première fois que j’utilisais ce type d’ailleurs).

ache

Mmm… Je ne vois pas le rapport entre ton exemple et la question « est-ce que !variable est équivalent à variable == NULL ». La macroconstante NULL correspond soit à 0 soit à (void *)0.

The macros are

NULL

which expands to an implementation-defined null pointer constant […]

ISO/IEC 9899:2017, doc. N2176, 7.19, Common definitions <stddef.h>, al. 3, p. 211.

An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.

ISO/IEC 9899:2017, doc. N2176, 6.3.2.3, Pointers, al. 3, p. 41.

Autrement dit, au vu de la définition de l’opérateur ! (citée précédemment), on a un cas où c’est strictement équivalent (NULL est définie comme valant zéro) et un cas un peu différent où NULL vaut (void *)0. Toutefois, c’est sans compter les conversions implicites induites par l’opérateur ==.

Otherwise, at least one operand is a pointer. If one operand is a pointer and the other is a null pointer constant, the null pointer constant is converted to the type of the pointer. If one operand is a pointer to an object type and the other is a pointer to a qualified or unqualified version of void , the former is converted to the type of the latter.

ISO/IEC 9899:2017, doc. N2176, 6.5.9, Equality operators, al. 5, p. 69.

En bref, si NULL vaut zéro, alors elle sera convertie vers le type du second opérande, si en revanche elle vaut (void *)0, c’est le second opérande qui sera converti vers le type void *.

char *p = 0;

/*
 * 0 == p => (char *)0 == p 
 *
 * OU
 *
 * (void *)0 == p => (void *)0 == (void *)p
 */

Toutefois, le résultat sera le même.

Two pointers compare equal if and only if both are null pointers, both are pointers to the same object (including a pointer to an object and a subobject at its beginning) or function, both are pointers to one past the last element of the same array object, or one is a pointer to one past the end of one array object and the other is a pointer to the start of a different array object that happens to immediately follow the first array object in the address space.

ISO/IEC 9899:2017, doc. N2176, 6.5.9, Equality operators, al. 6, p. 69 et 70.

If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.

ISO/IEC 9899:2017, doc. N2176, 6.3.2.3, Pointers, al. 3, p. 41.

En bref, est-ce que !pointeur est strictement équivalent à pointeur == NULL ? Si NULL vaut zéro, oui, si NULL vaut (void *)0, non. Néanmoins, le résultat sera identique.

+0 -0

En bref, est-ce que !pointeur est strictement équivalent à pointeur == NULL ? Si NULL vaut zéro, oui, si NULL vaut (void *)0, non. Néanmoins, le résultat sera identique.

Taurre

Voilà ^^
Tu es arrivé à la même conclusion que moi mais en détaillant tout le raisonnement.

Le truc, c’est que l’OP a parlé d’expression !variable et variable == NULL sans pour autant préciser que variable serait un pointeur.

En supposant que NULL vaille (void*)0 et que variable ne soit pas un pointeur, alors il est possible que !variable ne soit pas équivalent à variable == NULL.

Dans tous les autres cas, variable == NULL équivaut à !variable.

+0 -0

Ah en fait du coup les deux derniers messages ont embrouillés un peu ma compréhension de ce que je pensais avoir compris. o_O

Pour simplifier, peut-on affirmer que si variable est soit un pointeur (char*, void*, int*…) , soit une variable classique (int, char, short…), alors !variable est strictement équivalent à variable == NULL ? (Oui, d’après ce que j’ai compris.)

  1. Si variable est un pointeur, oui.
  2. Si variable est un type scalaire, et que NULL est (void*)0, alors non.

Pour le point 1, cf. message de Taurre. Pour le point 2, cf. mon contre exemple.

Contre exemple
#include <stdio.h>
#include <stdint.h>

int main(int argc, char* argv[]) {

    printf("%zu\n", sizeof(__int128));
    printf("%zu\n", sizeof(void*));

    unsigned __int128 a = 1;
    a <<= 64;

    printf("%p\n", (void*)a);


    if( a == NULL )
        puts("est NULL");
    if( !a )
        puts("est 0");

    return 0;
}

Qui affiche chez moi où NULL vaut (void*)0 :

$ ./a.out
16
8
(nil)
est NULL

Mais n’affiche pas la dernière ligne est 0.

Si chez toi, est NULL ne s’affiche pas, c’est que NULL vaut 0, ce qui est compatible avec la norme. Tu peux simuler le fait que NULL soit égale à (void*)0 en ajoutant ceci :

#undef NULL
#define NULL (void*)0
+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte