Programmingシステムプログラマー

C言語の関数における引数の渡し方(値渡しとポインタ渡し)について詳しく説明できますか?各アプローチの使用が正当化される例を挙げてください。

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

回答。

C言語では、関数の引数は常に値渡しされます。つまり、呼び出し元のコードから値のコピーが関数に渡されます。関数の外で変数の値を変更する必要がある場合は、ポインタを使って渡します。

値渡し

スカラー(例えば、int)を渡す場合、関数はそのコピーを受け取ります:

void foo(int a) { a = 10; } int main() { int x = 5; foo(x); // x == 5, 変更されません! }

ポインタ渡し

変数の値を変更するためにはポインタを使います:

void foo(int* a) { *a = 10; } int main() { int x = 5; foo(&x); // x == 10, 値が変更されました! }

ポインタを使用する理由

  • 複数の値を返す必要がある。
  • 大きな構造体を変更する(時間とメモリの節約、コピーがない)。
  • 配列と作業するため(配列は常にポインタとして渡される)。

トリッキーな質問。

配列は関数の引数として参照渡しですか?

多くの人が「参照渡し」と答えますが、C言語では、関数のシグネチャで配列は参照渡しされることはなく、実際にはポインタにダウングレードされます。

正しい答え:

配列が関数に渡されると、実際にはその最初の要素へのポインタが渡されます。つまり、呼び出された関数は配列の実際のサイズを知らず、配列の要素への変更は元の配列に影響します。

void foo(int arr[]) { arr[0] = 100; } int main() { int a[3] = {1,2,3}; foo(a); // a[0] は100になります! }

歴史


あるプロジェクトで、関数がint arr[10]として宣言された配列の値を更新していましたが、呼び出し元のコードでは配列のサイズが小さいものでした。関数は実際のサイズを知らなかったため、バッファオーバーフローが発生し、メモリが破損しました。


別のケースでは、構造体が値渡しで関数に渡されたため、大きなメモリブロックが何度もコピーされ、アプリケーションのパフォーマンスが低下しました。


開発者はスカラーを「参照」で(ポインタを介して)渡すことで元の値の不変性を保証されると期待していましたが、誤ってポインタを通じてスコープの外のメモリを変更してしまい(ポインタ算術のエラー)、予測できない結果を引き起こしました。