Programming組み込み開発者, バックエンド開発者

Cにおける条件演算子(3項演算子)の構造と動作について説明してください。使用時に関連する型変換や副作用に関する落とし穴は何ですか?

Hintsage AIアシスタントで面接を突破

答え

3項条件演算子(?:)は、条件に応じて2つの式のうちの1つを計算して返すことができます。

result = cond ? expr_true : expr_false;
  • 結果の型は、「通常の算術変換」の一般的なルールに基づいて決定されます(両方の式が数値である場合)または、最大のオペランドの型に基づきます(ポインタや構造体の場合)。
  • 両方の式は型が互換性がある必要があります。型が異なる場合、コンパイラはそれらを共通の型に変換しようとしますが、時には予期しない結果やデータの損失を引き起こすことがあります。
  • 3項演算子は、両方の式に副作用を持つことを許可していますが、条件に基づいて必要な式だけが計算されます。
  • 複雑な式では、曖昧さや予測不可能な動作の問題が発生することがあります(特に入れ子やマクロや関数との使用時)。

int a = 10, b = 0; int max = (a > b) ? a : b; // max = 10

トリッククエスチョン

「テナリー演算子の2つの式が異なる型のオブジェクトを返す場合(例えば、ポインタと0)、結果の型は何になりますか?」

多くの人は、コンパイラが常に結果の型を「推測する」と主張します。実際には、1つの式がポインタで、別の式が0またはNULLである場合、結果はポインタの型になります。ただし、より複雑な違いがある場合(例えば、異なる型のポインタやint型とenum型が返される場合)、明らかな情報損失が発生する可能性があり、時にはコンパイラがエラーを出すこともあります。

struct node *p = NULL; void *v = cond ? p : NULL; // ok void *z = cond ? p : 0; // ok int i = cond ? 0 : "abc"; // エラー:不適合な型

このテーマの微妙な点を知らずに実際に発生したエラーの例


物語

クロスコンパイルを使用した大規模プロジェクトでは、cond ? ptr : 0という表現があるコンパイラではポインタを返し、別のコンパイラではintを返しました(ここで0は厳密にintとして解釈されました)。結果をポインタとして使用しようとするとシステムがクラッシュしました。


物語

財務パッケージでは、戻り値の関数が「生」のリテラル (cond ? 0.0 : 1) を使用していて、結果の型が不注意にdoubleになり、intが期待されていたために比較と印刷のエラーが発生しました。


物語

あるライブラリでは、副作用を伴う式の呼び出しが行われました:flag ? inc(x) : dec(x)。リファクタリング中に隠れたエラーが発生し、両方の式が関数を呼び出したため(それぞれの副作用があった)、1つだけが実行されることが期待されていました。入れ子になったマクロによる混乱が2重の値の変更を引き起こし、詳細なテスト中にのみ発見されました。