• Hallo liebe Userinnen und User,

    nach bereits längeren Planungen und Vorbereitungen sind wir nun von vBulletin auf Xenforo umgestiegen. Die Umstellung musste leider aufgrund der Serverprobleme der letzten Tage notgedrungen vorverlegt werden. Das neue Forum ist soweit voll funktionsfähig, allerdings sind noch nicht alle der gewohnten Funktionen vorhanden. Nach Möglichkeit werden wir sie in den nächsten Wochen nachrüsten. Dafür sollte es nun einige der Probleme lösen, die wir in den letzten Tagen, Wochen und Monaten hatten. Auch der Server ist nun potenter als bei unserem alten Hoster, wodurch wir nun langfristig den Tank mit Bytes vollgetankt haben.

    Anfangs mag die neue Boardsoftware etwas ungewohnt sein, aber man findet sich recht schnell ein. Wir wissen, dass ihr alle Gewohnheitstiere seid, aber gebt dem neuen Board eine Chance.
    Sollte etwas der neuen oder auch gewohnten Funktionen unklar sein, könnt ihr den "Wo issn da der Button zu"-Thread im Feedback nutzen. Bugs meldet ihr bitte im Bugtracker, es wird sicher welche geben die uns noch nicht aufgefallen sind. Ich werde das dann versuchen, halbwegs im Startbeitrag übersichtlich zu halten, was an Arbeit noch aussteht.

    Neu ist, dass die Boardsoftware deutlich besser für Mobiltelefone und diverse Endgeräte geeignet ist und nun auch im mobilen Style alle Funktionen verfügbar sind. Am Desktop findet ihr oben rechts sowohl den Umschalter zwischen hellem und dunklem Style. Am Handy ist der Hell-/Dunkelschalter am Ende der Seite. Damit sollte zukünftig jeder sein Board so konfigurieren können, wie es ihm am liebsten ist.


    Die restlichen Funktionen sollten eigentlich soweit wie gewohnt funktionieren. Einfach mal ein wenig damit spielen oder bei Unklarheiten im Thread nachfragen. Viel Spaß im ngb 2.0.

C - Array Inhalt verschieben, Quellwert neu setzen

theSplit

1998
Veteran Barkeeper

Registriert
3 Aug. 2014
Beiträge
28.573
Stehe gerade auf dem Schlauch wie man in C folgendes Problem löst:

Ich will aus einem Array einen Wert um eines nach vorne verschieben und den alten Wert quasi auf 0 setzen und zwar mit folgendem Code.

Es handelt sich um ein vordefiniertem Array von SDL_Rect Typ. [ Definition = {x, y, width, height} ]

[src=c]for (int i = usedSurfacesCount; i > selectedIndex; i--) {
if (i > 0) {
surfaceStore[i-1] = {surfaceStore.x, surfaceStore.y, surfaceStore.h, surfaceStore.w };
}
surfaceStore = {0, 0, 0, 0};
}

usedSurfacesCount--;[/src]

Was hierbei passieren soll, den Quelleintrag an i in i-1 kopieren und danach i's Werte auf 0 setzen?
usedSurfacesCount ist ein Int-Wert der aktuellen Anzahl der SDL_Rect Elemente die in Verwendung sind.

Was nun aber passiert, das eigentlich mit den Werten von i-1 initialisierte SDL_Rect im Array wird beim aktuellen Clearing in Zeile 5, bei der 0-Setzung, auch mit resettet.

Wenn ich richtig nachvollziehe übergibt C beim Aufruf von, zum Beispiel surfaceStore.x nur den Wert der Speicheradresse mit diesem Wert weiter anstatt zu kopieren?

Wie kann ich nun eine Kopie dieses Wertes bekommen und gegebenfalls damit das SDL_Rect initialisieren?
 

SP4C3

Neu angemeldet

Registriert
27 Aug. 2013
Beiträge
54
[src=c]memcpy(surfaceStore+i-1,sorfaceStore+i)[/src]
Sollte doch funktionieren
 

Skipjack

Neu angemeldet

Registriert
17 Juli 2013
Beiträge
213
@SP4C3: Das wird bestimmt nicht funktionieren. Zum einen dürfen bei memcpy die Bereiche nicht überlappen (passiert aber, wenn er das Array verschiebt), also müsste er memmove verwenden. Zum Zweiten fehlt bei Deinem Vorschlag noch der dritte Parameter, der sagt, wieviel den kopiert werden soll.

--- [2015-04-08 09:18 CEST] Automatisch zusammengeführter Beitrag ---

Was nun aber passiert, das eigentlich mit den Werten von i-1 initialisierte SDL_Rect im Array wird beim aktuellen Clearing in Zeile 5, bei der 0-Setzung, auch mit resettet.
Überrascht nicht, Du läuft rückwärts durch Dein Array und rufst in der Loop jedesmal
[src=c]surfaceStore = {0, 0, 0, 0};[/src]
auf. Damit sind nachher natürlich alle Elemente von selectedIndex+1 bis usedSurfaceCount mit 0 initialisiert.

Du willst also den Bereich (selectedIndex; usedSurfaceCount] um einen Index nach vorne verschieben und bei usedSurfaceCount auf Null setzen.
Du musst von vorne anfangen. Rückwärts überschreibst Du Dir immer den Wert bei i-1 mit dem von i, bevor Du im nächsten Loop-Durchlauf i-1 nach i-2 verschieben kannst.
Also
[src=c]for (int i = selectedIndex; i<usedSurfacesCount; i++) {
surfaceStore = {surfaceStore[i+1].x, surfaceStore[i+1].y, surfaceStore[i+1].h, surfaceStore[i+1].w };
}
surfaceStore[usedSurfacesCount] = {0, 0, 0, 0};[/src]

Dabei gehe ich davon aus, dass sowohl selectedIndex wie auch usedSurfacesCount im Array liegen und selectedIndex<usedSurfacesCount gilt. Das würde ich vor der Loop prüfen.

Ansonsten wäre aber das oben angesprochene memmove wirklich effizienter, also
[src=c]memmove(surfaceStore+selectedIndex , surfaceStore+usedSurfacesCount, (usedSurfacesCount-surfaceStore-1)*sizeof(SDL_Rect));
surfaceStore[usedSurfacesCount] = {0, 0, 0, 0};[/src]
 

theSplit

1998
Veteran Barkeeper

Registriert
3 Aug. 2014
Beiträge
28.573
  • Thread Starter Thread Starter
  • #4
Erstmal danke für eure Beiträge.
Memcpy, memmove und Co stehen bei dir im Lehrbuch bzw. im beiligenden PDF, welche eine Übersicht der Standard-Lib gibt (das Buch ist von 2010, Grundkurs C) unter "string.h", nicht jedoch auch als genereller Befehl. :m

Ich hatte es heute morgen so versucht:
[src=c]memmove(&surfaceStore[i-1], &surfaceStore, sizeof(surfaceStore))[/src]

Auf den Index des Arrays mit surfaceStore+i+1 zu zeigen funktioniert allerdings nicht. Ich habs als Array mit fixer Anzahl von deklarierten Elementen deklariert.

@Skipjack:
Aber du hast wohl recht, ich überschreibe den Wert bevor dieser gesichert verschoben wurde.
Genau das Problem hatte ich heute morgen beim testen mit memcpy/memmove so nicht nachvollziehen können. Danke für den Hinweis!

*Edit:
Bin nun nochmals über den Code gegangen und folgendes ist bei rausgekommen:

[src=c]for (int i = selectedIndex; i < usedSurfacesCount; i++) {
memmove(&surfaceStore, &surfaceStore[i+1], sizeof(SDL_Rect));
surfaceStore[i+1] = {0, 0 , 0, 0};
}

usedSurfacesCount--;

for (int i = usedSurfacesCount-1; i > -1; i--) {
printf("surfaces: %d, %d - %dx%d\n", surfaceStore.x, surfaceStore.y, surfaceStore.w, surfaceStore.h);
}

printf("surfaces: %d\n", usedSurfacesCount);[/src]
 
Zuletzt bearbeitet:

Skipjack

Neu angemeldet

Registriert
17 Juli 2013
Beiträge
213
Auf den Index des Arrays mit surfaceStore+i+1 zu zeigen funktioniert allerdings nicht. Ich habs als Array mit fixer Anzahl von deklarierten Elementen deklariert.
surfaceStore+i+1 zeigt nicht auf einen Index, sondern direkt auf das i+Zweite Element.

Wieso sollte das nicht funktionieren? Bei
[src=c]SDL_Rect surfaceStore[10]; /* 10 Elemente */[/src]
wird im Source surfaceStore als Pointer auf den Beginn des Array verstanden (ist also identisch mit &surfaceStore[0]).
Ein
[src=c]memmove(surfaceStore, surfaceStore+2, 2*sizeof(SDL_Rect));[/src]
verschiebt also die Element 3 und 4 auf die ersten beiden Positionen.


Bin nun nochmals über den Code gegangen und folgendes ist bei rausgekommen:

[src=c]for (int i = selectedIndex; i < usedSurfacesCount; i++) {
memmove(&surfaceStore, &surfaceStore[i+1], sizeof(SDL_Rect));
surfaceStore[i+1] = {0, 0 , 0, 0};
}[/src]

Funktioniert, ist aber ineffektiv.
Das Null-Setzen in der Loop kannst Du Dir sparen. Brauchst Du doch nur nach dem Verschieben des letzten Elementes, also hinter der Loop.
Und die Loop um einzelne Elemente per memmove zu verschieben brauchst Du auch nicht, Du kannst doch per memmove direkt alle Verschieben. Dafür war doch memmove anstatt memcpy gedacht, weil sich beim gesamten Verschieben die Speicherbereiche überlappen. Sonst hättest Du auch beim memcpy bleiben können.

Mein Codebeispiel in letzten Posting kommt bereits ohne Loop aus.
 

theSplit

1998
Veteran Barkeeper

Registriert
3 Aug. 2014
Beiträge
28.573
  • Thread Starter Thread Starter
  • #6
Hallo,

wenn ich deinen optimierten Code kompilieren will bekomme ich allerdings einen Fehler von g++ gemeldet:
[src=c]error: invalid operands of types ‘unsigned int’ and ‘SDL_Rect*’ to binary ‘operator-’
memmove(surfaceStore+selectedIndex , surfaceStore+usedSurfaceCount, (usedSurfaceCount-surfaceStore-1)*sizeof(SDL_Rect));[/src]

* die Variable usedSurfacesCount hatte ich abgeändert zu usedSurfaceCount .
Die Stelle (usedSurfaceCount-surfaceStore-1) wird das Problem sein.

Ich hab das ganze aber nun, wie du geschrieben hast, mit einem memmove gemacht und danach gebe ich auch den allokierten Speicherbereich wieder frei:
[src=c]memmove(surfaceStore + selectedIndex, surfaceStore +(selectedIndex+1), sizeof(SDL_Rect)* (usedSurfaceCount - selectedIndex) );
usedSurfaceCount--;
surfaceStore = (SDL_Rect*) realloc(surfaceStore, sizeof(SDL_Rect) * usedSurfaceCount);[/src]

Der Befehl das SDL_Rect *surfaceStore anzulegen und ein surface zu speichern sieht so aus:
[src=c]surfaceStore = (SDL_Rect*) realloc(surfaceStore, sizeof(SDL_Rect) * (usedSurfaceCount+1));
memcpy(surfaceStore+usedSurfaceCount, &draggingRect, sizeof(SDL_Rect));

usedSurfaceCount++;[/src]

Und am Ende der Laufzeit wird der Pointer surfaceStore mit free(surfaceStore) geleert. Wenn ich richtig verstehe sollte auch beim Truncate mittels einer kleineren Speicherzuweisung mit realloc der Speicher der überhängt (beim verkleinern) freigegeben werden?

Vorher hatte ich auch folgende Variante, welche funktionierte aber natürlich auch vermutlich mehr als unnötig gewesen ist (ohne realloc) :D

[src=c]
SDL_Rect *surfaceStore;
SDL_Rect *surfaceStoreShadow;

if (usedSurfaceCount > 0) {
surfaceStoreShadow = (SDL_Rect*) malloc(sizeof(SDL_Rect) * usedSurfaceCount);
memcpy(surfaceStoreShadow, surfaceStore, sizeof(SDL_Rect)*usedSurfaceCount);
free(surfaceStore);
}

surfaceStore = (SDL_Rect*) malloc(sizeof(SDL_Rect) * (usedSurfaceCount+1));
if (usedSurfaceCount > 0) {
memcpy(surfaceStore, surfaceStoreShadow, sizeof(SDL_Rect) * usedSurfaceCount);
}

memcpy(&surfaceStore[usedSurfaceCount], &draggingRect, sizeof(SDL_Rect));

if (usedSurfaceCount > 0) {
free(surfaceStoreShadow);
}

usedSurfaceCount++;[/src]

Aber unterm Strich:
Hab schon wieder etwas gelernt, vielen Dank für deinen Beitrag! :T
 
Zuletzt bearbeitet:

Skipjack

Neu angemeldet

Registriert
17 Juli 2013
Beiträge
213
wenn ich deinen optimierten Code kompilieren will bekomme ich allerdings einen Fehler von g++ gemeldet:
[...]Die Stelle (usedSurfaceCount-surfaceStore-1) wird das Problem sein.
Stimmt, da hätte statt surfaceStore wohl eher irgendwas mit selectedIndex stehen müssten. Das ist das Problem, wenn man den Code nur schreibt und nicht selber validiert.

Aber Du hast es ja hinbekommen.

Aber unterm Strich:
Hab schon wieder etwas gelernt, vielen Dank für deinen Beitrag! :T
Gern geschehen.
Einen hätte ich noch: ich würde im Code wenn möglich auf sizeof(<type>) verzichten und als Argument eher Variablen/Pointer nehmen. Also in Deinem Beispiel
[src=c]surfaceStore = (SDL_Rect*) realloc(surfaceStore, sizeof(surfaceStore[0]) * (usedSurfaceCount+1));
memcpy(surfaceStore+usedSurfaceCount, &draggingRect, sizeof(draggingRect));
[/src]
Das hat den Vorteil, wenn Du nachher mal aus irgendeinem Grund den Typ änderst, must Du nicht dafür sorgen, dass überall die sizeof() nachgezogen werden.
Vergisst Du nämlich das, dann knallt es hinterher füchterlich. ;)
 

theSplit

1998
Veteran Barkeeper

Registriert
3 Aug. 2014
Beiträge
28.573
  • Thread Starter Thread Starter
  • #8
Eine Frage zum Thema Memory Management muß ich doch jetzt in den Raum werfen, oder vielleicht doch ein anderer Thread... naja.

Ich habe jetzt mal ein wenig mit struct gewerkelt und dabei auch festgestellt das bereits während dieser ersten Defnition mit sizeof(struct name) festgestellt werden kann wie viel Speicher für die Definition verwendet wird und dies nicht zwangsläufig aus den Membern/Attributen hervorgeht (also nicht int + int + char[25] ), sondern die Werte außerhalb der Member-Definition liegen können.

So weit so gut, aber was mir gerade auch völlig das Genick bricht ist folgender Ansatz:
[src=c]// Structure definitions
struct label {
char text[128];
};

struct group {
unsigned int length;
unsigned int *items;
label *labels;
};[/src]

Für die Struktur group werden auf meinem 64bit System schonmal 24 Byte reserviert, für ein Wert vom Typ Label 128 Byte (wie nach Definition).
So weit so gut.

Aber wie schaffe ich es, das ich nun Speicher für die Struktur mit malloc/calloc anlegen lasse und die Member items und labels erweitern kann?
Ich bekomme jedesmal "Speicherzugriffsfehler" nach der Wertzuweisung bzw. funktionierte die Wertzuweisung und das Auslesen, aber trotz keinem Zugriff anderer Funktionen auf die Daten erhielt ich Sekunden später wieder einen Speicherzugriffsfehler :unknown:

Laut Logik müsste ich doch einmal den Speicherbedarf für das struct reservieren plus für die unsigned int Werte items und das char Array Labels mittels (struct labels) (mal Anzahl der neuen Elemente?) ?

Habe parallel auch mal hier geschaut http://c.learncodethehardway.org/book/ex16.html - aber hier wird das char Array mit einem Wert initialisiert, ohne Limit anzugeben und Speicher nur in Größe der Struktur angelegt? Beziehungsweise über die Übergabe durch create_Person, ... Ist für mich nur bedingt nachvollziehbar. :(

Über Tips/Hinweise wäre ich sehr dankbar!

*Nachtrag:
Hab es nun so gelöst, scheinbar komme ich ohne Limits bzw. vordefenieren der Arrays in den Structs nicht weiter:
[src=c]// Structure definitions
typedef struct surface {
bool isInGroup = false;
unsigned int groupIndex = 0;
char *label;
SDL_Rect rect = {0, 0, 0, 0};
};

typedef struct group {
unsigned int length = 0;
unsigned int items[128];
};[/src]

Das Label kann ich auch setzen wie ich möchte, jedoch verstehe ich nicht wie intern das char Array geregelt wird..
Gilt hierfür automatisch die Grenze von <limits.h> ?
Irgendwo hatte ich gelesen das ein struct immer in einem durchgehenden Speicherraum abgelegt wird, aber dann wird mir unklar wie ohne Limit für das char-Array label , bei festen Wert von [64] Zeichen zum Beispiel müsste ich ja auffüllen, wie die Flexibilität hier zu stande kommt ohne das die Größe festgelegt wird. Mit sizeof bekomme ich einen Wert von 8 Byte geliefert.
 
Zuletzt bearbeitet:

Brother John

(schein)heilig
Veteran

Registriert
1 Aug. 2013
Beiträge
235
Laut Logik müsste ich doch einmal den Speicherbedarf für das struct reservieren plus für die unsigned int Werte items und das char Array Labels mittels (struct labels) (mal Anzahl der neuen Elemente?) ?
Im Prinzip ja. Um Platz für 42 labels und items zu reservieren, müsste es hiermit klappen:
Code:
group grp;
grp.length = 42;
grp.items = (unsigned int*)calloc(grp.length, sizeof(unsigned int));
grp.labels = (label*)calloc(grp.length, sizeof(label));
Zugreifen kannst du dann mit der normalen Array-Notation:
Code:
for (int i = 0; i < grp.length; ++i {
    grp.items[i] = i;
    printf("%d\n", grp.items[i]);
}
Sollte die Zahlen von 0 bis 41 ausgeben.
 

theSplit

1998
Veteran Barkeeper

Registriert
3 Aug. 2014
Beiträge
28.573
  • Thread Starter Thread Starter
  • #10
Stimmt, so haut es hin. Das freigeben des Speichers für jeden dieser Pointer sollte man nur nicht vergessen.. :)

War mir nicht ganz klar gewesen, ich habe das neben deinem Beitrag auch hier nochmal lesen müssen C structs und Pointers , es muß Speicher für das struct laut Definition allokiert werden plus extra Speicher für die Pointer des Structs. Irgendwie war ich davon ausgegangen dass der einzige Arbeitsschritt gewesen ist, Speicher für die Struct instanz zu erweitern und dies "automatisch" Speicher für die Member freigibt...

Vielen Dank für die Aufklärung das dem nicht so ist! :)
 

Brother John

(schein)heilig
Veteran

Registriert
1 Aug. 2013
Beiträge
235
Ja klar höchstpersönlich anfordern und freigeben. Für nettes Automatik-Ressourcenmanagement müsstest du an das C noch ein ++ drantun. ;)
 

theSplit

1998
Veteran Barkeeper

Registriert
3 Aug. 2014
Beiträge
28.573
  • Thread Starter Thread Starter
  • #12
Ich versuche mich erstmal daran die C Standard-Bibliothek kennen zu lernen so gut es geht. Wenn ich selbige Verstanden habe würde ich mir überlegen mit C++ anzufangen. :)

Aber ich hab schon gemerkt das solche Details ganz schön ausbremensen und relativ kompliziert sind - also mal zu überlegen wie man etwas deklariert, welchen Datentyp man einer Variable verpasst und jetzt auch wie man Daten in Einheiten strukturiert mit dem struct.

Habe die Struktur jetzt auch nochmal aufgebrochen weil es unnötig ist der Gruppe die Labels zu geben...
[src=c]// Structure definitions
typedef struct surface {
bool isGrouped = false;
unsigned int groupIndex = 0;
char label[128];
SDL_Rect rect;
};

typedef struct group {
unsigned int length;
unsigned int *items;
};[/src]

Auch interessant das sich in for-Schleifen mehrerer Elemente initiatiliseren und berechnen lassen, sowas kannte ich bisher gar nicht bzw. bin ich nicht auf solche Features aufmerksam geworden.

Aber erstmal jetzt die Basics und dann hoffentlich noch etwas mehr in SDL Funktionen tauchen (mein Code besteht bei 700 Zeilen aus quasi nur 20 Zeilen SDL Funktionen, der Rest ist reines C. Habe nur gemerkt das es enorm viel Zeit gekostet hat bis ich wirklich jetzt an dem Punkt bin wo man die Basics, aus dem Programm was ich als Vorlage genommen habe, wirklich schon verwenden könnte.

Da die ursprüngliche Umsetzung in Javascript gewesen ist, konnte man auch nicht mit Sachen wie Dateien arbeiten um Inhalte zu speichern, das geht zwar auch in anderen Sprachen, aber das dann noch in C in Angriff zu nehmen reizt mich schon. Vor allem mal weg von dem Webinterface bzw. dem Browser der zwar viele Funktionalitäten bietet aber aus Sicherheitsgründen (ohne Firefox OS oder Android zu nennen), um ein Beispiel zu nennen, keine File-Api bietet.

Aber wird noch eine schöner Weg das Projekt umzusetzen! Lernen konnte ich schon eine Menge und C ist gar nicht so das Monster für das man es hält (ohne jetzt den Luxus von C++ zu kennen).

*Edit:
Habe gerade Valkyrie für mich entdeckt, welches ein Interface für Valgrind ist und Memory Leaks aufdeckt, hab auch schon Fehler in meinem Code beheben können damit, echt klasse was es alles gibt :)
 
Zuletzt bearbeitet:

Skipjack

Neu angemeldet

Registriert
17 Juli 2013
Beiträge
213
Ich habe jetzt mal ein wenig mit struct gewerkelt und dabei auch festgestellt das bereits während dieser ersten Defnition mit sizeof(struct name) festgestellt werden kann wie viel Speicher für die Definition verwendet wird und dies nicht zwangsläufig aus den Membern/Attributen hervorgeht (also nicht int + int + char[25] ), sondern die Werte außerhalb der Member-Definition liegen können.
Das Ganze kann grösser sein als die Summe seiner Teile (Quantenmechanik in C ;)).
Es liegt daran, wie Datenstrukturen auf der jeweiligen Plattform verwaltet werden. Normalerweise werden die primitiven Datentypen hintereinander "aligned" abgelegt, also immer an 64Bit Grenzen.
Ein Pointer braucht 64Bit (also 8 Byte) weil der Adressraum so gross ist, ein unsigned char (UC) nur ein Byte. Aber eine struct aus einem Pointer und einem UC hat als sizeof trotzdem 2*8Byte, da auch der UC in einem 64Bit Block verwaltet wird. Eine struct aus einem Pointer und zwei UC wäre auch 16Byte gross, da beide UC in den 64Bit Block passen. Definierst Du die struct anders (ein UC, ein Pointer, ein UC) braucht sie plötzlich 3*8Byte.
Ändern könnte man das durch ein Compiler-Direktive namens "#pragma pack", dann würde die letzte struct nur noch 10 Byte gross sein - aber man muss mit Performanceverringerung rechnen.

Letztlich ist die struct also "mindestens" so gross, wie die Summe ihrer Member.

So weit so gut, aber was mir gerade auch völlig das Genick bricht ist folgender Ansatz:
[...]
Aber wie schaffe ich es, das ich nun Speicher für die Struktur mit malloc/calloc anlegen lasse und die Member items und labels erweitern kann?
Hast Du wahrscheinlich mittlerweile rausgefunden - den Speicher musst Du selber anlegen.
Wenn in der Struct nur ein Pointer ist, dann hast Du Platz für diesen Pointer, aber nicht für den Bereich, auf den er zeigen kann. Dieser Bereich ist noch extra anzulegen und der Pointer in der struct darauf zu setzen.

Habe parallel auch mal hier geschaut http://c.learncodethehardway.org/book/ex16.html - aber hier wird das char Array mit einem Wert initialisiert, ohne Limit anzugeben und Speicher nur in Größe der Struktur angelegt? Beziehungsweise über die Übergabe durch create_Person, ... Ist für mich nur bedingt nachvollziehbar. :(

Über Tips/Hinweise wäre ich sehr dankbar!
Gerne, aber was genau ist denn darin nicht nachvollziehbar?
Das macht die Funktion strdup - ein char Array ist null-terminiert, d.h. das letzte Byte ist 0x00 (und das ist auch das einzige Byte im Array, dass diesen Wert haben kann). So kann die Länge des Arrays bestimmt werden, indem man alle Bytes vom Anfang bis zum ersten 0x00 zählt. Das macht z.B. die Funktion strlen(). Beim Übergeben im Beispiel von "Joe Alex" wird also ein Pointer auf den Anfang dieses Arrays (liegt als Konstante im Programmcode) übergeben (also auf die Adresse des Buchstabens J). Die Konstante ist tatsächlich 9 Bytes lang und das letzte Byte (hinter x) ist 0x00. strdup zählt die Bytes bis zur 0x00, allokiert dann 9 Byte Speicher, kopiert diese 9 Byte ("Joe Alex" + 0x00) und gibt den Pointer darauf zurück - was dann unter dem Pointer name gespeichert wird.
Deswegen wird das in der destroy Funktion ja auch wieder freigegeben.

--- [2015-04-13 17:13 CEST] Automatisch zusammengeführter Beitrag ---

@Brother John:
Naja, Automatik hat man bei C++ aber per se auch noch nicht.
Speicher muss immer noch allokiert und wieder freigeben werden. Einziger Vorteil ist, dass man es in Konstuktoren/Destruktoren machen kann, die automatisch aufgerufen werden. Aber codieren muss man es immer noch.
 

theSplit

1998
Veteran Barkeeper

Registriert
3 Aug. 2014
Beiträge
28.573
  • Thread Starter Thread Starter
  • #14
Das Ganze kann grösser sein als die Summe seiner Teile (Quantenmechanik in C ;)).
Es liegt daran, wie Datenstrukturen auf der jeweiligen Plattform verwaltet werden. Normalerweise werden die primitiven Datentypen hintereinander "aligned" abgelegt, also immer an 64Bit Grenzen.
Ein Pointer braucht 64Bit (also 8 Byte) weil der Adressraum so gross ist, ein unsigned char (UC) nur ein Byte. Aber eine struct aus einem Pointer und einem UC hat als sizeof trotzdem 2*8Byte, da auch der UC in einem 64Bit Block verwaltet wird. Eine struct aus einem Pointer und zwei UC wäre auch 16Byte gross, da beide UC in den 64Bit Block passen. Definierst Du die struct anders (ein UC, ein Pointer, ein UC) braucht sie plötzlich 3*8Byte.
Ändern könnte man das durch ein Compiler-Direktive namens "#pragma pack", dann würde die letzte struct nur noch 10 Byte gross sein - aber man muss mit Performanceverringerung rechnen.
Letztlich ist die struct also "mindestens" so gross, wie die Summe ihrer Member.

Nur um das nochmal nachzuvollziehen - auch die simple Reihenfolge in welcher die Member in einem Struct definiert werden kann den Speicherverbrauch über das Minimum heraus erhöhen und gibt gleichzeitig den Speicheraufbau vor?
Dann gehe ich nochmal über die Anordnung der Structs, wäre dann ja logisch die Pointer ans Ende zu legen und statische Member vorne in der Definition abzulegen damit keine Fragmentierung entsteht und Daten unsinnig verschoben werden müssen(?) :T

Gerne, aber was genau ist denn darin nicht nachvollziehbar?
Das macht die Funktion strdup - ein char Array ist null-terminiert, d.h. das letzte Byte ist 0x00 (und das ist auch das einzige Byte im Array, dass diesen Wert haben kann). So kann die Länge des Arrays bestimmt werden, indem man alle Bytes vom Anfang bis zum ersten 0x00 zählt. Das macht z.B. die Funktion strlen(). Beim Übergeben im Beispiel von "Joe Alex" wird also ein Pointer auf den Anfang dieses Arrays (liegt als Konstante im Programmcode) übergeben (also auf die Adresse des Buchstabens J). Die Konstante ist tatsächlich 9 Bytes lang und das letzte Byte (hinter x) ist 0x00. strdup zählt die Bytes bis zur 0x00, allokiert dann 9 Byte Speicher, kopiert diese 9 Byte ("Joe Alex" + 0x00) und gibt den Pointer darauf zurück - was dann unter dem Pointer name gespeichert wird.
Deswegen wird das in der destroy Funktion ja auch wieder freigegeben.

Um das nochmal in eigene Worte zu kleiden was du gerade geschrieben hast. Wenn der Pointer anglegt wird, enthält er keine Daten so fern nicht initilaisiert.
strdup aber allokiert dann den Speicher und orientiert sich an "\0" Ende des char Arrays um die Anzahl festzustellen. Mit free(pointer) wird dann der Speicher wieder freigeben. Verstehe ich - danke für die Klärung.

Meine vorherigen Frage hatte Brother John geklärt - mir war nicht bewusst das man Speicher für das struct erfragen muß + Extra Speicher für die Pointer(inhalte).
Irgendwie war ich vorher auf dem falschen Dampfer das ich doch einfach nur sizeof(struct name) schreiben kann, was "automatisch" bestimmt wie groß.... aber war halt nicht gerade genug gedacht.

------------------

Ich hab gestern schon mal zwischendurch gefragt, aber... :

Ich sitze nun an einem Memory Leak bzw. wird ein einmalig falsche Read (laut Valgrind/Valkyrie) ausgeführt den ich bisher nicht beheben konnte, aber genau die Problemstelle im Code kenne ich die diesen Read ausführt - weiß aber nicht warum der Fehler nur einmal auftritt und danach scheinbar reibungslos funktioniert.

Vielleicht mag sich da jemand mal die Mühe machen in den Code zu schauen?
Ich habe das Projekt auch auf Github hochgeladen, mit Source und Makefile.

Zu finden unter: https://github.com/jrie/relayx-sdl

Das Projekt kann mit g++ und "make all" kompiliert werden, SDL 2.0.4 (latest development build) ist allerdings erforderlich.

Der Fehler passiert in Zeile:
360 bis 374, konkret der "memmove" in Zeile 363.

Um die Funktion zu triggern die für das Ungrouping zuständing ist und leere Gruppen überschreiben sollen mit oberen memmove, geht man wie folgt vor.
Mit gedrückter linker Maustaste eine Auswahl ziehen um einen Container anzulegen. Das wiederholen, bis man insgesamt vier Container erstellt hat.
Dann einen Container markieren und mit Strg (halten) + Klick auf einen anderen gruppieren.
Dann den dritten freien auswählen und mit dem vierten Container gruppieren.

Nun klickt man mit Links auf einen der ersten beiden gruppierten Container und hält Strg und klickt nochmals auf diesen.
Der Container wird dann aus der Gruppe gelöscht, und da die Gruppe nur 2 Container hat wird diese auch gelöscht.

Jetzt wurde der Fehler (memmove) schon getriggert. Ein "invalid read von 8 Bytes" (64 bit) im Valkyrie Statusfenster.

Wiederholt man das ganze, Gruppe anlegen, vorherigen Gruppe an Stelle 0 löschen, tritt der Fehler nicht mehr auf.
Kein Invalid Read oder Write zu sehen.

Vielleicht liegt der Fehler auch nur dabei, wie der Speicher berechnet wird (gespeichert in bytesToMove), aber bei darauffolgenden auflösen der ersten Gruppe in der Reihenfolge kommt kein Fehler mehr.
 
Zuletzt bearbeitet:

Brother John

(schein)heilig
Veteran

Registriert
1 Aug. 2013
Beiträge
235
@theSplit:
Mir springt bei dem Stück Code mit dem invalid read nichts direkt ins Auge. Dass es auf 64bit um 8 Byte geht, und zwar nur beim ersten Durchlauf, lässt mich vermuten, dass sich irgendwo ein uninitialisierter Pointer versteckt. Vielleicht könnte es auch ein Off-by-one-Fehler bei der ganzen Pointerarithmetik sein; dann würde ich intuitiv aber erwarten, dass der gleiche Fehler jedesmal anschlägt.

<leicht offtopic>
@Skipjack:
Doch, in C++ hat man per se Automatik. Persönlich hinschreiben muss man es nur im Ausnahmefall, das ist doch gerade der Sinn der Sache. Du kannst natürlich bewusst den ganzen Mechanismus aushebeln, z.B. indem du ausdrücklich mit owning Raw-Pointer hantierst. Sinnvoll ist das nur mit gutem Grund. Den ursprünglichen struct
Code:
struct group {
    unsigned int length;
    unsigned int *items;
    label *labels;
};
würde ich naiv 1:1 übersetzt erstmal so hinschreiben:
Code:
struct group {
    unsigned int length;
    std::vector<unsigned int> items;
    std::vector<label> labels;
};
Weder Ctor/Dtor noch Ressourcenmanagementcode weit und breit und trotzdem werden die beiden Listen korrekt her- und weggeräumt. Natürlich *existiert* der Mgt-Code, aber er steckt in der Standard Library oder ist vom Compiler generiert. Wichtig ist, dass ich nichts davon selbst machen muss.
</leicht offtopic>

Autsch, verzeiht mir bitte das geballte Denglisch …
 

Timon3

Team ModMii

Registriert
17 Juli 2013
Beiträge
499
@Brother John: Nur weil eine Bibliothek (bzw. die Standardbibliothek) die in einem Modell das Speichermanagement implementiert, bedeutet das ja nicht, dass man das generell in der Sprache nicht braucht. Ich kann mir auch beispielsweise einen Vektor in C implementieren (Beispielcode) und muss manuell nicht mehr aufräumen, weil die Klasse das übernimmt. Genauso, wenn ich mir in C++ selber was implementiere.
 

accC

gesperrt

Registriert
14 Juli 2013
Beiträge
5.250
Wow, das sind ja riesige if-else-Schachteln, ziemlich wenig Kommentare und ziemlich ähnliche Variablen. Mein C ist nicht so gut, ich bezweifele, dass ich da jetzt einen Fehler finden werde. Die Empfehlung, den Quellcode zur Verfügung zu stellen, war eher genereller Natur, da du damit anderen, die weit mehr Erfahrung mit C haben, als ich, die Möglichkeit gibst, den Fehler zu finden.
 

Skipjack

Neu angemeldet

Registriert
17 Juli 2013
Beiträge
213
In C++ würde dein Vector-Objekt irgendwann out-of-scope gehen. In dem Moment läuft automatisch ohne weiteres Zutun der Destruktor. Das kriegst du in C mit keiner Konstruktion hin.
Ein Objekt, das ich mit new anlege geht aber erst mit Beendigung des Programms "out-of-scope", ansonsten musst Du es mit delete zerstören (analog zu *alloc/free) - das ruft dann natürlich "automatisch" den Destruktor auf. An dieser Stelle ist der "einzige" Vorteil von C++, dass man die Konstruktor/Destruktor Routinen nicht explizit aufrufen muss - codiert werden müssen sie aber (bei einer eigenen Klasse) trotzdem.

--- [2015-04-14 09:23 CEST] Automatisch zusammengeführter Beitrag ---

Nur um das nochmal nachzuvollziehen - auch die simple Reihenfolge in welcher die Member in einem Struct definiert werden kann den Speicherverbrauch über das Minimum heraus erhöhen und gibt gleichzeitig den Speicheraufbau vor?
Ja.
Früher war das durchaus wichtig. Ich hatte schon mit Systemen zu tun, wo jedes Byte Arbeitsspeicher kostbar war (wusstest Du, dass if-then-else grösseren Code erzeugt als dieselbe Logik mit dem Bedinungsoperator ?) - aber heute ... wenn Du nicht gerade Milliarden solcher Structs anlegst - was machen da ein paar 100KB Speicherverbrauch mehr oder weniger schon aus.

Dann gehe ich nochmal über die Anordnung der Structs, wäre dann ja logisch die Pointer ans Ende zu legen und statische Member vorne in der Definition abzulegen damit keine Fragmentierung entsteht und Daten unsinnig verschoben werden müssen(?) :T
Wieso Fragmentierung?
Fragmentierung ist, wenn Du viele mallocs und frees machst und dann irgendwann sich allokierte und unbenutze Speicherbereiche wild abwechseln. Aber innerhalb der Struct - dann sind da halt ein paar unbenutzte Bytes.
Wieso unsinnig verschoben?

Mach Dir mal keine Gedanken. Pack die Elemente in Deine Structs wie es Dir in den Sinn kommt und gut.

Vielleicht mag sich da jemand mal die Mühe machen in den Code zu schauen?
Ganz ehrlich: Nein, da wühle ich mich nicht durch.

Was Bruder John schon gesagt hat.
Du solltest den Code besser strukturieren, diese endlos geschachtelten if-Schleifen - grauenvoll, mach mal ein paar Subroutinen.

Und vom Design: dieses ewige memmove und realloc ist auch ein Performancekiller. Wenn es wichtig ist, sollte man solche Funktionen nur mit Bedacht einsetzen.
Anscheinend geht es ja vor allem darum, Elemente an die Arrays anzufügen (realloc) bzw. zu entfernen (memmove nach vorne). Du solltest mal überlegen, ob dies mit (doppelt-)verketteten Listen nicht viel eleganter und fehlerunanfälliger zu lösen wäre.
Definier Dir ein
[src=c]typedef struct item {
unsigned int value;
struct item * prev;
struct item * next;
} item;[/src]
analog zum Beispiel in Algorithmen_und_Datenstrukturen_in_C
Die Hilfsfunktionen kannst Du direkt davon kopieren.
 

BurnerR

Bot #0384479

Registriert
20 Juli 2013
Beiträge
5.507
Ein Objekt, das ich mit new anlege geht aber erst mit Beendigung des Programms "out-of-scope", ansonsten musst Du es mit delete zerstören (analog zu *alloc/free) - das ruft dann natürlich "automatisch" den Destruktor auf. An dieser Stelle ist der "einzige" Vorteil von C++, dass man die Konstruktor/Destruktor Routinen nicht explizit aufrufen muss - codiert werden müssen sie aber (bei einer eigenen Klasse) trotzdem.
John nimmt da Bezug auf die Containerklassen aus der STL. Der Vektor verwaltet seine Daten und gibt sie automatisch frei wenn es out-of-scope geht. Das selbe gilt für die Smartpointer. Man kann heute in C++ sauberen Code ohne Speicherlecks schreiben ohne ein einziges mal explizit delete zu verwenden insbesondere auch unter Verwendung von (smart) pointer.
 
Oben