paint-brush
おそらくあなたが知らない C と C++ の隠れた宝石@udalov
11,234 測定値
11,234 測定値

おそらくあなたが知らない C と C++ の隠れた宝石

Ilia6m2024/08/09
Read on Terminal Reader

長すぎる; 読むには

C++ は、機能が多すぎる素晴らしい言語です。私は C++ 開発者としてのキャリアを通じて、何度も「何だこれ」という瞬間を経験しました。このストーリーでは、C および C++ プログラミングの最も面白い側面がテストの形で凝縮されています。2、3 問以上正解できるはずがありません。
featured image - おそらくあなたが知らない C と C++ の隠れた宝石
Ilia HackerNoon profile picture
0-item
1-item

C および C++ プログラミングの壮大な世界に参加する準備はできていますか? 数行の簡単な C++ を学習した後、自分の存在に疑問を感じてみませんか?


あなたの答えが「うん!」「そうだね」「なぜダメなの?」なら、あなたの知識をテストしましょう。C または C++ に関連する複数の質問が出題されます。


正解と解説は物語の最後にあります。頑張ってください!

1. 最小のプログラム

main;


このプログラムを C コンパイラを使用してコンパイルしようとすると何が起こるでしょうか?

  1. コンパイルされない
  2. コンパイルはされるが、リンクはされない
  3. コンパイルしてリンクします

2. フォーク

#include <iostream> #include <unistd.h> int main() { for(auto i = 0; i < 1000; i++) std::cout << "Hello world!\n"; fork(); }


このプログラムは何行印刷しますか?

  1. 1000
  2. 1000未満
  3. 1000以上

3. 必要なのはインデックスだけ

#include <iostream> int main() { int array[] = { 1, 2, 3 }; std::cout << (4, (1, 2)[array]) << std::endl; }


このプログラムは何を印刷しますか?

  1. 1
  2. 2
  3. 3
  4. 4
  5. コンパイルされない
  6. 未定義

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ミリ秒
  2. 1秒
  3. 1分
  4. 1時間
  5. 1年
  6. 永遠に

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; }


このプログラムによって印刷される最後の行は…


  1. Foo()
  2. Foo(Foo&&)
  3. Foo(const Foo&)

6. Xとバー

#include <iostream> int x = 0; int bar(int(x)); int main() { std::cout << bar; }


このプログラムは何を印刷しますか?

  1. 0
  2. 1
  3. 0x0
  4. コンパイルされない
  5. リンクしません

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(); }


このプログラムによって印刷される最後の行は…

  1. Foo(int, int)
  2. Foo(const Foo&, int)
  3. Foo(int, const Foo&)
  4. Foo(int)

結論の代わりに

この奇妙な断片が世の中で見つかることが決してないことを願います。

回答

  1. 最小のプログラム


    これは正当な C コードです。コンパイルとリンクは正常に行われます。実行しようとするとクラッシュします。main main; - はグローバル変数です。


    C コードでは、多くのものを省略できます。たとえば、グローバル変数の型を省略できます。デフォルトでは、コンパイラはこの型がintであると想定します。また、C では名前のマングリングが行われないため (C++ とは異なります)、リンク時に変数mainと関数mainを区別する方法はありません。


    したがって、コンパイラは有効なコードをコンパイルし、リンカーはオブジェクト ファイル内でmainという名前のものを見つけてプログラムをリンクします。


  2. フォーク


    これは、C や C++ の機能というよりは、POSIX の機能です。IO 操作の実装では、パフォーマンスを最適化するためにバッファを使用します。fork fork呼び出すと、OS はプロセス メモリのコピーオンライト複製を作成し、IO バッファも複製される可能性が高く、バッファリングされた文字列は 1000 回以上印刷される可能性があります


  3. 必要なのはインデックスだけ


    答えは3です


    このコードを理解するために、C および C++ でのインデックスの動作を詳しく見てみましょう。array array[index]*(array + index)と同じであり、 (index + array)と同じであり、 index[arrayと同じです。

    2 番目のヒントは演算子,です。これは二項演算子であり、左の引数を破棄して右の引数を返します。


  4. 正規表現


    何が起こるかを予測することは不可能です。動作は実装次第です。


    私の環境では、このプログラムは例外を発生させますThe complexity of an attempted match against a regular expression exceeded a pre-set level.


    他の可能性のあるオプションは、驚くほど長い時間がかかるか、期待どおりに動作するかです。これは、正規表現を実装するための 2 つのアプローチが考えられるためです。


    まず、正規表現を有限オートマトンに変換しますO(n**2) (n - パターンの長さ))。文字列を一致させるにはO(m) (m - 文字列の長さ) が必要です。このアプローチではバックトラッキングはサポートされません。


    2 番目 - 貪欲なアプローチ + DFS は、バックトラッキングをサポートしますが、特定のパターンでは指数関数的な時間複雑性が発生する傾向があります。


  5. 移動とラムダ


    答えはFoo(const Foo&)です。ラムダはデフォルトで不変であり、 []を使用してラムダにキャプチャされたすべての値は暗黙的にconstです。これにより、ラムダのべき等動作が解除されます。


    fを移動するとconst Foo&&が作成されます。const const Foo&&は奇妙な型なので、コンパイラはFooをコピーするだけです。


    これを修正するには 2 つの方法があります。


    1. 可変ラムダを作成する

      auto a = [f = std::move(f)]() mutable { return std::move(f); };


    2. コンストラクタFoo(const Foo&&)を宣言します。


  6. Xとバー

    プログラムは1出力します。


    int bar(int(x)); — は関数を宣言する奇妙な方法ですが、 int bar(int x);と同じです。

    型キャストと混同している場合は、 int bar((int(x))); - これは型キャストです。


    関数アドレスをboolに暗黙的にキャストしようとすると、そのようなキャストの結果は常にtrueなります。


    関数bar()一度も使用されていないため、リンク時に参照されていないシンボルのエラーを回避できます。


  7. コンストラクター

    最後の行はFoo(const Foo&, int)です。


    Foo(i)は変数宣言であり、 Foo iと同じです。したがって、 iという名前のクラス メンバーはこのスコープ内で隠されています。