Sind Sie bereit, in die majestätische Welt der C- und C++-Programmierung einzutauchen? Wollen Sie Ihre Existenz nach ein paar einfachen Zeilen C++ hinterfragen? Wenn Ihre Antwort „Yeh!“, „Jep“ oder „Warum nicht?“ ist, können Sie Ihr Wissen gerne testen. Sie erhalten mehrere Fragen zu C oder C++. Die richtigen Antworten und Erklärungen finden Sie am Ende der Geschichte. Viel Glück! 1. Das kleinste Programm main; Was passiert, wenn Sie versuchen, dieses Programm mit dem C-Compiler zu kompilieren? wird nicht kompiliert wird kompiliert, wird nicht verlinkt wird kompilieren und wird verknüpfen 2. Die Gabel #include <iostream> #include <unistd.h> int main() { for(auto i = 0; i < 1000; i++) std::cout << "Hello world!\n"; fork(); } Wie viele Zeilen druckt dieses Programm? 1000 weniger als 1000 mehr als 1000 3. Alles was Sie brauchen sind Indizes #include <iostream> int main() { int array[] = { 1, 2, 3 }; std::cout << (4, (1, 2)[array]) << std::endl; } Was wird dieses Programm drucken? 1 2 3 4 wird nicht kompiliert undefiniert 4. Reguläre Ausdrücke #include <regex> #include <iostream> int main() { std::regex re("(.*|.*)*O"); std::string str("0123456789"); std::cout << std::regex_match(str, re); return 0; } Wie lange dauert es, bis dieser reguläre Ausdruck mit dieser Eingabezeichenfolge übereinstimmt? 1 ms 1 Sek. 1 Minute 1 Stunde 1 Jahr für immer 5. Bewegungen und Lambdas #include <iostream> struct Foo { Foo() { std::cout << "Foo()\n"; } Foo(Foo&&) { std::cout << "Foo(Foo&&)\n"; } Foo(const Foo&) { std::cout << "Foo(const Foo&)\n"; } }; int main() { Foo f; auto a = [f = std::move(f)]() { return std::move(f); }; Foo f2(a()); return 0; } Die letzte von diesem Programm auszugebende Zeile ist … Foo() Foo(Foo&&) Foo(const Foo&) 6. X und Balken #include <iostream> int x = 0; int bar(int(x)); int main() { std::cout << bar; } Was wird dieses Programm drucken? 0 1 0x0 wird nicht kompiliert wird nicht verlinken 7. Konstruktoren #include <iostream> struct Foo { Foo() { std::cout << "Foo()\n"; } Foo(const Foo&) { std::cout << "Foo(const Foo&)\n"; } Foo(int) { std::cout << "Foo(int)\n"; } Foo(int, int) { std::cout << "Foo(int, int)\n"; } Foo(const Foo&, int) { std::cout << "Foo(const Foo&, int)\n"; } Foo(int, const Foo&) { std::cout << "Foo(int, const Foo&)\n"; } }; void f(Foo) {} struct Bar { int i, j; Bar() { f(Foo(i, j)); f(Foo(i)); Foo(i, j); Foo(i); Foo(i, j); } }; int main() { Bar(); } Die letzte von diesem Programm auszugebende Zeile ist … Foo(int, int) Foo(const Foo&, int) Foo(int, const Foo&) Foo(int) Statt Schlussfolgerung Ich hoffe, dass Sie niemals einen dieser eigenartigen Schnipsel in freier Wildbahn finden werden. Antworten Das kleinste Programm Dies ist gültiger C-Code. Beim Versuch, ihn auszuführen, stürzt er ab. - ist eine globale Variable. Er wird erfolgreich kompiliert und verknüpft. main; In C-Code können Sie viele Dinge weglassen. Sie können beispielsweise den Typ einer globalen Variable weglassen. Der Compiler geht standardmäßig davon aus, dass dieser Typ ein ist. Außerdem gibt es in C keine (anders als in C++), sodass beim Verknüpfen die Variable nicht von der Funktion unterschieden werden kann. int Namensverfälschung main main Somit kompiliert der Compiler gültigen Code und der Linker findet etwas mit dem Namen in der Objektdatei, um ein Programm zu verknüpfen. main Die Gabel Dies ist eher eine POSIX-Funktion als eine C- oder C++-Funktion. Implementierungen von IO-Operationen verwenden Puffer zur Leistungsoptimierung. Wenn Sie aufrufen, erstellt das Betriebssystem ein Copy-on-Write-Duplikat des Prozessspeichers, die IO-Puffer werden wahrscheinlich ebenfalls dupliziert und gepufferte Zeichenfolgen . fork werden wahrscheinlich mehr als 1000 Mal gedruckt Alles was Sie brauchen sind Indizes Die Antwort ist 3 Um diesen Code zu verstehen, schauen wir uns genauer an, wie Indizes in C und C++ funktionieren: ist dasselbe wie , ist dasselbe wie und dasselbe wie . array[index] *(array + index) (index + array) index[array Der zweite Hinweis ist der Operator . Sein binärer Operator verwirft das linke Argument und gibt das rechte Argument zurück. , Reguläre Ausdrücke Es ist was passieren wird! Das Verhalten hängt von der Umsetzung ab. unmöglich vorherzusagen, In meiner Umgebung löst dieses Programm die Ausnahme aus The complexity of an attempted match against a regular expression exceeded a pre-set level. Andere mögliche Optionen dauern überraschend lange oder funktionieren wie erwartet. Das liegt daran, dass es zwei mögliche Ansätze zur Implementierung regulärer Ausdrücke gibt. Erstens: Transformieren Sie reguläre Ausdrücke in endliche Automaten (n - Länge des Musters), Match-String (m - Länge des Strings). Dieser Ansatz unterstützt kein O(n**2) O(m) Backtracking. Zweitens: Greedy-Ansatz + DFS, unterstützt Backtracking, neigt aber bei bestimmten Mustern zu exponentieller Zeitkomplexität. Bewegungen und Lambdas Die Antwort lautet . Lambdas sind standardmäßig unveränderlich, alle mit in Lambdas erfassten Werte sind implizit . Dies schaltet Verhalten für Lambdas frei. Foo(const Foo&) [] const idempotentes Wenn Sie verschieben, erstellen Sie . ist ein seltsamer Typ, daher kopiert der Compiler einfach f const Foo&& const Foo&& Foo Es gibt zwei Möglichkeiten, dieses Problem zu beheben: Veränderbares Lambda erstellen auto a = [f = std::move(f)]() mutable { return std::move(f); }; Deklarieren Sie den Konstruktor Foo(const Foo&&) X und Balken aus. Das Programm gibt 1 — ist eine seltsame Art, eine Funktion zu deklarieren, es ist gleich . int bar(int(x)); int bar(int x); Falls Sie es mit der Typumwandlung verwechseln: – das ist eine Typumwandlung. int bar((int(x))); Dann versuchen wir, die Funktionsadresse implizit in umzuwandeln. Das Ergebnis einer solchen Umwandlung ist immer “. bool true Die Funktion wurde nie verwendet, wodurch wir beim Verknüpfen den Fehler „nicht referenziertes Symbol“ umgehen konnten. bar() Konstruktoren Die letzte Zeile ist . Foo(const Foo&, int) ist eine Variablendeklaration, dasselbe wie . Daher ist das Klassenmitglied mit dem Namen in diesem Bereich verborgen. Foo(i) Foo i i