404 Not Found

404 Not Found


nginx

ポインタと配列

[第]16[課で]arrayについて学びましたが、[array]が[一体何なのか]については[説明しませんでした]。

[実は]、配列とは——[そこに格納されているのは]配列の[先頭要素の]アドレスです。

[この点を理解すれば]、[なぜ]array[が引数として使われると]「ポインタに『退化』するのか」が理解できる。


1. array[名]はポインタである

(1) 1.1 [基本的事実]

array[名]は、先頭の要素を指すポインタです。

CPP
#include iostream

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 
 std::cout << "arr = " << arr << std::endl;
 std::cout << "&arr[0] = " << &arr[0] << std::endl;
 // output! arr &arr[0]
 
 return 0;
}

出力(例):

TEXT
arr = 0x7ffd4a3b
&arr[0] = 0x7ffd4a3b

💡 [重要]: arr [と] &arr[0] [は][同じ]アドレスです。



2. [ポインタ]を使って[配列]の[要素]に[アクセス]する

基本的な使い方

CPP
#include iostream

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 int* p = arr; // p
 
 //:()
 std::cout << "arr[0] = " << p[0] << std::endl; // 10
 std::cout << "arr[1] = " << p[1] << std::endl; // 20
 
 //:
 std::cout << "*p = " << *p << std::endl; // 10
 std::cout << "*(p+1) = " << *(p+1) << std::endl; // 20
 
 return 0;
}

💡 [要点]: p[i] [つまり] *(p+i)([後述する]ポインタ[演算])。



3. ポインタ[算術](Pointer Arithmetic)

(1) 3.1 ポインタ[整数の加算・減算]

ポインタ[整数を加減算する場合]、[単にアドレス値を加算・減算するのではなく]、[整数] × sizeof(type) [個のバイト]を加減算する。

CPP
#include iostream

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 int* p = arr; // arr[0]
 
 std::cout << "p = " << p << std::endl; // 0x1000
 std::cout << "p + 1 = " << p + 1 << std::endl; // 0x1004( 0x1001!)
 std::cout << "p + 2 = " << p + 2 << std::endl; // 0x1008
 
 return 0;
}

[原理]:


(2) 3.2 ポインタ[減算]

[2つの]ポインタを[引き算すると]、[得られるのはそれらの間の距離]([要素の数])であり、[バイト数]ではない。

CPP
#include iostream

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 int* p1 = &arr[0]; // arr[0]
 int* p2 = &arr[3]; // arr[3]
 
 std::cout << "p2 - p1 = " << p2 - p1 << std::endl; // 3()
 
 return 0;
}

💡 [要点]: [[同じ]配列[を指す]ポインタ同士を[引き算すること]にのみ意味がある]。



4. [ポインタを使って]配列を[走査する]([3つの方法])

▶ サンプル 1: [配列の走査] (難易度 ⭐)

CPP
#include iostream

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 
 //:()
 std::cout << ":" << std::endl;
 for (int i = 0; i < 5; i++) {
 std::cout << arr[i] << " ";
 }
 std::cout << std::endl;
 
 //:
 std::cout << ":" << std::endl;
 for (int* p = arr; p < arr + 5; p++) {
 std::cout << *p << " ";
 }
 std::cout << std::endl;
 
 //:()
 // std::cout << ":std::begin/end" << std::endl;
 // for (int* p = std::begin(arr); p != std::end(arr); p++) {
 // std::cout << *p << " ";
 // }
 
 return 0;
}
▶ 試してみよう

出力:

10 20 30 40 50:
10 20 30 40 50 

💡 [おすすめ]: [方法1]([添字])[最も直感的]で、[優先して使う]。[方法2](ポインタ[算術演算])[低レベル]のコード[でよく見られる]。



5. array[引数としての退化]

(1) 5.1 [問題]:配列[はポインタに]退化してしまう

[arrayを]function[に渡すと]、arrayは[ポインタに][変換され]([サイズ情報が失われる])。

CPP
#include iostream

void printSize(int arr) {
 // arr pointer,array!
 std::cout << "sizeof(arr) = " << sizeof(arr) << std::endl; // 8(64system,pointer)
}

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 
 std::cout << "sizeof(arr) = " << sizeof(arr) << std::endl; // 20(5 × 4 = 20 lessons of)
 printSize(arr);
 
 return 0;
}

💡 [要点]: printSize 関数[内の] arr [は実際には]ポインタであり、[したがって] sizeof(arr) [は]ポインタ[のサイズ](8 [バイト])であり、[配列の]サイズ(20 [バイト])[ではない]。


(2) 5.2 [解決方法]:[引数]array[サイズ]

CPP
#include iostream

// ...
void printArray(int arr, int size) {
 for (int i = 0; i < size; i++) {
 std::cout << arr[i] << " ";
 }
 std::cout << std::endl;
}

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 printArray(arr, 5); // ()
 return 0;
}

💡 [ヒント]: [後ほど学習する] std::array [および] std::vectorは、[サイズ情報を保持できる]ため、[より安全]です。



6. 演習:[反転]array

▶ サンプル 2: [用]ポインタ[反転]配列 (難易度 ⭐⭐)

CPP
#include iostream

void reverseArray(int* arr, int size) {
 int* left = arr; // ...
 int* right = arr + size - 1; // ...
 
 while (left < right) {
 // left right
 int temp = *left;
 *left = *right;
 *right = temp;
 
 left++; // ...
 right--; // ...
 }
}

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 
 std::cout << ":";
 for (int i = 0; i < 5; i++) {
 std::cout << arr[i] << " ";
 }
 std::cout << std::endl;
 
 reverseArray(arr, 5);
 
 std::cout << ":";
 for (int i = 0; i < 5; i++) {
 std::cout << arr[i] << " ";
 }
 std::cout << std::endl;
 
 return 0;
}
▶ 試してみよう

出力:

20 30 40 50:50 40 30 20 10


7. [よくある]エラー

(1) 7.1 ポインタ[範囲外アクセス]

CPP
#include iostream

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 int* p = arr;
 
 std::cout << *(p + 10) << std::endl; // ❌!p+10 array
 // output,
 
 return 0;
}

💡 [ヒント]: C++では、[ポインタの範囲外アクセス]は自動的にチェックされません![これが] C++の[高速さの理由の一つ]ですが、[同時によくある]バグの[原因]でもあります。


(2) 7.2 ポインタ[引き算は意味をなさない]

CPP
#include iostream

int main() {
 int x = 5;
 int y = 10;
 int* p1 = &x;
 int* p2 = &y;
 
 std::cout << p2 - p1 << std::endl; // ❌ Definebehavior!p1 and p2 array
 // ...
 
 return 0;
}

❓ よくある質問

Q [なぜ] arr [と] &arr [の]値[は同じ]なのに、[型][が違う]のですか?
A > - arrの型はint*intを[指す]ポインタ) > - &arrの型はint(*)[5]([「5個のintを含む配列」を][指す]ポインタ)
Q ポインタ[算術演算が可能] * [和] / [ですか]?
A [できません]!pointer[できるのは]: > - [整数の加算・減算](p + np - n) > - [2つの]pointer[の引き算](p2 - p1) > - [大小の比較](p1 < p2

📖 まとめ


📝 練習問題

  1. 初心者(難易度 ⭐): [1つ宣言] int 配列 arr[5] = {1, 2, 3, 4, 5}、[さらに1つ宣言] int* ポインタ p [が指す先] arr。[ポインタ] を使用して [すべての要素] を出力する。

  2. 中級(難易度 ⭐⭐): [関数] int* findElement(int* arr, int size, int target) を作成し、[配列] の中から目的の [値] を検索する。[見つかった場合]、[その要素を指す] ポインタを返す。[見つからなかった場合]、nullptr を返す。

  3. 上級(難易度 ⭐⭐⭐): [「バブルソート」を]ポインタ[を使って][実装する]プログラムを[作成する]:


8. 🚀 次は

[ポインタと配列]を学んだので、[次は] ポインタの算術演算の応用編([第]24[課])—— [ポインタの]型、void* ポインタ、[および多重]ポインタについて[深く理解]します。

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%