web-dev-qa-db-fra.com

Comment initialiser const dans une structure en C (avec malloc)

J'ai essayé;

void *malloc(unsigned int);
struct deneme {
    const int a = 15;
    const int b = 16;
};

int main(int argc, const char *argv[])
{
    struct deneme *mydeneme = malloc(sizeof(struct deneme));
    return 0;
}

Et c'est l'erreur du compilateur:

gereksiz.c:3:17: error: expected ':', ',', ';', '}' or '__attribute__' before '=' token

Et aussi ceci;

void *malloc(unsigned int);
struct deneme {
    const int a;
    const int b;
};

int main(int argc, const char *argv[])
{
    struct deneme *mydeneme = malloc(sizeof(struct deneme));
    mydeneme->a = 15;
    mydeneme->b = 20;
    return 0;
}

Et c'est l'erreur du compilateur:

gereksiz.c:10:5: error: assignment of read-only member 'a'
gereksiz.c:11:5: error: assignment of read-only member 'b'

Et ni ont été compilés. Est-il possible d'initialiser une variable const dans une structure lors de l'allocation de mémoire avec malloc?

20
yasar

Vous devez rejeter le const pour initialiser les champs d'une structure mallocée:

struct deneme *mydeneme = malloc(sizeof(struct deneme));
*(int *)&mydeneme->a = 15;
*(int *)&mydeneme->b = 20;

Alternativement, vous pouvez créer une version initialisée de la structure et la mémoriser:

struct deneme deneme_init = { 15, 20 };
struct deneme *mydeneme = malloc(sizeof(struct deneme));
memcpy(mydeneme, &deneme_init, sizeof(struct deneme));

Vous pouvez rendre deneme_init statique et/ou global si vous le faites souvent (il ne doit donc être construit qu'une seule fois).


Explication de la raison pour laquelle ce code n'est pas un comportement indéfini, comme suggéré par certains commentaires, à l'aide des références standard C11:

  • Ce code ne viole pas 6.7.3/6 car l'espace renvoyé par malloc n'est pas "un objet défini avec un type qualifié de const". L'expression mydeneme->a n'est pas un objet, c'est une expression. Bien qu'il ait le type qualifié const-, il désigne un objet qui n'a pas été défini avec un type qualifié const (en fait, il n'a pas été défini avec aucun type du tout).

  • La règle de repliement strict n'est jamais violée en écrivant dans l'espace alloué par malloc, car le type effectif (6.5/6) est mis à jour à chaque écriture.

(La règle d'aliasing stricte peut être violée en lisant à partir de l'espace alloué par malloc cependant).

Dans les exemples de code de Chris, le premier définit le type effectif des valeurs entières sur int, et le second définit le type effectif sur const int, mais dans les deux cas, la lecture de ces valeurs passe par *mydeneme est correct car la règle de crénelage strict (6.5/7, puce 2) permet de lire un objet via une expression également ou plus qualifiée que le type effectif de l'objet. Puisque l'expression mydeneme->a a le type const int, elle peut être utilisée pour lire des objets de type effectif int et const int.

22
Chris Dodd

Avez-vous essayé de faire comme ça:

int main(int argc, const char *argv[])
{
    struct deneme mydeneme = { 15, 20 };
    struct deneme *pmydeneme = malloc(sizeof(struct deneme));
    memcpy(pmydeneme, &mydeneme , sizeof(mydeneme));
    return 0;
}

Je n'ai pas testé mais le code semble correct

10
FBruynbroeck

Je ne suis pas d'accord avec la réponse de Christ Dodd , car je pense que sa solution donne Undefined Behavior conformément aux normes, comme d'autres l'ont dit. 

Pour "contourner" le qualificatif const d'une manière qui n'invoque pas un comportement indéfini, je propose la solution suivante: 

  1. Définissez une variable void* initialisée avec un appel malloc()
  2. Définissez un objet du type souhaité, dans ce cas struct deneme et initialisez-le de manière à ce que le qualificateur const ne se plaint pas (c'est-à-dire, dans la ligne de déclaration elle-même). 
  3. Utilisez memcpy() pour copier les bits de l'objet struct deneme dans l'objet void*
  4. Déclarez un pointeur sur l'objet struct deneme et initialisez-le à la variable (void*), précédemment convertie en (struct deneme *)

Donc, mon code serait: 

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct deneme {
    const int a;
    const int b;
};
struct deneme* deneme_init(struct deneme data) {
    void *x = malloc(sizeof(struct deneme));
    memcpy(x, &data, sizeof(struct deneme));
    return (struct deneme*) x;
}
int main(void) {
    struct deneme *obj = deneme_init((struct deneme) { 15, 20, } );
    printf("obj->a: %d, obj->b: %d.\n", obj->a, obj->b);
    return 0;
}
2
pablo1977

Intéressant, j'ai trouvé que cette méthode C99 fonctionnait en mode Clang mais pas en gcc

int main(int argc, const char *argv[])
{
    struct deneme *pmydeneme = malloc(sizeof(struct deneme));
    *pmydeneme = (struct deneme) {15, 20};
    return 0;
}
1
Andrey