it-swarm.com.de

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

Diese Frage wurde von einer ähnlichen Frage inspiriert: Woher „kennt“ delete [] die Größe des Operandenarrays?

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, die ein Array annimmt, 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));

Dies druckt "Size of arr: 4 ", das ist nur die Größe des Zeigers. Es wäre schön, eine Funktion zu haben, die 256 ausgibt, aber ich glaube nicht, dass eine in C++ existiert. (Wiederum ist ein Teil der Frage, warum sie nicht existiert .)

Erläuterung : Ich weiß, dass, wenn ich das Array auf dem Stapel anstelle des Heaps deklariert habe (d. H. "int arr[256]; "), dass der Operator sizeof 1024 (Arraylänge * sizeof (int)) zurückgibt.

61
Kip

delete [] Kennt die zugewiesene Größe. Dieses Wissen befindet sich jedoch in der Laufzeit oder im Speichermanager des Betriebssystems, was bedeutet, dass es dem Compiler während der Kompilierung nicht zur Verfügung steht. Und sizeof() ist keine echte Funktion, sondern wird vom Compiler als Konstante ausgewertet. Dies ist bei dynamisch zugewiesenen Arrays, deren Größe während der Kompilierung nicht bekannt ist, nicht möglich.

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ß p ist? Die Wurzel des Problems ist, dass Arrays in C und C++ keine erstklassigen Objekte sind. Sie verfallen in Zeiger, und der Compiler oder das Programm selbst können nicht wissen, ob ein Zeiger auf den Anfang eines von new zugewiesenen Speicherbereichs oder auf ein einzelnes Objekt oder auf einen bestimmten Ort verweist in der Mitte eines von new zugewiesenen Speicherbereichs.

Ein Grund dafür ist, dass C und C++ die Speicherverwaltung dem Programmierer und dem Betriebssystem überlassen, weshalb sie auch keine Garbage Collection haben. Die 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 ist möglicherweise möglich, C++ alle zugewiesenen Arrays und ihre Größen nachverfolgen zu lassen, wenn Sie ein Textverarbeitungsprogramm für eine Windows-Box schreiben, die auf der neuesten Intel-CPU ausgeführt wird, aber es ist möglicherweise völlig unmöglich, wenn Sie ein eingebettetes System schreiben, auf dem es ausgeführt wird ein DSP.

65
Dima

Nein, das ist in Standard C++ nicht möglich.

Es gibt keinen wirklich guten Grund, warum mir das nicht bewusst ist. Wahrscheinlich wurde die Größe als Implementierungsdetail angesehen und am besten nicht herausgestellt. Beachten Sie, dass Sie mit malloc (1000) nicht garantieren können, dass der zurückgegebene Block 1000 Bytes umfasst - nur, dass es sich um mindestens 1000 Bytes handelt. Höchstwahrscheinlich sind es ungefähr 1020 (1 KB minus 4 Byte für Overhead). In diesem Fall ist die Größe "1020" für die Laufzeitbibliothek von Bedeutung. Und das würde sich natürlich zwischen den Implementierungen ändern.

Aus diesem Grund hat das Normungsgremium std: vector <> hinzugefügt, das die exakte Größe protokolliert.

18
James Curran

Nun, es gibt tatsächlich eine Möglichkeit, die Größe zu bestimmen, aber sie ist nicht "sicher" und unterscheidet sich von Compiler zu Compiler .... sollte also überhaupt nicht verwendet werden.

Wenn Sie dies tun: int * arr = new int [256];

Der Wert 256 ist irrelevant. Sie erhalten den Wert 256 * sizeof (int), vorausgesetzt, in diesem Fall 1024 wird dieser Wert wahrscheinlich bei (arr - 4) gespeichert.

So geben Sie die Anzahl der "Artikel"

int * p_iToSize = arr - 4;

printf ("Anzahl der Elemente% d", * p_iToSize/sizeof (int));

Für jeden Malloc, der neu ist, wird vor dem Continuos-Speicherblock, den Sie empfangen, auch ein Platz reserviert, der einige Informationen zu dem Speicherblock enthält, den Sie erhalten haben.

16
João Augusto

Die übliche Vorgehensweise ist die Verwendung eines Vektors

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);
}
5
Doug T.

Ein bisschen 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; 
}
3
Zingam

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 verarbeiten (oder eine lineare Suche durchführen), um den Sentinel-Wert zu ermitteln. Sobald Sie den Sentinel-Wert erreicht haben, haben Sie Ihre Array-Anzahl.

Bei einer einfachen C-Zeichenfolge ist die abschließende\0 ein Beispiel für einen Sentinel-Wert.

3
bearvarine

C++ hat beschlossen, new hinzuzufügen, um ein typsicheres malloc zu erstellen. New muss dann die Größe und Anzahl der Elemente für den Aufruf von ctors kennen, also delete für den Aufruf von dtors. In den frühen Tagen müssen Sie tatsächlich übergeben, um die Nummern von Objekten zu löschen, die Sie an neue übergeben haben.

string* p = new string[5];
delete[5] p;

Sie dachten jedoch, dass bei Verwendung von new <type> [] der Overhead einer Zahl gering ist. Also beschlossen sie, dass sich new [n] an n erinnern und es zum Löschen übergeben muss. Es gibt drei Möglichkeiten, dies umzusetzen.

  1. behalte eine Hash-Tabelle mit Zeigern auf die Größe
  2. schrieb es direkt in der Nähe des Vektors
  3. mach etwas ganz anderes

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 zum Teufel keiner von denen.

3
Mykelyk

Es gibt keine tragbare Möglichkeit, die Größe eines dynamisch zugewiesenen Arrays in C++ zu bestimmen, wenn nur sein Zeiger angegeben wird. C++ wurde entwickelt, um sehr flexibel zu sein und dem Benutzer Macht zu geben. Beispielsweise definiert der Standard nicht, wie Speicherzuweiser arbeiten müssen, z. durch Hinzufügen eines Headers mit der erforderlichen Größe. Der Verzicht auf einen Header bietet viel mehr Flexibilität.

Betrachten Sie als ein Beispiel eine Zeichenfolge, die als char * -Array implementiert ist. Es ist üblich, Zeiger in der Mitte des Arrays zu verwenden, um Teilzeichenfolgen auszuwählen. Ein Beispiel hierfür ist die Funktion strtok in der Standard-C-Bibliothek. Wenn ein Header direkt vor jedem Array eingebettet werden muss, müssen Sie Teile des Arrays vor der Teilzeichenfolge in den Papierkorb verschieben.

Eine alternative Möglichkeit, die Header zu verarbeiten, besteht darin, Array-Header in einem Speicherblock zu haben und sie auf den unformatierten Array-Speicher an einer anderen Stelle zu verweisen. In vielen Situationen würde dies zwei Zeigersuchen für jede Referenz erfordern, was die Leistung stark beeinträchtigen würde. Es gibt Möglichkeiten, diese Mängel zu beheben, sie erhöhen jedoch die Komplexität und verringern die Flexibilität bei der Implementierung.

Die Vorlage std :: vector ist meine Lieblingsmethode, um die Größe eines Arrays, das an das Array selbst gebunden ist, beizubehalten.

C ist eine portable Assemblersprache mit einer besseren Syntax.

2
Mr Fooz

Das liegt daran, dass Ihre Variable arr nur ein Zeiger ist. Es enthält die Adresse eines bestimmten Speicherorts im Speicher, ohne etwas darüber zu wissen. Sie deklarieren es als int *, was dem Compiler einen Hinweis darauf gibt, was zu tun ist, wenn Sie den Zeiger inkrementieren. Andernfalls könnten Sie auf den Anfang oder das Ende des Arrays oder auf den Stapel oder auf einen ungültigen Speicher verweisen. Aber ich stimme dir zu, es ist sehr ärgerlich, sizeof nicht anrufen zu können :)

QuantumPete

2
QuantumPete

Grundsätzlich können Sie nicht:

void foo(int* arr);

int arr[100] = {0};

foo(arr+1); // Calls foo with a pointer to 100-1 elements.

Ein C++ - Array ist nichts anderes als eine Sammlung von Objekten, die in einem zusammenhängenden Speicherbereich gespeichert sind. Da sich zwischen ihnen keine Löcher befinden (Auffüllen ist inside objects), können Sie das nächste Element eines Arrays finden, indem Sie einfach den Zeiger inkrementieren. Auf CPU-Ebene ist dies eine einfache Anpassung. C++ fügt nur einen sizeof (Element) -Multiplikator ein.

Beachten Sie, dass Implementierungen möglicherweise "Fettzeiger" implementieren, die Array-Grenzen enthalten. Sie müssten doppelt so groß sein, wie Sie eine Verknüpfung zu einer Art "Array-gebundenem Deskriptor" herstellen müssten. Als Nebeneffekt können Sie bei solchen Implementierungen möglicherweise delete [] (1+new int[5]); aufrufen.

1
MSalters

Das geht leider nicht. In C und C++ liegt es in der Verantwortung des Programmierers, sich an die Länge eines Arrays zu erinnern, da die Arraylänge nirgendwo gespeichert wird. Delete [] und free () merken sich die Größe des zugewiesenen Blocks, weisen jedoch möglicherweise mehr Speicher zu als angefordert, sodass die internen Datenstrukturen, in denen die Größe der zugewiesenen Speicherblöcke gespeichert ist, möglicherweise nicht die genaue Größe Ihres Arrays ergeben.

Beachten Sie, dass C++ STL-Vektoren, bei denen es sich im Wesentlichen um Arrays handelt, die in eine Klasse mit einigen Hilfsfunktionen eingeschlossen sind, die Länge des Arrays speichern. Wenn Sie diese Funktionalität also wirklich benötigen, können Sie einfach Vektoren verwenden.

1
Tamas Czinege

Im Allgemeinen nicht. Arrays in C und C++ sind nur Speicherblöcke ohne Buchhaltungsinformationen. Ohne Speichern der Länge des Arrays im Speicher und Hinzufügen von Overhead ist dies im Allgemeinen nicht möglich.

Es gibt eine Ausnahme für Arrays, die statisch zugeordnet sind. Wenn Sie beispielsweise Folgendes deklarieren: int a[50], Funktioniert sizeof(a). Dies ist möglich, weil [50] Teil des statischen Typs des Arrays ist: Dem Compiler ist dies bekannt. sizeof wird zur Kompilierungszeit interpretiert.

Wenn Sie jedoch einen Zeiger erstellen: int *p = a, Gibt sizeof(p) die Größe des Zeigers zurück, nicht die Größe des Arrays, da der Compiler nicht weiß, welche p-Punkte zu.

1
James Rose

Nein, es gibt keine Möglichkeit, dies zu tun. Sie müssen nachverfolgen, wie groß es von außen ist. Klassen wie std::vector tu das für dich.

1
Greg Rogers

Nun gibt es std :: array , einen effizienten Wrapper zur Kompilierungszeit um ein Array konstanter Größe:

#include <array>

int main (int argc, char** argv)
{
    std::array<int, 256> arr;
    printf("Size of arr: %ld\n", arr.size());
}

Die Parameter sind <type, #elements>.

Sie erhalten auch ein paar andere Feinheiten, wie Iteratoren, empty () und max_size ().

1
Brent Faust

Gibt es eine Möglichkeit, die Größe eines C++ - Arrays programmgesteuert zu bestimmen? Und wenn nicht, warum?

  1. Nein, es sei denn, Sie behalten selbst den Überblick.
  2. Denn wenn der Compiler niemandem außer sich von diesen Informationen erzählen muss, schränkt er den Compiler weniger ein. Ob das wünschenswert ist oder nicht, steht zur Debatte.
0
MSN

@Dima,

Wie würde der Compiler wissen, wie groß p ist?

Der Compiler muss die Größe von p kennen; Andernfalls kann delete[] nicht implementiert werden. Der Compiler muss keinem anderen mitteilen, wie er das herausfindet.

Um dies zu überprüfen, sollten Sie den von operator new[] Zurückgegebenen Zeiger mit dem von new[] Zurückgegebenen Zeiger vergleichen.

0
MSN

Der Compiler kann das nicht wissen

char *ar = new char[100] 

ist ein Array mit 100 Zeichen, da kein tatsächliches Array im Speicher erstellt wird. Es wird lediglich ein Zeiger auf 100 nicht initialisierte Bytes im Speicher erstellt.

Wenn Sie die Größe des angegebenen Arrays wissen möchten, verwenden Sie einfach std :: vector. std :: vector ist einfach ein besseres Array.

0
SMeyers

Wenn Sie Array-Zeiger erstellen (Wrapper mit Vorlage für Zeiger erstellen), können Sie dies nicht, aber wenn Sie ein Array von Objekten erstellen, können Sie die Größe des Arrays folgendermaßen ermitteln:

char* chars=new char[100];
printf("%d",*((int*)chars-1));

Das delete[] Funktion müssen alle Objekte in ihm dekonstruieren. um es zu tun die new[] Schlüsselwort setzt die Anzahl der Elemente hinter das gesamte Array.

Der Körper des Arrays ist wie folgt:

int count;
ObjectType* data; //This value is returned when using new[]
0
MessyCode

dazu dividiere ich die Größe des Arrays durch die Größe des ersten Elements

int intarray[100];
printf ("Size of the array %d\n", (sizeof(intarray) / sizeof(intarray[0]));

Es druckt 100

0
user4443767