C++ Lernen - Verständnisfragen
-
@Drgreentom sagte in C++ Lernen - Verständnisfragen:
Übersehe ich da was?
Ja, tust du.
std::string::npos
ist einsize_t
.size_t
ist ein unsigned Datentyp undnpos
ist der größte Wert, der damit darstellbar ist. Das dies alsint
interpretiert-1
ist, ist nirgends garantiert.
Fürbool
gilt, jeder Zahlenwert ungleich Null wird als wahr interpretiert.Daher, aufpassen, wenn Typen konvertiert werden.
std::string::size_type
garantiert, dass der Typ groß genug ist, um die Größe des Strings wiederzugeben.std::string::find
gibt einensize_t
zurück.size_t
garantiert, dass es die maximale Größe eines Objektes speichern kann.Ich bin mir gerade nicht sicher, ob
std::string::size_type
immer einsize_t
sein muss, halte es aber für wahrscheinlich, dass es in den meisten Implementationen so sein wird.
-
@Drgreentom sagte in C++ Lernen - Verständnisfragen:
18446744073709551615
Das ist das Maximale, was mit 64 Bit - als unsigned - darstellbar ist. Alle Bits sind auf 1 gesetzt.
-
@Schlangenmensch sagte in C++ Lernen - Verständnisfragen:
Das dies als int interpretiert -1 ist, ist nirgends garantiert.
@DirkB sagte in C++ Lernen - Verständnisfragen:
Das ist das Maximale, was mit 64 Bit - als unsigned - darstellbar ist. Alle Bits sind auf 1 gesetzt.
Jetzt überlege ich gerade... Im Zweierkomplement wäre das vorzeichenbehaftet, ja immer -1. Darüber wird der Wert ja auch gesetzt. D.h. mit -1 vergleichen sollte auch immer gehen.
-
@Schlangenmensch sagte in C++ Lernen - Verständnisfragen:
Jetzt überlege ich gerade... Im Zweierkomplement wäre das vorzeichenbehaftet, ja immer -1. Darüber wird der Wert ja auch gesetzt. D.h. mit -1 vergleichen sollte auch immer gehen.
Ich bin nicht sicher, ob du das richtige meinst, oder ob du dich ungünstig ausdrückst.
(unsigned)-1 == numeric_limits<unsigned>::max()
ist garantiert, ja. Aber nicht weil irgendwelche Annahmen zur internen Zahlenschreibweise garantiert wären, sondern weil unsigned-Typen garantiert der Überlaufarithmetik (bzw. hier halt Unterlauf) modulo ihres Maximalwertes gehorchen. Und du musst mit dem Vergleichswert ungeheuer aufpassen, wegen Integer Promotions:unsigned char c(-1); unsigned short s(-1); unsigned int i(-1); unsigned long l(-1); c == -1; // False s == -1; // False i == -1; // True l == -1; // True
-
@Schlangenmensch sagte in C++ Lernen - Verständnisfragen:
Jetzt überlege ich gerade... Im Zweierkomplement wäre das vorzeichenbehaftet, ja immer -1. Darüber wird der Wert ja auch gesetzt. D.h. mit -1 vergleichen sollte auch immer gehen.
Das Zweierkomplement ist aber nirgends garantiert.
Auch wenn es schwierig ist, heutzutage was anderes zu finden.Ein int muss auch nicht 64 Bit groß sein, so dass der Wert nicht eindeutig ist.
size_t wurde nicht ohne Grund eingeführt.
-
@SeppJ Ja, zuviel überlegt.
@DirkB Nun, wenn ich mir einen 8 bit unsigned Typen nehme und einen 4 bit signed Typen. Und wenn man dem 4 Bit Typen den 8 Bit Typen zuweist in dem man die Bits, die nicht rein passen, einfach abschneidet, wäre das im Zweierkomplement wieder -1
8bit_usigned a = -1; //11111111 4bit_signed b = a; //1111 ->-1
Aber, dass wäre ja zu einfach. Gerade nochmal nachgelesen, wenn es nicht passt und der Zieltyp signed ist, war das Ergebnis bis c++20 tatsächlich Implementation defined.
Seit c++ 20 ist das fest definiert als der Wert modulo 2^(Anzahl Bits des Zieltypes)In meinem Beispiel also 255 mod(2^4) = 15
-
@Schlangenmensch sagte in C++ Lernen - Verständnisfragen:
Aber, dass wäre ja zu einfach. Gerade nochmal nachgelesen, wenn es nicht passt und der Zieltyp signed ist, war das Ergebnis bis c++20 tatsächlich Implementation defined.
Seit c++ 20 ist das fest definiert als der Wert modulo 2^(Anzahl Bits des Zieltypes)Das haben sie jetzt wirklich fest definiert? Das war doch immer ein Paradebeispiel für einen Fall wo undefiniertes Verhalten Optimierungen ermöglicht.
-
A prvalue of an integer type or of an unscoped (since C++11) enumeration type can be converted to any other integer type. If the conversion is listed under integral promotions, it is a promotion and not a conversion.
If the destination type is signed, the value does not change if the source integer can be represented in the destination type. Otherwise the result is implementation-defined (until C++20)the unique value of the destination type equal to the source value modulo 2n
where n is the number of bits used to represent the destination type. (since C++20). (Note that this is different from signed integer arithmetic overflow, which is undefined).https://en.cppreference.com/w/cpp/language/implicit_conversion
Wenn ich das richtig lese, sind Overflows aufgrund von Berechnungen explizit davon ausgenommen. Das heißt im Fall von
for
loops kann da immer noch optimiert werden.
-
Ah ja, das klingt nach einer guten Anpassung.
-
Auch hier wieder vielen Dank für eure Erläuterungen!
Kommen wir zu meinen nächsten Problem:
#include <iostream> #include <string> #include <vector> using namespace std; int main () { vector<string> ausgabe; string eingabe; int z=0; cout << "Wie viele Elemente soll der String haben? "; cin >> z; cout << endl; cout << "Sie wollen " << z << " Elemente eingeben." << endl; cout << endl; do { cout << "Etwas eingeben: "; getline (cin, eingabe); cout << endl; ausgabe.push_back(eingabe); cout << "Elemente im String: " << ausgabe.size() << endl; cout << endl; for (int k=0; k < ausgabe.size(); k++){ cout << ausgabe.at(k) << " "; } cout << endl; } while (z-1 >=ausgabe.size()); }
Kann mir jemand erklären warum die do-while Schleife die Eingabe-Taste von
cin >> z;
mit zählt und den ersten Loop quasi durchläuft?
-
@Drgreentom sagte in C++ Lernen - Verständnisfragen:
Kann mir jemand erklären warum die do-while Schleife die Eingabe-Taste von
cin >> z;
mit zählt und den ersten Loop quasi durchläuft?Weil das Zeichen der Eingabetaste ('\n') noch im Eingabestrom steht.
Beicin
werden nur Zeichen gelesen, die zu dem Typ passen.
Dann wird aufgehört und das nichtpassende Zeichen zurück gestellt.getline()
jedoch, liest das '\n' und hört dann auf.Mit
cin.clear(); cin.ignore(1000,'\n');
kannst du es überlesen
-
@Dirk: Warum 1000?
Ich denke, das ist, gerade für einen Anfänger, sehr verwirrend.Sauber wäre
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // mit #include <limits>
-
Danke, das funktioniert mit dem clear und ignore!
Aber so richtig erschließt sich mir das noch nicht.
Wenn ich also z.B. bei einem Datenypint
oderdouble
die Taste "Enter" drücke, muss ich damit rechnen, dass es mir das "Enter" in den nächsten String mit hineinschreiben wird/kann, wenn ich dortcin
verwende?
-
@Drgreentom sagte in C++ Lernen - Verständnisfragen:
…, muss ich damit rechnen, dass es mir das "Enter" in den nächsten String mit hineinschreiben wird/kann, wenn ich dort
cin
verwende?Nein.
cin
betrachtet Whitespace als Trennzeichen und überliest diese auch am Anfang.Das Problem tritt bei der Mischung von cin und getline() auf
-
Da ich aber z.B. eine
int
Variable nicht mit getline() beschreiben kann/darf, wird diese Mischung mit cin und getline() immer wieder mal auftreten?
-
@Drgreentom Wenn du zeilenweise einlesen möchtest, kannst du auch den int aus einem string wandeln.
-
Hallo zusammen, ich bins mal wieder...
In der ersten for-Schleife steht
X : v
als Bedingung. Nur um sicher zugehen, was bedeutet der ":" hier?
Ich verstehe was die Funktion macht: Für jedes Element in Vecor v erfolgt die zuweisung an X bis zu letzten Element in v. Im Buch wird das zwar benutzt aber nicht erklärt. Im Netz habe ich auch bisher keine befriedigende Erklärung gefunden. Der ":" Operator? hat doch bestimmt einen Namen?#include <iostream> #include <vector> using namespace std; int main() { typedef std::vector<int> vInt; vInt v; v.push_back(190); v.push_back(180); v.push_back(10); v.push_back(10); v.push_back(27); for (auto X : v) { // < wie kann ich den ":" hier verstehen? cout << X << " "; } for (int i=0; i<v.size(); i++){ // Andere Schreibweise, gleiche Ausgabe cout << v[i] << " "; } return 0; }
-
In der ersten for-Schleife steht X : v als Bedingung. Nur um sicher zugehen, was bedeutet der ":" hier?
Das ist ein sog. "range based for loop".
Der Teil vor dem:
gibt die Laufvariable an und der Teil hinter dem:
ist der Container über den iteriert werden soll. Der:
ist einfach nur das Trennzeichen.
-
Hallo zusammen,
ich habe mal wieder eine Frage:Ich möchte mir eine debug Funktion schreiben. Dazu möchte ich unterschiedliche Arrays, die jeweils mit Strings gefüllt sind, an ein Funktion übergeben. Zur Übersichtlichkeit habe ich mich mal das #include, namspace und std:: blabla weg gelassen.
void funktion(array<string> fc_array) \\<- Funktioniert so nicht, da ein Argument fehlt { \\mach mit fc_array etwas } int main() { array<string,3> arr = {string1,string2,string3}; funktion(arr); }
Wie muss ich das Array an die Funktion übergeben? Ich habe bereits gelesen, dass das u.a. mit Pointer funktioniert könnte. Aber ich bekomme das leider nicht hin und den Anwendungsfall finde ich so im Internet nicht. Könnt ihr mir das bitte an dem Bsp. zeigen?
Vielen Dank vorab
-
@Drgreentom sagte in C++ Lernen - Verständnisfragen:
Wie muss ich das Array an die Funktion übergeben?
void funktion(array<string> fc_array) \\<- Funktioniert so nicht, da ein Argument fehlt
std::array
ist ein Array mit fester Größe. Das heisst die Größe muss zur Compile-Zeit bekannt sein, also im Allgemeinen im Code mitgegeben werden. Daher hatstd::array
auch noch einen zweiten Template-Parameter, mit dem die Länge des Arrays angegeben wird. Die Fehlermeldung bekommst du, weil dieser fehlt - so sollte es funktionieren:void funktion(array<string, 3> fc_array)
Damit legst du dich allerdings auf String-Arrays der Länge 3 fest. Vielleicht möchtest du aber Arrays beliebiger Länge verarbeiten.
Eine Möglichkeit ist, ein Array dynamischer Größe zu verwenden:
std::vector
. Bei diesen muss die Länge nicht bereits beim Kompilieren bekannt sein und sie können ihre Größe auch ändern, während das Programm läuft.std::vector
hat daher keinen Template-Parameter für die Länge:#include <vector> ... void funktion(std::vector<string> fc_array) ... std::vector<string> arr = {string1,string2,string3}; funktion(arr);
Falls du dennoch ein
std::array
mit fixer Größe verwenden willst, gibt es auch die Möglichkeit, die Länge des Arrays automatisch vom Compiler bestimmen zu lassen. Dazu macht manfunktion
zu einer Template-Funktion und kann die Array-Länge deduzieren lassen (d.h. vom Compiler aus dem jeweils übergebenen Argument herleiten lassen):#include <cstdint> ... template <std::size_t N> void funktion(array<string, N> fc_array) ... array<string, 3> arr = {string1,string2,string3}; funktion(arr);
Hierbei erzeugt der Compiler für jede Array-Länge, mit der du die Funktion in deinem Programm aufrufst, eine Template-Instanz der Funktion mit jeweils der passenden Länge im Funktionsparameter. Das ist, als würdest du für jede Länge, die du brauchst, eine individuelle
funktion
schreiben.Noch ein Hinweis: Du übergibst das Array "by Value", d.h. es wird bei jedem Funktionsaufruf eine Kopie des Array angelegt. Bei einem
array<int, 3>
wäre das völlig in Ordnung, bei einemarray<string, 3>
fängt das jedoch an, etwas teurer zu werden, da hier jeder String kopiert werden muss (dynamische Speicher-Reservierung und Kopie des unter Umständen recht langen Strings). Wenn also keine guten Gründe für die Übergabe "by Value" sprechen, empfielt es sich, das Array z.B. als Referenz entgegenzunehmen:void funktion(const std::array<string, 3>& fc_array)
oder
void funktion(const std::vector<string>& fc_array)