delete void* ?
-
Wenn ich folgendes mache:
void* myBool = new bool; delete myBool;
- wird der speicherplatz, der für bool angefordert wird auch korrekt gelöscht?
Und dazu auch gleich: Wenn void (nicht void*) doch eigentlich gar nichts speichern kann, warum ist es dann 4 bytes groß ( sizeof(void) == 4 ) ?rein logisch gesehen dürfte das o.g. nicht ordentlich funktionieren, schließlich kann delete nicht sehen, wieviel speicher es löschen muss (void* könnte ja auch auf einen long double, welches wesentlich mehr speicher benötigt als ein bool benötigt zeigen).
Liege ich damit korrekt?Ansonsten bleibt dann nur noch eine lösch funktion.
-
Krauzi schrieb:
Wenn ich folgendes mache:
void* myBool = new bool; delete myBool;
- wird der speicherplatz, der für bool angefordert wird auch korrekt gelöscht?
Und dazu auch gleich: Wenn void (nicht void*) doch eigentlich gar nichts speichern kann, warum ist es dann 4 bytes groß ( sizeof(void) == 4 ) ?Da musst du aufpassen. Ein Zeiger auf void ist ebenfalls nur ein Zeiger und ist daher so gross, wie jeder andere Zeiger des Systemes auch. void und ein void Zeiger darfst du nicht durcheinander bringen. Die haben nicht sehr viel miteinander gemeinsam.
rein logisch gesehen dürfte das o.g. nicht ordentlich funktionieren, schließlich kann delete nicht sehen, wieviel speicher es löschen muss (void* könnte ja auch auf einen long double, welches wesentlich mehr speicher benötigt als ein bool benötigt zeigen).
Liege ich damit korrekt?Genau das ist das Problem. Darum geht es nicht.
-
drakon schrieb:
Wenn ich folgendes mache:
Da musst du aufpassen. Ein Zeiger auf void ist ebenfalls nur ein Zeiger und ist daher so gross, wie jeder andere Zeiger des Systemes auch. void und ein void Zeiger darfst du nicht durcheinander bringen. Die haben nicht sehr viel miteinander gemeinsam.Ja void und void* ist was sehr unterschiedliches, das ist mir schon bewusst, aber warum ist void denn 4 bytes groß? Hat das compilertechnische gründe oder wenn nicht, welche dann?
-
-.- lol
Edit: Ich kann kein Sizeof(void) ausgeben. Wird mit: Error C2070: 'void': illegal sizeof operand abgebrochen.
-
Ein Pointer ist ein Pointer. Jeder Pointer ist 4 Bytes groß (auf den meisten Compilern!), da dort _nur_ die Adresse gespeichert wird. Nicht mehr, nicht weniger. Ein
void*
kann zum Beispiel den Wert0x123456
beinhalten (das zu adressierende Objekt wäre somit im virtuallen Speicher an der Position0x123456
zu finden). Trotzdem kann an0x123456
ein völlig anderes Objekt liegen.Beispiel:
int mynum = 566; void *ptr = static_cast<void*>(&mynum); // äquivalent zu int *ptr = &mynum; // ptr ist 4 Bytes groß, *ptr - also das int selber - ist je nach Compiler auch 4 oder mehr Bytes groß cout << *ptr << endl; // 566
Gruß
-
Krauzi schrieb:
drakon schrieb:
Wenn ich folgendes mache:
Da musst du aufpassen. Ein Zeiger auf void ist ebenfalls nur ein Zeiger und ist daher so gross, wie jeder andere Zeiger des Systemes auch. void und ein void Zeiger darfst du nicht durcheinander bringen. Die haben nicht sehr viel miteinander gemeinsam.Ja void und void* ist was sehr unterschiedliches, das ist mir schon bewusst, aber warum ist void denn 4 bytes groß? Hat das compilertechnische gründe oder wenn nicht, welche dann?
Warum ist ein Zeiger auf
int
4 Byte gross? Gleiche Frage, gleiche Antwort.
Das ist einfach Systembedingt. Es muss Speicher adressiert werden und dafür braucht es halt eine bestimme Anzahl an Bytes (welche natürlich Implementierungsspezifisch ist) und die ist für jeden Typen gleich. Der Speicher ist für jede "Speicherzelle" gleich und daher braucht es auch immer gleich viel, um ihn zu adressieren.
-
ihr braucht mir nicht zu erklären, was ein zeiger ist
Ich wollte eine erklärung, warum void bei mir 4 bytes groß ist.
Aber das hat sich glaube ich erledigt
sizeof(void) hat nur ROOT geklappt (meta compiler von CERN), beim compile versuch mit VC++ schlug schon dieser fehl.Damit hat sich die frage erledigt (offenbar ist sizeof(void) in root definiert [wegen compile technischen gründen]).
EDIT:
theliquidwave schrieb:
Ein Pointer ist ein Pointer. Jeder Pointer ist 4 Bytes groß
int mynum = 566; void *ptr = static_cast<void*>(&mynum); // äquivalent zu int *ptr = &mynum; // ptr ist 4 Bytes groß, *ptr - also das int selber - ist je nach Compiler auch 4 oder mehr Bytes groß cout << *ptr << endl; // 566
ich wage zu bezweifeln, dass das zum compilen geht. Der compiler kann kaum wissen, wie weit denn das ptr objekt im speicher geht (da es keinen datentyp hat). (int)ptr würde gehen.
-
Krauzi schrieb:
rein logisch gesehen dürfte das o.g. nicht ordentlich funktionieren, schließlich kann delete nicht sehen, wieviel speicher es löschen muss (void* könnte ja auch auf einen long double, welches wesentlich mehr speicher benötigt als ein bool benötigt zeigen).
Liege ich damit korrekt?Nach der Argumentation würde Folgendes unweigerlich in einem Memory Leak oder undefiniertem Verhalten enden. Wir wissen aber, dass dies nicht der Fall ist.
int* ptr = (rand() > RAND_MAX/2) ? new int[2] : new int[7]; delete[] ptr; // "delete[] kann nicht wissen, wieviel Speicher es freigeben muss".
drakon schrieb:
Genau das ist das Problem. Darum geht es nicht.
Nein, wie gesagt ist das nicht das Problem. Man muss sich nur vorstellen, wie
delete
funktioniert: Es ruft denoperator delete
auf, der lediglich einenvoid*
bekommt. Also hat man hier auch schon keine Typinformation mehr, und trotzdem istoperator delete
in der Lage, den Speicher korrekt freizugeben. Die C++-Laufzeitumgebung speichert die vergebenen Adressen und vergleicht diese im Falle einer Freigabe mit dem Argument.Das tatsächliche Problem besteht darin, dass keine Destruktoren aufgerufen werden, wenn
delete
aufvoid*
angewendet wird. Hier zwar nicht, aber für den Grossteil von C++ wäre diese Situation problematisch.Warum hier überhaupt
void*
verwendet werden muss, sehe ich bei der Auswahl an C++-Alternativen nicht ganz ein.
-
Nexus schrieb:
Warum hier überhaupt
void*
verwendet werden muss, sehe ich bei der Auswahl an C++-Alternativen nicht ganz ein.Weil vermutlich wohl eher ums grundlegende Verständnis denn um den praktischen Nutzen geht.
-
Krauzi schrieb:
Wenn ich folgendes mache:
void* myBool = new bool; delete myBool;
hast du undefiniertes Verhalten.
-
camper schrieb:
Krauzi schrieb:
Wenn ich folgendes mache:
void* myBool = new bool; delete myBool;
hast du undefiniertes Verhalten.
Deswegen warnt dich der Compiler ja auch...
test.cpp:24: warning: deleting ‘void*’ is undefined
-
Nexus schrieb:
Krauzi schrieb:
rein logisch gesehen dürfte das o.g. nicht ordentlich funktionieren, schließlich kann delete nicht sehen, wieviel speicher es löschen muss (void* könnte ja auch auf einen long double, welches wesentlich mehr speicher benötigt als ein bool benötigt zeigen).
Liege ich damit korrekt?Nach der Argumentation würde Folgendes unweigerlich in einem Memory Leak oder undefiniertem Verhalten enden. Wir wissen aber, dass dies nicht der Fall ist.
int* ptr = (rand() > RAND_MAX/2) ? new int[2] : new int[7]; delete[] ptr; // "delete[] kann nicht wissen, wieviel Speicher es freigeben muss".
Aus dem kontext gerissen stimmt dann meine argumentation nicht, das ist richtig.
Ich habe mich in meiner argumentation auf das beispiel bezogen (welches mit void* war) und genau in diesem fall ist das verhalten des programms dann undefiniert und es gibt auch memory leaks.
-
camper schrieb:
hast du undefiniertes Verhalten.
Nexus schrieb:
Nein, wie gesagt ist das nicht das Problem. Man muss sich nur vorstellen, wie
delete
funktioniert: Es ruft denoperator delete
auf, der lediglich einenvoid*
bekommt. Also hat man hier auch schon keine Typinformation mehr, und trotzdem istoperator delete
in der Lage, den Speicher korrekt freizugeben. Die C++-Laufzeitumgebung speichert die vergebenen Adressen und vergleicht diese im Falle einer Freigabe mit dem Argument.Das tatsächliche Problem besteht darin, dass keine Destruktoren aufgerufen werden, wenn
delete
aufvoid*
angewendet wird.Wenn ich Nexus richtig verstehe sagt er, dass das Verhalten sehr wohl definiert ist,
nämlich so, dass keine Destruktoren aufgerufen werden.
Demanch finde ich die Warning auch ein bisschen zweifelhaft.#include <iostream> using namespace std; class Foo { public: ~Foo() { cout << endl << "Destruktor" << endl; } }; int main() { void* vp = new Foo[2]; delete[] vp; // keine Destruktoren werden aufgerufen. system("pause>nul"); return 0; }
-
Unglaublich das man eine einfache Frage so aufbauschen kann 0o .
Das Betriebssystem speichert bei einer solchen Speicheranforderung die Größe des reservierten Blocks ab und weiss deswegen beim delete genau wie viel Speicher es freizugeben hat. So schwierig?
-
5.3.5 Delete [expr.delete]
1 The delete-expression operator destroys a most derived object (1.8) or array created by a new-expression. delete-expression:
::opt delete cast-expression
The first alternative is for non-array objects, and the second is for arrays. The operand shall have a pointer type, or a class type having a single conversion function (12.3.2) to a pointer type. The result has type
void.
2 If the operand has a class type, the operand is converted to a pointer type by calling the above-mentioned conversion function, and the converted operand is used in place of the original operand for the remainder of this section. In either alternative, if the value of the operand of delete is the null pointer the operation has no effect. In the first alternative (delete object), the value of the operand of delete shall be a pointer to a non-array object or a pointer to a sub-object (1.8) representing a base class of such an object (clause 10). If not, the behavior is undefined. In the second alternative (delete array), the value of the operand of delete shall be the pointer value which resulted from a previous array new-expression.72) If not, the behavior is undefined. [Note: this means that the syntax of the delete-expression must match the type of the object allocated by new, not the syntax of the new-expression. ] [Note: a pointer to a const type can be the operand of a delete-expression; it is not necessary to cast away the constness (5.2.11) of the pointer expression before it is used as the operand of the delete-expression. ]
3 In the first alternative (delete object), if the static type of the operand is different from its dynamic type, the static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual destructor or the behavior is undefined. In the second alternative (delete array) if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined.73)delete auf eine void*-Zeiger ist somit immer undefiniert (mit Ausnahme des Nullzeigers), weil der dynamische Typ des Objektes niemals mit dem statischen Typ (void) übereinstimmen kann und void keine Basisklasse sein kann.
- This implies that an object cannot be deleted using a pointer of type void* because there are no objects of type void.
-
Dweb schrieb:
Wenn ich Nexus richtig verstehe sagt er, dass das Verhalten sehr wohl definiert ist
Anscheinend habe ich bei meiner Überlegung ein kleines Detail übersehen.
Wahrscheinlich besteht der Grund, dass der
delete
-Operator Typinformation benötigt, unter anderem darin, dass ein Programmierer eine Deallokationsfunktionvoid operator delete(void*, size_t);
bereitstellen könnte. Für deren zweiten Parameter muss
sizeof(T)
ausgewertet werden, was natürlich die Kenntnis vonT
voraussetzt.Ich könnte mir zudem vorstellen, dass hier die übliche "Man will die Implementierung nicht unnötig einschränken"-Philosophie greift, wobei mir dazu kein konkretes Anwendungsbeispiel einfällt. Gibt es noch weitere Gründe?