it-swarm.com.de

Wie man const in einer struct in C initialisiert (mit malloc)

Ich habe versucht;

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;
}

Und das ist der Fehler des Compilers:

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

Und auch das;

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;
}

Und das ist der Fehler des Compilers:

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

Und keiner wurde zusammengestellt. Gibt es eine Möglichkeit, eine const-Variable innerhalb einer Struktur zu initialisieren, wenn der Speicher mit malloc belegt wird?

20
yasar

Sie müssen den const ablegen, um die Felder einer malloc-Struktur zu initialisieren:

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

Alternativ können Sie eine initialisierte Version der Struktur erstellen und diese speichern:

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

Sie können deneme_init statisch und/oder global machen, wenn Sie dies häufig tun (es muss also nur einmal erstellt werden).


Erklärung, warum dieser Code nicht undefiniert ist, wie in einigen Kommentaren unter Verwendung von C11-Standardreferenzen vorgeschlagen:

  • Dieser Code verstößt nicht gegen 6.7.3/6, da der von malloc zurückgegebene Speicherplatz nicht "ein Objekt definiert mit einem von const qualifizierten Typ" ist. Der Ausdruck mydeneme->a ist kein Objekt, sondern ein Ausdruck. Obwohl es einen const-qualifizierten Typ hat, bezeichnet es ein Objekt, das nicht mit einem const-qualifizierten Typ definiert wurde (tatsächlich nicht mit einem beliebigen Typ definiert).

  • Die strikte Aliasing-Regel wird niemals durch Schreiben in den von malloc zugewiesenen Speicherplatz verletzt, da der effektive Typ (6.5/6) bei jedem Schreibvorgang aktualisiert wird.

(Die strikte Aliasing-Regel kann jedoch durch Lesen aus dem von malloc zugewiesenen Speicherplatz verletzt werden.).

In den Codebeispielen von Chris setzt der erste den effektiven Typ der ganzzahligen Werte auf int, und der zweite setzt den effektiven Typ auf const int. In beiden Fällen ist das Lesen dieser Werte durch *mydeneme jedoch aufgrund der strengen Aliasing-Regel korrekt (6.5/7 Punkt 2) erlaubt das Lesen eines Objekts durch einen Ausdruck, der gleich oder besser als der effektive Typ des Objekts ist. Da der Ausdruck mydeneme->a den Typ const int hat, können mit ihm Objekte des effektiven Typs int und const int gelesen werden.

22
Chris Dodd

Hast du versucht, so zu tun:

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;
}

Ich habe es nicht getestet, aber der Code scheint richtig zu sein

10
FBruynbroeck

Ich stimme der Antwort von Christ Dodd nicht zu, da ich denke, dass seine Lösung undefined Behavior gemäß den Standards ergibt, wie andere sagten. 

Um das const-Qualifikationsmerkmal auf eine Weise zu "umgehen", die kein undefiniertes Verhalten auslöst, schlage ich folgende Lösung vor: 

  1. Definieren Sie eine void*-Variable, die mit einem malloc()-Aufruf initialisiert wird. 
  2. Definieren Sie ein Objekt des gewünschten Typs, in diesem Fall struct deneme, und initialisieren Sie es auf eine Weise, die const-Qualifier nicht beanstandet (dh in der Deklarationszeile selbst). 
  3. Verwenden Sie memcpy(), um die Bits des struct deneme-Objekts in das void*-Objekt zu kopieren. 
  4. Deklarieren Sie einen Zeiger auf struct deneme-Objekt und initialisieren Sie ihn mit der (void*)-Variablen, die zuvor in (struct deneme *) umgewandelt wurde. 

Mein Code wäre also: 

#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

Interessanterweise fand ich, dass diese C99-Methode zwar in clang arbeitet, aber nicht in gcc

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