Benutzerdefinierte Rückgabe ohne struct



  • Hallo.

    Ich habe eine Routine, die eine gewisse Anzahl von Werten und einen Index zurückgibt.

    Also in etwa so:

    Paar 1:
    Wert 1, Index 1

    Paar 2:
    Wert 2, Index 2

    ...

    Der Wert ist vom Typ double, der Index vom Typ int

    Ich dachte mir, ich kann ja für n Paare einfach

    n * (sizeof(int) + sizeof(double))
    

    reservieren und diese dann vollschreiben.

    Ein Beispielcode sieht so aus:

    #include <stdio.h>
    #include <stdlib.h>
    
    void * GetValueAndIndex(int fn);
    
    int main(){
    	int n = 5, i=0;
    	void * Value = GetValueAndIndex(n);
    
    	for (i=0;i<n;i++){
    		printf("Index: %i\n", *Value);
    		Value+=sizeof(int);
    		printf("Value: %2.2f\n", *Value);
    		Value+=sizeof(double);
    	}
    	getch();
    	return 0;
    }
    
    void * GetValueAndIndex(int fn){
    	int fi=0;
    	void * fValue = calloc(fn, sizeof(int)+sizeof(double)), * fValueStart = fValue;
    
    	for (fi=0;fi<fn;fi++){
    		*fValue = fi;
    		fValue+=sizeof(int);
    		*fValue = 1.5;
    		fValue+=sizeof(double);
    	}
    
    	return fValueStart;
    }
    

    Also, in Realität ist der Index nicht aufsteigend, dann wäre er ja sinnlos.

    Die Ausgabe des Index funktioniert, aber der Wert liefert immer 0.00

    Ist diese Idee prinzipiell realisierbar, oder muss ich mir eine struct definieren?

    Vielen Dank für die Hilfe.



  • CJens schrieb:

    Ist diese Idee prinzipiell realisierbar, oder muss ich mir eine struct definieren?

    Ja, sie ist realisierbar. Fehlen noch ein paar Casts, aber grundsätzlich geht das.

    Fragt sich bloss, was das genau bringen soll, denn dadurch verschenkt man nur Typsicherheit und gewinnt nichts.

    Es ist aber eine wichtige Erkenntnis, dass nach dem der Compiler drübergegangen ist, alles so gemacht wird. structs kosten nichts und der Code lässt sind recht einfach so umformen, dass sie nicht mehr gebraucht werden.



  • Wie man bei meinem anderen Post schon entnehmen kann, kann n > 10 Mio. sein.
    Und die Routine muss im laufe einer Berechnung öfters ausgeführt werden.

    Kosten structs wirklich nichts? Ich dachte, das verhält sich ähnlich wie eine Klasse, auf die man in der prozessorientierten Programmierung wenn möglich verzichtet. Ich lasse mich da aber auch gerne eines Besseren belehren.

    Ich dachte nur, dass so ein Direktzugriff vielleicht schneller ist...



  • Was den konkreten Fall angeht, so hast du mit dem Direktzugriff (neben der fürchterlichen Syntax) das Problem, dass das Alignment der doubles potentiell nicht stimmt. Wenn die Plattform erwartet, dass doubles auf mehr als sizeof(int) Byte ausgerichtet sind, wird der Code sich komisch verhalten. Das ist zum Beispiel auf x86 (und x86-64) der Fall, wenn der Compiler SSE-Instructions erzeugen will.

    Glücklicherweise gibt es mit Structs einen einfachen Weg, das Problem dem Compiler aufs Auge zu drücken; der hat dafür zu sorgen, dass die Struct-Member richtig ausgerichtet sind (oder andernfalls Code zu erzeugen, der das mühsam richtig zusammenstückelt). Structs in C sind nur flacher Speicher; ein Performanceverlust ist durch sie nicht zu erwarten. Zwar kann es sich lohnen, bei der Reihenfolge der Struct-Member etwas aufzupassen, um Speicherverlust durch Padding zu verringern (Padding ist unbenutzter Speicher zwischen Struct-Membern, der dort eingefügt wird, um richtiges Alignment zu garantieren), aber da man das Problem sonst von Hand lösen müsste, ist das gegenüber des Hantierens mit void-Zeigern kein Nachteil.

    Klassen in C++ als solche bedeuten übrigens auch keinen Overhead. Sie funktionieren ja im Grunde genauso wie structs, und wenn man sich "overhead-trächtige" Mechanismen an Bord holt (ich denke da jetzt an virtuelle Funktionen und derlei), sollte man das machen, weil man sie braucht. Den selben Mechanismus in C nachzuimplementieren, ob jetzt mit Structs oder mit void*, wird da in aller Regel auch nicht performanter sein.

    In gemanagten Sprachen sieht das anders aus, aber da hat man nicht wirklich eine Wahl.



  • Bei der Zeigerarithmetik wird ja die Größe des Objektes berücksichtigt, indem der Zeiger bei einem Inkrement von 1 um sizeof(object) weitergezählt wird.

    Nur was ergibt sizeof(void) ?

    void * Value = GetValueAndIndex(n); 
           Value+=sizeof(int); // meckert der Compiler hier nicht?
    

    Soweit ich das verstanden habe, deckt der Standard Zeigerarithmetik auf void* nicht ab.

    Genauso bei

    void * fValue = calloc(fn, sizeof(int)+sizeof(double)), * fValueStart = fValue;
    
            *fValue = fi; // Hier soll ein int zugewiesen werden
            *fValue = 1.5;  // und hier ein double.
    Soll der Compiler raten auf was fValue jetzt gerade zeigt? Und wenn er das tut, dann macht er Meldung.
    
    Was du da vor hast, geht nur mit Zeiger-cast 
    [code="c"]*(double *)fValue = 1.5;
    *(int*)fValue = 1;
    

    Das ist aber trotzdem der falsche Weg. Nimm eine struct.
    Der Aufbau der struct enthält ja nur Informationen für den Compiler, welche Element enthalten sind.
    Im Compilat wird nur zur Basisadresse der Offset addiert. Das machst du aber auch willst du aber auch machen.
    Nur der Compiler kann das besser.



  • void* arithmetik ist nicht standardkonform benutze stattdessen char*, und du musst den Zeiger bei Zuweisungen entsprechend Casten.

    Eine struct braucht evtl. etwas mehr Speicher. Dafür sind die Variablen so ausgerichtet, dass möglichst schnell auf sie zugegriffen werden kann und du bekommst Typsicherheit.


Anmelden zum Antworten