Bestimmen Sie die Größe eines C++-Arrays programmgesteuert?
Lesezeit: 8 Minuten
Pennen
Diese Frage wurde von einer ähnlichen Frage inspiriert: Wie löscht[] „Kennen“ Sie die Größe des Operanden-Arrays?
Meine Frage ist etwas anders: Gibt es eine Möglichkeit, die Größe eines C++-Arrays programmgesteuert zu bestimmen? Und wenn nicht, warum? Jede Funktion, die ich gesehen habe und die ein Array verwendet, benötigt auch einen Integer-Parameter, um ihm die Größe zu geben. Aber wie die verknüpfte Frage zeigte, delete[] muss die Größe des Speichers kennen, der freigegeben werden soll.
Betrachten Sie diesen C++-Code:
int* arr = new int[256];
printf("Size of arr: %d\n", sizeof(arr));
Das druckt “Size of arr: 4“, was nur die Größe des Zeigers ist. Es wäre schön, eine Funktion zu haben, die 256 ausgibt, aber ich glaube nicht, dass eine in C++ existiert. (Auch hier ist ein Teil der Frage, warum es nicht existiert.)
Klärung: Ich weiß das, wenn ich das Array auf dem Stack statt auf dem Heap deklariert habe (dh “int arr[256];“) dass die sizeof Operator würde 1024 (Array-Länge * sizeof(int)) zurückgeben.
Wenn Sie das Array auf dem Stapel zuweisen, würde der sizeof-Operator tatsächlich 1024 zurückgeben – das ist 256 (die Anzahl der Elemente) * 4 (die Größe eines einzelnen Elements). (sizeof(arr)/sizeof(arr[0])) würde das Ergebnis 256 ergeben.
– Kevin
13. Oktober 2008 um 16:03 Uhr
danke, das habe ich übersehen, weil ich eigentlich char benutzt habe[] in meinem Testcode (und sizeof(char) == 1)
– Kipp
13. Oktober 2008 um 16:20 Uhr
Obwohl es nur hypothetisch ist – da es nicht funktioniert – muss ich darauf hinweisen, dass Sie hätten schreiben sollen printf("Size of arr: %d\n", sizeof(*arr)); anstatt printf("Size of arr: %d\n", sizeof(*arr)); da Sie die Größe des dereferenzierten Zeigers abrufen möchten.
– mg30rg
12. Juni 2014 um 10:32 Uhr
Dima
delete [] kennt die zugewiesene Größe. Dieses Wissen befindet sich jedoch in der Laufzeit oder im Speichermanager des Betriebssystems, sodass es dem Compiler während der Kompilierung nicht zur Verfügung steht. Und sizeof() ist keine echte Funktion, sie wird tatsächlich vom Compiler als Konstante ausgewertet, was er für dynamisch zugewiesene Arrays, deren Größe während der Kompilierung nicht bekannt ist, nicht tun kann.
Betrachten Sie auch dieses Beispiel:
int *arr = new int[256];
int *p = &arr[100];
printf("Size: %d\n", sizeof(p));
Wie würde der Compiler wissen, wie groß die ist p ist? Die Wurzel des Problems liegt darin, dass Arrays in C und C++ keine erstklassigen Objekte sind. Sie zerfallen zu Zeigern, und es gibt für den Compiler oder das Programm selbst keine Möglichkeit zu wissen, ob ein Zeiger auf den Anfang eines Speicherblocks zeigt, der von zugewiesen wurde newoder an ein einzelnes Objekt oder an eine Stelle in der Mitte eines Speicherblocks, der von zugewiesen wurde new.
Ein Grund dafür ist, dass C und C++ die Speicherverwaltung dem Programmierer und dem Betriebssystem überlassen, weshalb sie auch keine Garbage Collection haben. Implementierung von new und delete ist nicht Teil des C++-Standards, da C++ auf einer Vielzahl von Plattformen verwendet werden soll, die ihren Speicher möglicherweise auf sehr unterschiedliche Weise verwalten. Es kann möglich sein, C++ alle zugewiesenen Arrays und ihre Größen verfolgen zu lassen, wenn Sie ein Textverarbeitungsprogramm für eine Windows-Box schreiben, die auf der neuesten Intel-CPU läuft, aber es kann völlig unmöglich sein, wenn Sie ein eingebettetes System schreiben, das darauf läuft ein DSP.
Es gibt durchaus Arrays in C++. Wie sonst würden Sie erklären, warum mit diesem “char x[4]; size_t sz = sizeof(x);” dass ‘sz’ 4 zugewiesen wird?
– Kevin
13. Oktober 2008 um 16:06 Uhr
Dima, es gibt absolut Arrays. Arrays unterscheiden sich von Zeigern. Leider verwirren viele Lehrer es und sagen ihren Schülern, dass sie “nur” Hinweise sind. nein, das sind sie nicht. wie sonst erklären Sie das: char const**s = &”bar”; kompiliert nicht? […]
– Johannes Schaub – litb
7. Januar 2009 um 11:41 Uhr
litb, der Grund char const **s = &”bar”; nicht kompiliert, ist, dass “bar” eine Konstante und kein Lvalue ist, also können Sie die Adresse nicht nehmen. Es ist dasselbe wie int *p = &5; die auch nicht kompilieren.
– Dima
7. Januar 2009 um 18:27 Uhr
Es ist klar, aber fast alles ist falsch. Es gibt bereits eine Situation, in der sizeof Laufzeit statt Kompilierzeit ist, Arrays tun existieren, und es gibt Möglichkeiten für die Implementierung, die Größe aller Arrays zu kennen. Sogar ein DSP muss Bewahren Sie die Größeninformationen für Zuordnungen auf.
– Muhende Ente
1. Mai 2012 um 14:58 Uhr
void foo(int *a); nimmt einen Zeiger, void foo(int (&a)[5]); nimmt ein Array. Array-Namen zerfallen zu Zeigern, was scheiße ist, aber das bedeutet nicht, dass Arrays und Zeiger dasselbe sind.
– Katze Plus Plus
1. Mai 2012 um 16:16 Uhr
Nun, es gibt tatsächlich eine Möglichkeit, die Größe zu bestimmen, aber es ist nicht “sicher” und wird von Compiler zu Compiler unterschiedlich sein …. sollte also gar nicht verwendet werden.
Wenn Sie dies tun: int* arr = new int[256];
Die 256 ist irrelevant, Sie erhalten 256*sizeof(int), wenn Sie für diesen Fall 1024 annehmen, wird dieser Wert wahrscheinlich bei ( arr – 4 ) gespeichert.
Um Ihnen also die Anzahl der “Elemente” zu geben
int* p_iToSize = arr – 4;
printf(“Anzahl der Elemente %d”, *p_iToSize / sizeof(int));
Für jedes malloc, new, was auch immer vor dem kontinuierlichen Speicherblock, den Sie erhalten, wird auch ein reservierter Platz mit einigen Informationen bezüglich des Ihnen gegebenen Speicherblocks zugewiesen.
Trotzdem beantwortet dies eigentlich die Frage.
– A.Rex
8. Januar 2009 um 20:41 Uhr
interessant, 🙂 als extra 2 cent könntest du “neu” überladen und die speicherverwaltung nach belieben implementieren, du könntest es haben wie joao es beschreibt, oder jeden zeiger in einer map mit der entsprechenden größe speichern…kurz gesagt gibt es viele verrückte Möglichkeiten, es zu erreichen, aber ich würde sie nicht verwenden: p
– Chrispepper1989
23. Mai 2013 um 12:53 Uhr
Was ist mit Char-Array? char * arr = neues zeichen[100];
– Jai
4. März 2014 um 7:58 Uhr
Nein, in Standard C++ gibt es dafür keine Möglichkeit.
Es gibt keinen wirklich guten Grund, warum nicht, dass ich mir bewusst bin. Wahrscheinlich wurde die Größe als Implementierungsdetail angesehen und am besten nicht offengelegt. Beachten Sie, dass es keine Garantie dafür gibt, dass der zurückgegebene Block 1000 Bytes groß ist, wenn Sie malloc(1000) sagen – nur, dass es so ist wenigstens 1000 Byte. Höchstwahrscheinlich sind es etwa 1020 (1K minus 4 Bytes für Overhead). In diesem Fall ist die Größe „1020“ für die Laufzeitbibliothek wichtig. Und das würde sich natürlich zwischen den Implementierungen ändern.
Aus diesem Grund hat das Standardkomitee std:vector<> hinzugefügt, das die genaue Größe verfolgt.
Eine Sache zu beachten ist, dass neu[] speichert auch die Anzahl der angeforderten Elemente, um die richtige Anzahl von Konstruktoren und Destruktoren für das Array aufzurufen. Wo dies gespeichert wird, ist wiederum implementierungsspezifisch. Der Grund, keinen Weg einzuschließen, um es zu bekommen, liegt außerhalb meiner Vorstellungskraft.
– arbeitswütig3
13. Oktober 2008 um 15:05 Uhr
Ich denke, der “gute Grund” ist, dass Arrays überhaupt keine Objekte sind. Ein Array ist nur ein roher Speicherblock. Die Größe sind Speicherverwaltungsdaten, keine Objektdaten. Sie könnten eine Array-Klasse schreiben, die den Speicher und die Größe verfolgt, aber Sie könnten einfach std::vector verwenden und sich darüber keine Gedanken machen.
– Hermes
13. Oktober 2008 um 15:09 Uhr
Aha … Natürlich. Ein int* konnte nicht wissen, ob das Array, auf das es zeigte, ein neues Array oder ein lokales Array oder eine Stelle in der Mitte des Arrays war.
– James Curran
13. Oktober 2008 um 15:13 Uhr
@Herms: std::string[10] ist definitiv kein Rohspeicher, aber es ist ein Array.
– MSalter
14. Oktober 2008 um 7:31 Uhr
workmad3, möglicherweise nur für Elemente mit einem nicht trivialen Destruktor und für Typen mit einem benutzerdefinierten Operator delete, der die Größe bekannt geben möchte. für alles andere genügt es, die Nummer nicht zu speichern
– Johannes Schaub – litb
7. Januar 2009 um 11:49 Uhr
Die übliche Methode, dies zu handhaben, besteht darin, entweder einen Vektor zu verwenden
int main()
{
std::vector<int> v(256);
printf("size of v is %i capacity is %i\n", sizeof(int) * v.size(), sizeof(int) * v.capacity());
}
oder die Größe vorgeben
const int arrSize = 256;
int main()
{
int array[arrSize];
printf("Size of array is %i", sizeof(int) * arrSize);
}
C++ hat beschlossen, new hinzuzufügen, um einen typsicheren Malloc auszuführen, da new sowohl die Größe als auch die Anzahl der Elemente zum Aufrufen von ctors kennen muss, also delete zum Aufrufen von dtors. In den Anfangszeiten musste man tatsächlich übergeben, um die Nummern eines übergebenen Objekts neu zu löschen.
string* p = new string[5];
delete[5] p;
Sie dachten jedoch, dass bei Verwendung von new[] der Overhead einer Nummer war gering. Also haben sie das neu entschieden[n] muss sich an n erinnern und es zum Löschen übergeben. Es gibt drei Hauptwege, um es zu implementieren.
Halten Sie eine Hash-Tabelle von Zeigern auf die Größe
schrieb es direkt in der Nähe des Vektors
etwas ganz anderes machen
Vielleicht ist es möglich, die Größe so zu erhalten:
size_t* p = new size_t[10];
cout << p[-1] << endl;
// Or
cout << p[11] << endl;
Oder zur Hölle nichts davon.
Bärenvarietät
Abhängig von Ihrer Anwendung können Sie am Ende Ihres Arrays einen “Sentinel-Wert” erstellen.
Der Sentinel-Wert muss eine eindeutige Eigenschaft haben.
Sie können dann entweder das Array nach dem Sentinel-Wert verarbeiten (oder eine lineare Suche durchführen) und dabei zählen. Sobald Sie den Sentinel-Wert erreicht haben, haben Sie Ihre Array-Anzahl.
Bei einem einfachen C-String ist das abschließende \0 ein Beispiel für einen Sentinel-Wert.
Wenn Sie das Array auf dem Stapel zuweisen, würde der sizeof-Operator tatsächlich 1024 zurückgeben – das ist 256 (die Anzahl der Elemente) * 4 (die Größe eines einzelnen Elements). (sizeof(arr)/sizeof(arr[0])) würde das Ergebnis 256 ergeben.
– Kevin
13. Oktober 2008 um 16:03 Uhr
danke, das habe ich übersehen, weil ich eigentlich char benutzt habe[] in meinem Testcode (und sizeof(char) == 1)
– Kipp
13. Oktober 2008 um 16:20 Uhr
Obwohl es nur hypothetisch ist – da es nicht funktioniert – muss ich darauf hinweisen, dass Sie hätten schreiben sollen
printf("Size of arr: %d\n", sizeof(*arr));
anstattprintf("Size of arr: %d\n", sizeof(*arr));
da Sie die Größe des dereferenzierten Zeigers abrufen möchten.– mg30rg
12. Juni 2014 um 10:32 Uhr