Bestimmen Sie die Größe eines C++-Arrays programmgesteuert?

Lesezeit: 8 Minuten

Benutzer-Avatar
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

Benutzer-Avatar
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.

  1. Halten Sie eine Hash-Tabelle von Zeigern auf die Größe
  2. schrieb es direkt in der Nähe des Vektors
  3. 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.

Benutzer-Avatar
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.

Etwas Magie:

template <typename T, size_t S>
inline
size_t array_size(const T (&v)[S]) 
{ 
    return S; 
}

Und so machen wir es in C++11:

template<typename T, size_t S>
constexpr 
auto array_size(const T (&)[S]) -> size_t
{ 
    return S; 
}

  • Sehr nützliche und schöne Lösung. Nur eine Sache: Ich würde stattdessen size_t als zweiten Template-Parameter verwenden.

    – besworland

    10. August 2016 um 19:06 Uhr

1013350cookie-checkBestimmen Sie die Größe eines C++-Arrays programmgesteuert?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy