ProgrammingC 開発者

C言語におけるポインタや配列を関数に渡す際に何が起こるのか?それらの違い、注意点、一般的な間違いを避ける方法は?

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

回答。

C言語において、ポインタまたは配列を関数に渡す際に実際にはポインタの値(すなわちメモリアドレス)のコピーが渡されるだけであり、配列そのものやメモリの内容が渡されるわけではありません。C言語では配列は値として渡されず、代わりに配列の最初の要素へのポインタが関数へ渡されます。このメカニズムはメモリを節約しますが、誤った使い方をすることで望ましくない副作用を招く可能性があります。

問題 — ポインタと配列の混同:開発者はしばしば、関数内では配列を変更できないと考えたり、関数が渡された配列のサイズを自動的に知っていると誤解したりします。実際には関数は配列の元々のサイズを失い、簡単にその範囲を超えることができます。

解決策 — 常に配列のサイズを別の引数として明示的に渡し、ポインタのコピーとオブジェクトのコピーの違いを明確に理解し、ポインタを介した変更が元のデータに反映されることを忘れないことです。

配列を正しく渡す例:

void print_array(const int* arr, size_t size) { for (size_t i = 0; i < size; ++i) printf("%d ", arr[i]); } int main() { int nums[] = {1,2,3,4,5}; print_array(nums, sizeof(nums)/sizeof(nums[0])); return 0; }

重要な特徴:

  • 配列のアドレスが関数に渡され、要素のコピーは渡されない。
  • 配列のサイズは明示的に渡す必要がある。
  • 関数内での要素の変更は外部の配列に影響を与える。

騙しの質問。

関数内で int arr[10] として宣言された配列の長さを関数が知ることができるか?

回答:いいえ、関数内で sizeof(arr) の式はポインタのサイズを返し、配列全体のサイズではありません。サイズは別途渡す必要があります。

関数における配列の渡し方として int arr[] と int arr は同じか?*

回答:はい、関数のシグネチャにおいては同等で、どちらも int へのポインタが渡されます。違いは文法にのみあります。

関数内で配列の要素を変更した場合、元の配列は変更されるのか?

回答:はい、ポインタが渡されたため、関数はそのポインタが指すメモリを変更します。

典型的な間違いとアンチパターン

  • 配列のサイズを引数として渡さない(範囲外参照)。
  • 関数内で配列のサイズを判断するために sizeof(arr) を使用する(間違った結果)。
  • 異なるコンテキストで int arr[10] と int* arr を混同する。

実例

ネガティブケース

プロジェクトは配列を初期化する関数を実装し、内部で配列のサイズを sizeof(arr) / sizeof(arr[0]) で定義していました。テスト段階では関数は機能しましたが、他の配列を処理すると異なるメモリを上書きしたり、正しく動作しないことがありました。

利点:

  • 関数のシグネチャが簡素化されました。

欠点:

  • プログラムは他のデータでクラッシュしたり、正しく動作しなくなりました。

ポジティブケース

関数は常に配列のサイズを別のパラメータとして受け取り、長さは呼び出し側が計算しました。関数内部では渡されたパラメータのみで作業しました。

利点:

  • 範囲外参照がない保証。
  • より信頼性が高く、安全で移植性のあるコード。

欠点:

  • サイズを明示的に渡す必要があり、呼び出しがやや長くなります。