Ich habe eine Methode, die eine Variable int erhält. Diese Variable stellt eine Array-Größe dar (bitte bieten Sie mir keinen Vektor an). Daher muss ich in meiner Methode eine Konstante int initieren, um ein Array einer bestimmten Größe zu initialisieren. Frage: wie mache ich das?
void foo(int variable_int){
int a[variable_int] = {0}; //error
}
Sie haben nach einer Nicht-Vektor-Lösung gefragt, aber gehen wir sie noch einmal durch, weil Sie sie möglicherweise aus den falschen Gründen abgelehnt haben. Sie sollten sich keine Gedanken über die Leistung machen, da dies in den Händen eines kompetenten Compilers so ziemlich den gleichen Overhead haben wird wie jede andere standardkonforme Lösung. Auf der anderen Seite gibt es einige Lesbarkeits- und Sicherheitsbedenken, auf die ich weiter unten eingehen werde. Schauen wir uns die Möglichkeiten an, wie Sie dies tun können, von den am meisten empfohlenen bis zu den am wenigsten empfohlenen.
std::Vektor
Der Lieblingscontainer der Community und das aus gutem Grund. Es kann nicht nur mit einer Laufzeitgröße deklariert werden, sondern die Größe kann jederzeit geändert werden. Dies erleichtert die Verwendung, wenn die Größe nicht vorherbestimmt werden kann, z. B. beim wiederholten Abfragen von Benutzereingaben. Beispiele:
// Known size
size_t n;
std::cin >> n;
std::vector<int> vec(n);
// Unknown size
std::vector<int> vec;
int input;
while (std::cin >> input) { // Note: not always the best way to read input
vec.push_back(in);
}
Es gibt nicht viel Nachteil bei der Verwendung std::vector
. Der Fall bekannter Größe erfordert genau eine dynamische Zuordnung. Die unbekannte Größe erfordert im allgemeinen Fall mehr, aber besser geht es sowieso nicht. Die Leistung ist also mehr oder weniger optimal.
Semantisch ist es möglicherweise nicht ideal für Größen, die während der gesamten Ausführung konstant sind. Für den Leser ist möglicherweise nicht ersichtlich, dass dieser Container nicht geändert werden soll. Es ist dem Compiler auch nicht bekannt, sodass Sie etwas falsch machen können, z push_back
in ein vector
das ist logischerweise von konstanter Größe.
std::unique_ptr (oder std::shared_ptr)
Die sicherste Lösung, wenn Ihnen die Durchsetzung der statischen Größe wichtig ist.
size_t n;
std::cin >> n;
auto arr = std::make_unique<int[]>(n);
arr
Die Größe von kann sich nicht ändern, es kann jedoch dazu gebracht werden, das aktuelle Array freizugeben und auf ein anderes mit unterschiedlicher Größe zu verweisen. Wenn also die Größe Ihres Containers logischerweise konstant ist, vermittelt dies die Absicht klarer. Leider ist es auch viel schwächer als std::vector
auch im Fall konstanter Größe. Es ist nicht größenbewusst, daher müssen Sie die Größe explizit speichern. Aus dem gleichen Grund bietet es keine Iteratoren und kann nicht im Bereich für Schleifen verwendet werden. Es liegt an Ihnen (und dem betreffenden Projekt), ob Sie diese Funktionen opfern möchten, um eine statische Größe zu erzwingen.
Anfangs hatte ich empfohlen boost::scoped_array
aber nach weiterem Nachdenken glaube ich nicht, dass es gegenüber dieser Lösung viel zu bieten hat, also bleibe ich bei der Standardbibliothek.
Neu[] – löschen[]
Technisch gesehen eine Lösung, aber wenn Sie nicht gezwungen sind, einen alten C++-Standard zu verwenden, oder Sie eine Low-Level-Bibliothek schreiben, die den Speicher intern verwaltet, sind sie streng schlechter als die std::unique_ptr
oder std::shared_ptr
Lösung. Sie bieten keine Features mehr, sind aber deutlich weniger sicher, weil man den Speicher explizit freigeben muss, wenn man damit fertig ist. Andernfalls wird es auslaufen, was zu erheblichen Problemen führen kann. Um die Sache noch schlimmer zu machen, mit delete[]
kann für Programme mit komplizierten Ausführungsabläufen und Ausnahmebehandlung nicht trivial sein. Bitte verwenden Sie dies nicht, wenn Ihnen die oben genannten Lösungen zur Verfügung stehen!
size_t n;
std::cin >> n;
int* arr = new int[n];
...
// Control flow must reach exactly one corresponding delete[] !!!
delete[] arr;
Bonus: Compiler-Erweiterung
Einige Compiler sind möglicherweise mit dem folgenden Code einverstanden
size_t n;
std::cin >> n;
int arr[n];
Sich darauf zu verlassen, hat schwerwiegende Nachteile. Ihr Code kann nicht auf allen C++-konformen Compilern kompiliert werden. Es kompiliert wahrscheinlich nicht einmal auf allen Versionen des angegebenen Compilers. Außerdem bezweifle ich, dass die produzierte ausführbare Datei den Wert überprüft n
und weist bei Bedarf auf dem Haufen zu, was bedeutet, dass Sie Ihren Stapel sprengen können. Diese Lösung ist nur sinnvoll, wenn Sie die Obergrenze von kennen n
klein ist und Ihnen die Leistung so wichtig ist, dass Sie bereit sind, sich auf das Compiler-spezifische Verhalten zu verlassen, um sie zu erhalten. Das sind wirklich Ausnahmefälle.
int *a = new int[variable_int];
Denken Sie daran, zu löschen[] den zugewiesenen Speicherplatz, wenn Sie damit fertig sind!
C++ unterstützt keine Arrays variabler Länge. Stattdessen müssen Sie das Array dynamisch zuweisen:
std::vector<int> a(variable_int);
oder da Sie sagen, dass Sie aus irgendeinem Grund keinen Vektor verwenden möchten:
class not_a_vector
{
public:
explicit not_a_vector(size_t size) : a(new int[size]()) {}
~not_a_vector() {delete [] a;}
int & operator[](size_t i) {return a[i];}
int operator[](size_t i) const {return a[i];}
not_a_vector(not_a_vector const &) = delete;
void operator=(not_a_vector const &) = delete;
private:
int * a;
};
not_a_vector a(variable_int);
UPDATE: Die Frage wurde gerade mit dem “C”-Tag sowie “C++” aktualisiert. C (seit 1999) unterstützt Arrays mit variabler Länge, daher sollte Ihr Code in dieser Sprache in Ordnung sein.
Sie können einfach durch Schreiben eine konstante Variable aus einer nicht konstanten Variablen machen const int bar = variable_int;
– aber das wird dir nicht helfen. In C++ muss die Größe eines Arrays mit automatischer Speicherung a sein Kompilierzeit Konstante. Sie können eine Variable nicht in eine Kompilierzeitkonstante umwandeln, also ist das, was Sie wollen, einfach nicht möglich.
Je nach Ihren Bedürfnissen könnten Sie machen a
einen Zeiger und weisen Sie Speicher mit zu new
(und dann später delete
it) oder, wenn der Parameter to foo
wird immer zur Kompilierzeit bekannt sein, könnten Sie umdrehen foo
in eine Template-Funktion wie diese:
template<int n> void foo() {
int a[n] = {0};
}
Um zu tun, was Sie wollen, müssen Sie die dynamische Zuordnung verwenden. In diesem Fall würde ich ernsthaft vorschlagen, stattdessen Vektor zu verwenden – es ist die “richtige” Vorgehensweise in C++.
Aber wenn Sie immer noch keinen Vektor verwenden möchten [why you wouldn’t is beyond me]der richtige Code lautet:
void foo(int variable_int){
int *a = new int[variable_int](); // Parenthesis to initialize to zero.
... do stuff with a ...
delete [] a;
}
Wie andere vorgeschlagen haben, können Sie auch calloc verwenden, was den gleichen Effekt der Initialisierung auf Null hat, aber nicht wirklich die “c++”-Lösung.
Wenn Sie Arrays verwenden, ist es eine gute Idee, sie zu kapseln:
template<typename Type>
class Vector {
//...
};
Die Standardbibliothek enthält eine Implementierung: std::Vektor
.
Was ist falsch an einem Vektor? Da dies fehlschlägt, biete ich Ihnen einen intelligenten Hinweis.
– Chris
22. Januar 13 um 13:07 Uhr
Warum kein Vektor? Warum überhaupt C++ verwenden, wenn Sie nur C-Idiome verwenden möchten?
– PaulR
22. Januar 13 um 13:07 Uhr
Stellen Sie sich einen Vektor einfach als ein Array vor, das Sie mit einer variablen Größe initialisieren können. Sind Sie sicher, dass Sie Ihre Frage nicht mit “c” markieren wollten?
– Roddy
22. Januar 13 um 13:12 Uhr
@den-javamaniac Für die meisten Zwecke ein C++
vector
ist mindestens so gut wie ein Array! (Falls Sie noch nie mit C++-Vektoren gearbeitet haben: Der Name ist verwirrend, sie verhalten sich eher wie ein Array als wie ein mathematischer Vektor.) Sie können sogar Vektorelemente mit adressieren[]
-Operator, wenn Sie möchten (wenn Sie keine Begrenzungsprüfung benötigen/wollen).– us2012
22. Januar 13 um 13:18 Uhr
Die Antwort ist die Verwendung eines Vektors. C++ unterstützt keine Arrays mit variabler Länge, und die Speicherverwaltung ohne RAII führt zu Speicherlecks, wenn irgendetwas eine Ausnahme auslöst.
– Mike Seymour
22. Januar 13 um 13:18 Uhr