C および C++ プログラミングの壮大な世界に参加する準備はできていますか? 数行の簡単な C++ を学習した後、自分の存在に疑問を感じてみませんか? あなたの答えが「うん!」「そうだね」「なぜダメなの?」なら、あなたの知識をテストしましょう。C または C++ に関連する複数の質問が出題されます。 正解と解説は物語の最後にあります。頑張ってください! 1. 最小のプログラム main; このプログラムを C コンパイラを使用してコンパイルしようとすると何が起こるでしょうか? コンパイルされない コンパイルはされるが、リンクはされない コンパイルしてリンクします 2. フォーク #include <iostream> #include <unistd.h> int main() { for(auto i = 0; i < 1000; i++) std::cout << "Hello world!\n"; fork(); } このプログラムは何行印刷しますか? 1000 1000未満 1000以上 3. 必要なのはインデックスだけ #include <iostream> int main() { int array[] = { 1, 2, 3 }; std::cout << (4, (1, 2)[array]) << std::endl; } このプログラムは何を印刷しますか? 1 2 3 4 コンパイルされない 未定義 4. 正規表現 #include <regex> #include <iostream> int main() { std::regex re("(.*|.*)*O"); std::string str("0123456789"); std::cout << std::regex_match(str, re); return 0; } この正規表現がこの入力文字列と一致するまでどのくらいの時間がかかりますか? 1ミリ秒 1秒 1分 1時間 1年 永遠に 5. 移動とラムダ #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; } このプログラムによって印刷される最後の行は… Foo() Foo(Foo&&) Foo(const Foo&) 6. Xとバー #include <iostream> int x = 0; int bar(int(x)); int main() { std::cout << bar; } このプログラムは何を印刷しますか? 0 1 0x0 コンパイルされない リンクしません 7. コンストラクター #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(); } このプログラムによって印刷される最後の行は… Foo(int, int) Foo(const Foo&, int) Foo(int, const Foo&) Foo(int) 結論の代わりに この奇妙な断片が世の中で見つかることが決してないことを願います。 回答 最小のプログラム これは正当な C コードです。 実行しようとするとクラッシュします。main - はグローバル変数です。 コンパイルとリンクは正常に行われます。 main; C コードでは、多くのものを省略できます。たとえば、グローバル変数の型を省略できます。デフォルトでは、コンパイラはこの型が であると想定します。また、C では が行われないため (C++ とは異なります)、リンク時に変数 と関数 を区別する方法はありません。 int 名前のマングリング main main したがって、コンパイラは有効なコードをコンパイルし、リンカーはオブジェクト ファイル内で という名前のものを見つけてプログラムをリンクします。 main フォーク これは、C や C++ の機能というよりは、POSIX の機能です。IO 操作の実装では、パフォーマンスを最適化するためにバッファを使用します。fork 呼び出すと、OS はプロセス メモリのコピーオンライト複製を作成し、IO バッファも複製される可能性が高く、バッファリングされた文字列 。 fork は 1000 回以上印刷される可能性があります 必要なのはインデックスだけ 答えは3です このコードを理解するために、C および C++ でのインデックスの動作を詳しく見てみましょう。array は と同じであり、 と同じであり、 と同じです。 array[index] *(array + index) (index + array) index[array 2 番目のヒントは演算子 です。これは二項演算子であり、左の引数を破棄して右の引数を返します。 , 正規表現 何が起こるか 。動作は実装次第です。 を予測することは不可能です 私の環境では、このプログラムは例外を発生させます The complexity of an attempted match against a regular expression exceeded a pre-set level. 他の可能性のあるオプションは、驚くほど長い時間がかかるか、期待どおりに動作するかです。これは、正規表現を実装するための 2 つのアプローチが考えられるためです。 まず、正規表現を有限オートマトンに変換します (n - パターンの長さ))。文字列を一致させるには (m - 文字列の長さ) が必要です。このアプローチでは O(n**2) O(m) バックトラッキングはサポートされません。 2 番目 - 貪欲なアプローチ + DFS は、バックトラッキングをサポートしますが、特定のパターンでは指数関数的な時間複雑性が発生する傾向があります。 移動とラムダ 答えは です。ラムダはデフォルトで不変であり、 を使用してラムダにキャプチャされたすべての値は暗黙的に です。これにより、ラムダの 動作が解除されます。 Foo(const Foo&) [] const べき等 を移動すると が作成されます。const は奇妙な型なので、コンパイラは をコピーするだけです。 f const Foo&& const Foo&& Foo これを修正するには 2 つの方法があります。 可変ラムダを作成する auto a = [f = std::move(f)]() mutable { return std::move(f); }; コンストラクタ を宣言します。 Foo(const Foo&&) Xとバー 出力します。 プログラムは 1 — は関数を宣言する奇妙な方法ですが、 と同じです。 int bar(int(x)); int bar(int x); 型キャストと混同している場合は、 - これは型キャストです。 int bar((int(x))); 関数アドレスを に暗黙的にキャストしようとすると、そのようなキャストの結果は常に なります。 bool true 関数 一度も使用されていないため、リンク時に参照されていないシンボルのエラーを回避できます。 bar() コンストラクター 最後の行は です。 Foo(const Foo&, int) は変数宣言であり、 と同じです。したがって、 という名前のクラス メンバーはこのスコープ内で隠されています。 Foo(i) Foo i i