const variablen struct und Intalisierung



  • Guten Morgen Leute,

    ich habe eine Struktur welche werte enthält die "nur" einmal anfangs gesetzt werden dürfen. Und da wäre ja "const" das richtige:

    Bspw:

    typedef struct stFoo
    {
    	const struct
    	{
    		int val;
    	} constVars;
    
    } stFoo;
    

    nun kann ich die die Struktur wie folgt aufm Stack intalisiern:

    stFoo foo = { {42}};
    t2.constVars.val = 4;  // Geht nicht, was auch so sein soll.
    

    nun möchste ich aber dies struktur aufm heap anlegen:

    stFoo* pFoo = (stFoo*)malloc(sizeof(stFoo));
    pFoo->constVars.val = 5; //Get nicht , was logisch ist, will ich aber einmal initalisieren
    

    Dann habe ich mir folgenden Workaorund überlegt;

    
    // Foo mit inital values
    	int l = 5;
    	stFoo initFoo = { {l}};
    		
    	// Foo auf heap
    	stFoo* pFoo = (stFoo*)malloc(sizeof(stFoo));
    
    	// init foo auf foo pointer kopieren
    	memcpy(pFoo, &initFoo, sizeof(stFoo));
    

    ich kopiere quasi die initalFoo auf den heap foo bereich. Aber ich weiß nich ob das gehacke ist, oder wie könnte ich das lösen?



  • Wieso machst du den inneren struct const? Mach den Zeiger auf das äussere Ding const.

    Davon abgesehen: in Speicher den du per malloc besorgt hast darfst du natürlich immer mit memcpy reinkopieren.



  • @hustbaer sagte in const variablen struct und Intalisierung:

    Wieso machst du den inneren struct const? Mach den Zeiger auf das äussere Ding const.

    wie meinst du das? du meinst stFoo * const?



  • @SoIntMan Wie wäre es mit einem Placement new in dem mit malloc reservierten Speicherbereich?

    new (pFoo) stFoo{ { 42 } };
    

    Damit machst du eine reguläre Initialisierung wie bei der Stack-Initalisierung ohne solche (in meinen Augen) "hackigen" Tricksereien wie mit memcpy ein const-Objekt zu schreiben (obwohl man natürlich argumentieren kann, dass das noch kein wirkliches Objekt innerhalb seiner Lebenszeit ist, wenn noch kein Konstruktor aufgerufen wurde - sondern lediglich "Speicher").

    Zusatz: bei Placement new empfiehlt sich auch ein manueller Destruktor-Aufruf bei der Freigabe:

    pFoo->~stFoo();
    std::free(pFoo);
    

    Dieser ist bei Objekten mit trivialem Destruktor glaube ich nicht strikt notwendig, aber schaden tut er auch nicht. Und spätestens wenn der Destruktor irgendwelche nebeneffekte hat, braucht man den Aufruf sowieso.



  • @Finnegan sagte in const variablen struct und Intalisierung:

    @SoIntMan Wie wäre es mit einem Placement new in dem mit malloc reservierten Speicherbereich?

    Wir sind hier in C, nicht in C++ 🙂



  • @wob sagte in const variablen struct und Intalisierung:

    @Finnegan sagte in const variablen struct und Intalisierung:

    @SoIntMan Wie wäre es mit einem Placement new in dem mit malloc reservierten Speicherbereich?

    Wir sind hier in C, nicht in C++ 🙂

    Lol... Danke, hab ich echt verpeilt. Ich nehme alles zurück... hatte einfach auf "ungelesene Beiträge" geklickt und gar nicht aufs Forum geachtet ... wars schon verwundert, weil malloc etwas unorthodox aussah 😁



  • @SoIntMan sagte in const variablen struct und Intalisierung:

    @hustbaer sagte in const variablen struct und Intalisierung:

    Wieso machst du den inneren struct const? Mach den Zeiger auf das äussere Ding const.

    wie meinst du das? du meinst stFoo * const?

    Ja, sorry, hab mich ungenau ausgedrückt. Natürlich soll nicht der Zeiger const sein, sondern der Zeiger soll auf den Typ stFoo const zeigen. Also stFoo const* foo

    stFoo const* createFoo(void) {
        stFoo* foo = malloc(sizeof(stFoo));
        foo->things.stuff = 123;
        return foo;
    }
    

    Wenn nur Teile von stFoo const sein sollen, aber andere Teile nicht, dann mach es wie du es schon gezeigt hast, per memcpy.



  • @SoIntMan sagte in const variablen struct und Intalisierung:

    stFoo* pFoo = (stFoo*)malloc(sizeof(stFoo));
    
    1. Wenn Du Beiträge in C Forum einstellst, sollten auch die Codeblöcke als C markiert sein.
    2. In C ist der Cast weder notwendig noch sinnvoll, und in C++ solltest Du ihn auch nicht nutzen, da dort ein static_cast angesagt wäre.


  • den cast muss man machen, weil der c++ compiler sonst meckert, wenn man den c code einfach so ins c++ programm reinkopiert. ehrlich: das ist die begründung!🤣



  • @hustbaer sagte in const variablen struct und Intalisierung:

    @SoIntMan sagte in const variablen struct und Intalisierung:

    @hustbaer sagte in const variablen struct und Intalisierung:

    Wieso machst du den inneren struct const? Mach den Zeiger auf das äussere Ding const.

    wie meinst du das? du meinst stFoo * const?

    Ja, sorry, hab mich ungenau ausgedrückt. Natürlich soll nicht der Zeiger const sein, sondern der Zeiger soll auf den Typ stFoo const zeigen. Also stFoo const* foo

    stFoo const* createFoo(void) {
        stFoo* foo = malloc(sizeof(stFoo));
        foo->things.stuff = 123;
        return foo;
    }
    

    Wenn nur Teile von stFoo const sein sollen, aber andere Teile nicht, dann mach es wie du es schon gezeigt hast, per memcpy.

    genau ich habe nur eine "inner struct" Teil welche const ist.. der rest kann sich zur laufzeit ändern... d.h. memcpy is in dem fall nich hacky!?

    @john-0 sagte in const variablen struct und Intalisierung:

    In C ist der Cast weder notwendig noch sinnvoll, und in C++ solltest Du ihn auch nicht nutzen, da dort ein static_cast angesagt wäre.

    hmm , ist halt ne konvention hier.. alter complier alle warnungen etc.

    @Peter-Viehweger sagte in const variablen struct und Intalisierung:

    den cast muss man machen, weil der c++ compiler sonst meckert, wenn man den c code einfach so ins c++ programm reinkopiert. ehrlich: das ist die begründung!

    genau



  • @SoIntMan sagte in const variablen struct und Intalisierung:

    @Peter-Viehweger sagte in const variablen struct und Intalisierung:

    den cast muss man machen, weil der c++ compiler sonst meckert, wenn man den c code einfach so ins c++ programm reinkopiert. ehrlich: das ist die begründung!

    genau

    ja also vernünftige programmierung sieht jetzt aber glaube ich so aus, dass man c code auch in c dateien packt und dann die header-dateien mit den prototypen in das c++ programm einbindet.

    man ruft in c++ nicht mal eben so ne funktion aus c auf - soviel ich weiß........🤔



  • @SoIntMan sagte in const variablen struct und Intalisierung:

    @hustbaer sagte in const variablen struct und Intalisierung:

    @SoIntMan sagte in const variablen struct und Intalisierung:

    @hustbaer sagte in const variablen struct und Intalisierung:

    Wieso machst du den inneren struct const? Mach den Zeiger auf das äussere Ding const.

    wie meinst du das? du meinst stFoo * const?

    Ja, sorry, hab mich ungenau ausgedrückt. Natürlich soll nicht der Zeiger const sein, sondern der Zeiger soll auf den Typ stFoo const zeigen. Also stFoo const* foo

    Ich würde sagen das sollte OK sein. Ich bin allerdings kein C Experte.

    In C++ gäbe es hier zumindest folgende Bedingungen:

    • Der Speicher wo du das memcpy rein machst muss in Wirklichkeit "beschreibbar" sein. Das sollte immer erfüllt sein, so lange du nicht das ganze Objekt const machst. Gegenbeispiel wäre eine globale Variable, wo die ganze Variable const ist. So eine dürfte der Compiler/Linker nämlich in einem nicht-schreibbaren Speichersegment ablegen.
    • Nachdem du das erste mal über den Struktur-Typ auf die Struktur zugegriffen hast, darfst du die const Teile des selben Objekts nicht mehr ändern. D.h. du darfst nicht "memcpy -> Zugriff mittels struct-type -> memcpy -> Zugriff mittels struct-type" mit dem selben Objekt machen. Denn der Compiler darf davon ausgehen dass const struct-Member des selben Objekts immer den selben Wert haben.


  • @SoIntMan sagte in const variablen struct und Intalisierung:

    hmm , ist halt ne konvention hier.. alter complier alle warnungen etc.

    Wie alt ist der Compiler, und vor allem was für ein Compiler wird eingesetzt?

    Ich habe hier als älteste Norm nur die C99 Norm. Im K&R Buch in der neueren ANSI Auflage wird die Variante mit Cast verwendet, allerdings ist das K&R Buch auch in der ANSI Version nicht zu 100% konform mit der Norm. Da finden sich noch so einige Altlasten der K&R Version drin.

    Der Verdacht steht hier im Raum, dass anstatt eines C Compilers ein C++ Compiler genutzt wird. C sollte man immer mit dem C Compiler übersetzen. C erlaubt es immer einen void * Zeiger in einen beliebigen konkreten Zeiger per Zuweisung zu konvertieren. Wenn man nun einen Cast verwendet überdeckt man damit leicht andere Fehler.

    In C++ ist der Cast notwendig, allerdings ist dann die Variante mit C++ Cast zu verwenden

    char* string = static_cast<char*>(malloc(1024));
    

    @Peter-Viehweger sagte in const variablen struct und Intalisierung:

    den cast muss man machen, weil der c++ compiler sonst meckert, wenn man den c code einfach so ins c++ programm reinkopiert. ehrlich: das ist die begründung!

    genau

    Da hat jemand den Sarkamus wohl nicht mitbekommen?



  • ich denk, in c++ soll man zwingend "new" verwenden bzw. allgemein keine "altlasten" aus c?

    das war kein sarkasmus, das wurde/wird tatsächlich so gelehrt!



  • @Peter-Viehweger sagte in const variablen struct und Intalisierung:

    ich denk, in c++ soll man zwingend "new" verwenden bzw. allgemein keine "altlasten" aus c?

    Ja, das ist prinzipeill richtig. Nur zuweilen muss man doch auf malloc zurückgreifen. Wenn man denn von der Regel new verwenden abweicht, dann aber auch richtig mit dem passenden C++ Cast.

    In C hingegen kann man mit dem Cast Compiler Warnungen verdecken, und das ist da nicht gut. Deshalb in C kein Cast.

    Für Code, der sowohl von C wie auch C++ genutzt wird, braucht man das auch nicht, da man nur C Header von C++ aus nutzt. Man natürlich esoterische Spezialfälle anführen, bei denen man C Code explizit als C++ übersetzen können will, weil man etwa C++ Exceptions durch den C Code hindurch progagieren können will, was sonst nicht garantiert ist. Aber in so einem Fall kommt ein ganzer Rattenschwanz an Einschränkungen für nutzbaren C Code, dass man das gerade für Anfänger schlichtweg ignorieren kann.



  • @Peter-Viehweger sagte in const variablen struct und Intalisierung:

    ich denk, in c++ soll man zwingend "new" verwenden bzw. allgemein keine "altlasten" aus c?

    das war kein sarkasmus, das wurde/wird tatsächlich so gelehrt!

    Ja, wäre vermutlich gut.
    Trotzdem gibt es in C++ Regeln zum Thema roher Speicher, memcpy etc. U.a. damit "üblicher" C-Code wie malloc + memcpy zum Initialisieren von structs explizit erlaubt ist. Zum anderen damit man standardkonforme Implementierungen von Containern ala vector machen kann.



  • ich habe jetzt mal ein bisschen rumexperimentiert und dabei ist dann das herausgekommen:

    //main.cpp
    
    extern "C"
    {
    #include "CFunctions.h"
    }
    
    class MyClass
    {
    public:
    	MyClass()
    	{
    		CFunction();
    	}
    };
    
    int main()
    {
    	MyClass myclass;
    
    	return 0;
    }
    
    //CFunctions.h
    
    #ifndef CFUNCTIONSH
    #define CFUNCTIONSH
    
    int CFunction(void);
    
    #endif
    
    //CFunctions.c
    
    #include "CFunctions.h"
    
    #include <stdio.h>
    #include <stdlib.h>
    
    int CFunction(void)
    {
    	printf("Hello from C!\n");
    
    	//system("pause");
    
    	return 0;
    }
    

    da hätte ich doch eine saubere trennung von c und c++, der compiler weiß anhand der dateiendung, welche programmiersprache vorliegt, und ein zu testzwecken eingefügtes malloc kann ohne cast aufgerufen und der zeiger über die parameter o.ä. beliebig zwischen c und c++ hin und her gereicht werden, oder?



  • @Peter-Viehweger sagte in const variablen struct und Intalisierung:

    und ein zu testzwecken eingefügtes malloc kann ohne cast aufgerufen und der zeiger über die parameter o.ä. beliebig zwischen c und c++ hin und her gereicht werden, oder?

    Ja das geht. Du musst halt aber auf das Freigeben achten. Und ich würde den Header aber folgendermaßen definieren:

    //CFunctions.h
    
    #ifndef CFUNCTIONSH
    #define CFUNCTIONSH
    
    #ifdef __cplusplus
    extern "C"
    {
    #endif
    
    int CFunction(void);
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif
    

    Aber wozu benötigst du C Code in C++? Hast du C Code übernommen, welchen du in C++ nutzen möchtest?



  • @Quiche-Lorraine sagte in const variablen struct und Intalisierung:

    Aber wozu benötigst du C Code in C++? Hast du C Code übernommen, welchen du in C++ nutzen möchtest?

    nein das jetzt nicht, aber weiter oben findest du einen überflüssigen cast bei malloc, wobei ich das im studium auch mal so gelernt habe, dass man da casten soll, um dann den compilerfehler bei c++ zu unterdrücken.

    dann habe ich irgendwo mal aufgeschnappt, dass man c und c++ nicht einfach so vermischen soll, weil das mit den 100% kompatibilität bestimmt seit 30 jahren nicht mehr so ist und mir halt gedanken gemacht, wie man sowas dann anders/besser umsetzen könnte.

    also c funktionen, die man evtl. in seinem c++ programm verwenden möchte, gibt es ja mehr als genug: windows api, posix, opengl......



  • Die Frage ist für mich wieso du die Funktion(en) überhaupt in C implementieren willst, wenn sie in einem C++ Projekt verwendet werden.


Anmelden zum Antworten