From b594322711cfef1f123578a3224cdbbc6b605bb2 Mon Sep 17 00:00:00 2001 From: yas-ako <105139975+yas-ako@users.noreply.github.com> Date: Fri, 8 May 2026 08:53:28 +0900 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E7=AC=AC6~8=E7=AB=A0=E3=82=92?= =?UTF-8?q?=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/.vitepress/config.ts | 29 ++++++ docs/cpp/chapter-6/1.md | 53 +++++++++- docs/cpp/chapter-6/2.md | 24 ++++- docs/cpp/chapter-6/3.md | 85 ++++++++++++++- docs/cpp/chapter-6/4.md | 167 +++++++++++++++++++++++++++++- docs/cpp/chapter-6/5.md | 102 +++++++++++++++++- docs/cpp/chapter-6/index.md | 5 +- docs/cpp/chapter-7/1.md | 157 +++++++++++++++++++++++++++- docs/cpp/chapter-7/2.md | 124 +++++++++++++++++++++- docs/cpp/chapter-7/3.md | 95 ++++++++++++++++- docs/cpp/chapter-7/4.md | 135 +++++++++++++++++++++++- docs/cpp/chapter-7/index.md | 4 +- docs/cpp/chapter-8/1.md | 105 ++++++++++++++++++- docs/cpp/chapter-8/2.md | 85 ++++++++++++++- docs/cpp/chapter-8/index.md | 5 +- docs/cpp/chapter-8/struct-raw.cpp | 25 +++++ docs/cpp/chapter-8/struct.cpp | 35 +++++++ docs/cpp/index.md | 14 +++ docs/index.md | 14 +++ 19 files changed, 1249 insertions(+), 14 deletions(-) create mode 100644 docs/cpp/chapter-8/struct-raw.cpp create mode 100644 docs/cpp/chapter-8/struct.cpp diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 2ad7ef07..8a92df73 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -136,6 +136,35 @@ export default withMermaid({ }, ], }, + { + text: "[WIP] 6. 繰り返し処理", + link: "/cpp/chapter-6/", + items: [ + { text: "[WIP] 6.1 for文", link: "/cpp/chapter-6/1" }, + { text: "[WIP] 6.2 while文", link: "/cpp/chapter-6/2" }, + { text: "[WIP] 6.3 配列", link: "/cpp/chapter-6/3" }, + { text: "[WIP] 6.4 string型②とchar型", link: "/cpp/chapter-6/4" }, + { text: "[WIP] 6.5 continueとbreak", link: "/cpp/chapter-6/5" }, + ], + }, + { + text: "[WIP] 7. 関数", + link: "/cpp/chapter-7/", + items: [ + { text: "[WIP] 7.1 関数とは", link: "/cpp/chapter-7/1" }, + { text: "[WIP] 7.2 引数", link: "/cpp/chapter-7/2" }, + { text: "[WIP] 7.3 返り値", link: "/cpp/chapter-7/3" }, + { text: "[WIP] 7.4 [発展] 参照渡し", link: "/cpp/chapter-7/4" }, + ], + }, + { + text: "[WIP] 8. 構造体", + link: "/cpp/chapter-8/", + items: [ + { text: "[WIP] 8.1 構造体とは", link: "/cpp/chapter-8/1" }, + { text: "[WIP] 8.2 メソッド", link: "/cpp/chapter-8/2" }, + ], + }, { text: "2025年度版テキスト", link: "/text/chapter-0/", diff --git a/docs/cpp/chapter-6/1.md b/docs/cpp/chapter-6/1.md index df9a3cd5..5664c18e 100644 --- a/docs/cpp/chapter-6/1.md +++ b/docs/cpp/chapter-6/1.md @@ -1 +1,52 @@ -# 6.1 for文 +# [WIP] 6.1 for文 + +繰り返しの処理 `for` は以下のように記述する。 + +```cpp:line-numbers +for (int i=0; i<5; i++) { + cout << i << endl; +} +``` + +``` +[output] +0 +1 +2 +3 +4 +``` + +新しい演算`++` が出ているが、これは **変数の値を1増やす** という演算である。 `i = i+1;` と同値。 + +for 文がどのように動作しているかを解説すると、`for` の後に `;` 区切りで 3 つ情報を指定している。 + +```cpp +for ([初期化];[条件];[継続処理]) { + [実行文] +} +``` + +```mermaid +flowchart LR + start[Start] --> init[[初期化]] + subgraph for + init[[初期化]] --> cond[[条件]] + cond --> |Yes| work[["処理"]] --> cont[["継続処理"]] --> cond + end + cond --> |No| e["End"] +``` + +例示したコードで当てはめると以下のようになる。 + +```mermaid +flowchart LR + start[Start] --> init[[初期化]] + subgraph for + init[[int i=0]] --> cond[[i<5]] + cond --> |Yes| work[[cout << i]] --> cont[[i++]] --> cond + end + cond --> |No| e["End"] +``` + +つまり、0,1,2,3,4 の整数を出力するプログラムである。これを使えば、例えば $7^4$ 等がプログラムで計算できる。 diff --git a/docs/cpp/chapter-6/2.md b/docs/cpp/chapter-6/2.md index 98eb6633..9a192e62 100644 --- a/docs/cpp/chapter-6/2.md +++ b/docs/cpp/chapter-6/2.md @@ -1 +1,23 @@ -# 6.2 while文 +# [WIP] 6.2 while文 + +```cpp:line-numbers +int a = 10; +while (a > 0) { + cout << a << endl; + a--; +} +``` + +while文は、 `while` の中の条件文が真の間実行される繰り返し処理。 + +上記のコードだと、 10,9,8,7,6,5,4,3,2,1 と順に出力される。 + +```mermaid +flowchart LR + start[Start] --> init[[int a = 10]] + init --> cond[[a>0]] + subgraph while + cond --> |Yes| work[[cout << a]] --> cont[[a--]] --> cond + end + cond --> |No| e["End"] +``` diff --git a/docs/cpp/chapter-6/3.md b/docs/cpp/chapter-6/3.md index c9353205..b714850e 100644 --- a/docs/cpp/chapter-6/3.md +++ b/docs/cpp/chapter-6/3.md @@ -1 +1,84 @@ -# 6.3 配列 +# [WIP] 6.3 配列 + +プログラミングでは、同じ様な変数を複数作りたいときがある。例えば、100人の点数の平均点を取りたい時に変数は100個必要となる。このときに、配列を使う。 + +配列とは、変数の集合のようなものである。 + +C++ において配列を使う時は、`vector` を用いる。`string` と同様にして、`vector`をインクルードする。 + +```cpp:line-numbers +#include +``` + +以下のようにして、`<>`内に型を宣言することで、配列を定義することができる。 + +```cpp:line-numbers +vector array; +vector strarray; +``` + +このように記述すると空の配列が作成される。以下のように、初めから配列に値を入れることもできる + +```cpp:line-numbers +vector arr = {10, 20, 30, 40, 50}; +``` + +以下のように、 `[]` の中に数字を書くことで要素を取得できる。数列を考えた時の $a_i$ の $i$ だと思うと良い。この時要素番号は* +*0始まり**である。 + +```cpp:line-numbers +vector arr = {10, 20, 30, 40, 50}; +cout << arr[2] << endl; +arr[1] = 100; +cout << arr[1] << endl; +``` + +``` +[output] +30 +100 +``` + +`.size()` とすると、配列の要素数を調べることができる。 + +```cpp:line-numbers +vector arr = {10, 20, 30, 40, 50}; +int siz = arr.size(); +cout << siz << endl; +``` + +``` +[output] +5 +``` + +これと、`for` を用いれば、配列の要素を全て取得することができる。 + +```cpp:line-numbers +vector arr = {10, 20, 30, 40, 50}; +for (int i=0; i < arr.size(); i++) { + cout << arr[i] << endl; +} +``` + +`for` 文の中で、 `arr[0]` から `arr[4]` までが1つずつ出力されるコードだという事が理解できるだろうか。`i < arr.size()` +の条件から、`i` は 0 から 4 までの場合で実行される。(5 は `5 < 5` となり条件を満たさない。) + +`.push_back()` を用いると、配列の末尾に新しい要素を追加することができる。 + +```cpp:line-numbers +vector arr = {10, 20, 30, 40, 50}; +arr.push_back(-10); + +cout << arr[5] << endl; +cout << arr.size() << endl; +``` + +``` +[output] +-10 +6 +``` + +for文と組み合わせると、できる事が非常に広がる。 + diff --git a/docs/cpp/chapter-6/4.md b/docs/cpp/chapter-6/4.md index afd0d7f6..ee7c0f11 100644 --- a/docs/cpp/chapter-6/4.md +++ b/docs/cpp/chapter-6/4.md @@ -1 +1,166 @@ -# 6.4 string型②とchar型 +# [WIP] 6.4 string型②とchar型 + +string は、実際には文字の配列であると捉えると、文字列に対する操作を行うことができる。 + +一度、アルファベットと数字・一部の記号だけ、いわゆる「**1バイト文字**」のみで考えよう。ひらがなやカタカナについては後で説明する。 + +:::warning +ひらがな・カタカナ・漢字などの文字を使うと、以下の説明が成り立たなくなってしまうことがある。 +::: + +配列と同じようにして、長さを取得したり、1文字ずつ取得したりすることができる。 + +```cpp:line-numbers +string s = "traP"; +cout << s.size() << endl; // 配列と捉えて配列の要素数を取得する。 +cout << s.length() << endl; // length() によって文字列の長さを取得する + +for (int i=0; i` の不等号演算子で簡単に実装できる。 + +```cpp:line-numbers +string s = "Hello"; +string t = "Bello"; +cout << (s < t) << endl; +``` + +``` +[output] +0 +``` + +string 型同士で比較するときに、1文字目を比較→同じなら2文字目を比較→… という処理を行って比較されている。 + +::: + +## 6.4.5. マルチバイト文字 + +::: tip +完全なおまけセクションです。講習の進行状況次第で飛ばします… +::: + +ところで、ASCIIコード表には日本語特有の文字(ひらがな・カタカナ・漢字)が存在しないことに気づくだろうか。 + +これは1バイトで扱える情報が 256 通りしかないから、日本語は1バイトだけでは扱うことができない。 +それゆえ、日本語(ひらがなカタカナ等)はマルチバイト文字で表現されている(複数バイトで1文字を表す方式)。 + +ここで、`char` 型は、正確には「1バイトの整数型」である。よって、`char` +型ではひらがな等の字を扱うことができない。実際に試してみると、5文字なのに長さは15と表示されてしまうし、1文字目を出力してみようと思ってもうまく出力されない。例えば「こ」の場合は、3文字出力してようやく「こ」が表示されるのである。 + +```cpp:line-numbers +string dame = "こんにちは"; +cout << dame.length() << endl; +cout << dame[0] << endl; +cout << dame[0] << dame[1] << dame[2] << endl; +``` + +``` +[output] +15 +� +こ +``` + +多バイト文字を扱える型も存在するが、非常に扱いが面倒なのでここでは扱わない。 +C++ 以外の言語でも大体は文字列を扱えるが、マルチバイト文字に対する扱いは言語によってまちまちだったりするので、よく調べること。 + +::: tip + +### 6.4.5.1. 文字化け + +ここで横道に逸れてしまうが、せっかくの機会なので「文字化け」についても触れておこう。 + +**歴史的な経緯** + +ASCII では日本語文字を扱えないのは明らかだったので、1文字で2バイトを使う文字コード体系が考案された。2バイトであれば +$2^{16}=65536$ 文字を扱えるので、これだけあれば十分だという事である。 +しかし、結果としては国内で文字コードが乱立し(Shift-JIS, EUC-JP +等…)、様々衝突するようになった上、国内にとどまらず各国で独自の文字コードが作成されてしまい、混乱を極めてしまったのである(ASCII +で表せないのは日本語だけではなく、アラビア文字・簡体字・ギリシャ文字など様々…)。 + +最終的には、世界の全文字を扱う文字コードとして Unicode (≒ UTF-8) +が策定され、今日ではこれが普及している。しかし完全に普及している訳でもなく、よって、今日見かける文字化けはもっぱら「UTF-8 か +UTF-8 以外か」の文字コードを使って発生しているパターンである。 + +**CJK 問題** + +また、日本語の漢字が中国語のフォントで表示されている、という謎の現象に遭遇したこともあるかもしれない。これは、Unicode +を制定した際に日本語・中国語(簡体字・繁体字)・韓国語の漢字について、似ている漢字を全て一緒くたにしてしまった事が原因である。 → [Your Code Displays Japanese Wrong](https://heistak.github.io/your-code-displays-japanese-wrong/) + +**横道** + +(横道の横道はもう獣道では…?) +日本国内は2バイト文字コードの覇権を争うために揉めていたが、その間ヨーロッパでは自国専用の1バイト文字が乱立していて同じように揉めていたらしい。 +::: + +::: tip +`int` 型は 4バイトの整数型である。扱える情報は $2^{32}$ で、整数は負と正の値を取るから非負整数の部分にに $2^{31}$ +個を割り当てる。$2^{31}=2147483648 +$ なのだが、実はこの値は既にテキストで一度出現している。さてどこだろうか? +::: diff --git a/docs/cpp/chapter-6/5.md b/docs/cpp/chapter-6/5.md index e8f02c1b..531f1e2c 100644 --- a/docs/cpp/chapter-6/5.md +++ b/docs/cpp/chapter-6/5.md @@ -1 +1,101 @@ -# 6.5 continueとbreak +# [WIP] 6.5 continueとbreak + +## 6.5.1. break + +for文・while 文はbreakを用いて途中で終了して抜け出すことができる。 + +```cpp:line-numbers +vector v = {1, 4, 0, 6, 10}; + +for (int i=0; i declare["v = {1,4,0,6,10}"] --> init[[int i=0]] + subgraph for + init --> cond[[i<5]] + cond --> |Yes| work[["cout << v[i]"]] --> isZero + isZero --> |Yes| break[[break]] + isZero --> |No| cont[[i++]] --> cond + end + break --> e + cond --> |No| e["End"] +``` + +## 6.5.2. continue + +continueを用いると、ループの現在のステップを飛ばして、次のステップに進むことができる。 + +```cpp:line-numbers +vector v = {1, 4, 0, 6, 10}; + +for (int i=0; i declare["v = {1,4,0,6,10}"] --> init[[int i=0]] + subgraph for + init --> cond[[i<5]] + cond --> isZero + isZero --> |Yes| continue[[continue]] --> cont + isZero --> |No| work[["cout << v[i]"]] --> cont[[i++]] --> cond + end + cond --> |No| e["End"] +``` + +## 6.5.3. 多重ループ + +```cpp:line-numbers +for (int i=0; i<2; i++) { + for (int j=0; j<3; j++) { + cout << i << " " << j << endl; + } +} +``` + +``` +[output] +0 0 +0 1 +0 2 +1 0 +1 1 +1 2 +``` + +for 文の内側に更に for 文を書いて、二重 for を書くことができる。 +また、配列の配列という形で二次元配列を作ることもできる。(オセロ盤みたいなイメージ) + +```cpp:line-numbers +vector> v = { + {1, 2, 3, 4}, + {3, 5, 1, 2}, + {10, 20, 10, 20}, +}; +``` + +::: info +[**\[ IV の練習問題へ\]**](https://md.trap.jp/IE4NUAc_RR-USMIXlevsgA#Section-IV) + +[**\[講習会ページに戻る\]**](https://wiki.trap.jp/Event/welcome/23/lecture/pg-basic) +::: + diff --git a/docs/cpp/chapter-6/index.md b/docs/cpp/chapter-6/index.md index dc493e5a..4b457d37 100644 --- a/docs/cpp/chapter-6/index.md +++ b/docs/cpp/chapter-6/index.md @@ -1 +1,4 @@ -# 6. 繰り返し処理 +# [WIP] 6. 繰り返し処理 + +第六章では、繰り返し処理について紹介します。 +ここまで身につけてしまえば、とりあえず頭の中に思い浮かべたものは理論上は作れるようになります。 diff --git a/docs/cpp/chapter-7/1.md b/docs/cpp/chapter-7/1.md index 64e8c36c..2ec1313f 100644 --- a/docs/cpp/chapter-7/1.md +++ b/docs/cpp/chapter-7/1.md @@ -1 +1,156 @@ -# 7.1 関数とは +# [WIP] 7.1 関数とは + +数学における「関数」とは、$f(x)$ 等のように表してある値(この例では $x$ )によって値 $f(x)$ が決まるものであった。 + +プログラミングにおいてはこれと少し似ているが、ある値を与えた時に、**決められた処理をして**、ある値を返すものを**関数** +と呼ぶ。ここで、関数は値を与える必要はないし、また値を返さなくても良い(決められた処理がなくても良い)という事に注意すること。 + +C++言語において、関数は以下のように記述する。 + +```cpp:line-numbers +#include +using namespace std; + +int hello() { + cout << "Hello, traP!" << endl; + return 0; +} + +int main() { + hello(); +} +``` + +```cpp:line-numbers +#include +using namespace std; + +[型] [関数名]() { + [文] + return [返り値]; +} + +int main() { + [関数名](); //実行する方 +} + +``` + +``` +[output] +Hello, traP! +``` + +フローチャートにすると以下のようになる。 + +```mermaid +flowchart LR + subgraph main["main()"] + Start --> calc["hello()"] + e["End"] + end + calc --> print + subgraph hello["Hello()"] + print["Hello, traP!"] --> return["return 0;"] + end + return --> e +``` + +---- + +関数は何回でも実行できる。 + +```cpp:line-numbers +#include +using namespace std; + +int hello() { + cout << "Hello, traP!" << endl; + return 0; +} + +int main() { + hello(); + hello(); + hello(); +} +``` + +``` +[output] +Hello, traP! +Hello, traP! +Hello, traP! +``` + +```mermaid +flowchart LR + subgraph main["main()"] + Start --> calc1["Hello()"] + calc2["Hello()"] + calc3["Hello()"] + e["End"] + end + calc1 --> print1 + subgraph hello1["Hello()"] + print1["Hello, traP!"] --> return1["return 0"] + end + return1 --> calc2 --> print2 + subgraph hello2["Hello()"] + print2["Hello, traP!"] --> return2["return 0"] + end + return2 --> calc3 --> print3 + subgraph hello3["Hello()"] + print3["Hello, traP!"] --> return3["return 0"] + end + return3 --> e +``` + +ここでは `Hello, traP!` と出力する `Hello` 関数を定義している。 +関数を実装するとき、関数の末尾に必ず `return 0;` と記述する(これについては「返り値」の節で扱う) 。 + +複数回呼び出すと、複数回実行されている事がわかる。複雑な処理を何回も実行するとき、よく関数を使う。 + +また、main も関数の1つである。C++言語においては、コンピューターはこの main 関数を実行すると考えると良い。 + +異なる関数の間では、変数は共有されない。 + +```cpp:line-numbers +#include +using namespace std; + +int printn() { + int n = 200; + n *= 2; + cout << n << endl; + return 0; +} + +int main() { + int n = 100; + printn(); + cout << n << endl; +} +``` + +``` +[output] +400 +100 +``` + +フローチャートでいえば、変数はこの黄色い囲みの中だけで共有されていると考えると良いだろう。 + +```mermaid +flowchart LR + subgraph main["main()"] + Start --> setmain["n = 100"] --> calc["printn()"] + printmain("output n") --> e["End"] + end + calc --> setprint + subgraph hello["printn()"] + + setprint["n = 200"] --> double["n *= 2"] --> printprint("output n") --> return["return 0"] + end + return --> printmain +``` diff --git a/docs/cpp/chapter-7/2.md b/docs/cpp/chapter-7/2.md index 769b7206..3925b727 100644 --- a/docs/cpp/chapter-7/2.md +++ b/docs/cpp/chapter-7/2.md @@ -1 +1,123 @@ -# 7.2 引数 +# [WIP] 7.2 引数 + +関数に値を渡すときは、以下のように実装する。 + +```cpp:line-numbers +#include +using namespace std; + +int triple(int x) { + cout << x*3 << endl; + return 0; +} + +int main() { + triple(4); + triple(3); +} +``` + +``` +[output] +12 +9 +``` + +この関数に渡す値のことを **引数** と呼ぶ。 +引数の型は `()` の内側で定義される(`(int x)` の部分)。 + +```mermaid +flowchart LR + subgraph main["main()"] + Start --> calc4["triple(4)"] + e["End"] + calc3["triple(3)"] + end + calc4 --> start4 + subgraph triple4["triple(4)"] + start4["start: x=4"] --> print4["cout << x*3"] --> return4["return 0;"] + end + return4-->calc3 + calc3-->start3 + subgraph triple3["triple(3)"] + start3["start: x=3"] --> print3["cout << x*3"] --> return3["return 0;"] + end + return3 --> e +``` + +例えば string 型の引数を設定したいなら以下のように実装する。 + +```cpp:line-numbers +#include +using namespace std; + +int hello(string s) { + cout << "Hello, " << s << endl; + return 0; +} + +int main() { + hello("traP"); + hello("Takeno_hito"); +} +``` + +``` +[output] +Hello, traP +Hello, Takeno_hito +``` + +引数は複数個設定することもできる。 + +```cpp:line-numbers +#include +using namespace std; + +int add(int x, int y) { + cout << x + y << endl; + return 0; +} + +int main() { + int a = 5; + int b = 10; + add(4, 7); + add(a, b); +} +``` + +``` +[output] +11 +15 +``` + +引数は、関数内でしか使うことができない(関数を呼び出した側の変数は変わらない)。 + +```cpp:line-numbers +#include +using namespace std; + +int triple(int x) { + x = x*3; + return 0; +} + +int main() { + int x = 4; + triple(x); + + cout << x << endl; +} + +``` + +``` +[output] +4 +``` + +これについての詳細は「参照渡し」の節で扱う。 + + diff --git a/docs/cpp/chapter-7/3.md b/docs/cpp/chapter-7/3.md index 91f26fd6..54cb86b0 100644 --- a/docs/cpp/chapter-7/3.md +++ b/docs/cpp/chapter-7/3.md @@ -1 +1,94 @@ -# 7.3 返り値 +# [WIP] 7.3 返り値 + +逆に、関数が値を返すときは以下のように実装する。 + +```cpp:line-numbers +#include +using namespace std; + + +int ten() { + return 10; +} + +int main() { + int x = ten(); + + cout << x << endl; +} +``` + +``` +[output] +10 +``` + +この時、この「10」を **返り値** と呼ぶ。(10 を return する → 10 を返す) + +```mermaid +flowchart LR + subgraph main["main()"] + Start --> calc["ten()"] + setx["x = ten()"] --> print["output x"] --> e["End"] + end + calc --> startten + subgraph ten["ten()"] + startten["Start"] --> return["return 10;"] + end + return --> |10| setx +``` + +返り値の型は、関数名の前で宣言している。引数の話とまとめると、関数の定義方法は次の通り。 + +```cpp +<返り値の型> <関数名> ([引数の名前、型]) {... +``` + +返り値の型も自分で好きなように宣言できる。ただし複数の返り値を返すことはできない。 + +main 関数の返り値 は int 型である。他の関数の返り値はすべて呼び出した側に渡されるように、main 関数の返り値は OS +に渡される。このOSに渡す値は「終了コード」と呼ばれ、正常時は 0 を返すことが定められている。 + +なので本来ならば main 関数にも関数末尾に `return 0` +を書かなければならないが(そういうテキストも多い)、それを省略しても良いという事になっている(省略すると末尾に `return 0` +が自動的に追加される)。 + +関数の返り値がないときは、他の型の代わりに **void** 型を用いる。この場合、`return` 文は省略できる。(書きたい場合は `return;` と書く)。 + +```cpp:line-numbers +#include +using namespace std; + + +void hello() { + cout << "Hello!" << endl; +} + +int main() { + hello(); +} +``` + +---- + +引数と返り値を組み合わせることで、数学のような関数が実装できる。 + +```cpp:line-numbers +#include +using namespace std; + +int square(int n) { + return n*n; +} + +int main() { + cout << square(10) << endl; +} +``` + +``` +[output] +100 +``` + +この `square` 関数は、引数 $n$ を受け取り、 $n^2$ を返す関数である。 diff --git a/docs/cpp/chapter-7/4.md b/docs/cpp/chapter-7/4.md index 083e4b0e..61eccf35 100644 --- a/docs/cpp/chapter-7/4.md +++ b/docs/cpp/chapter-7/4.md @@ -1 +1,134 @@ -# 7.4 [発展] 参照渡し +# [WIP] 7.4 [発展] 参照渡し + +## 7.4.1. [発展] アドレス + +一度関数から離れて「変数」について考えてみる。 +以前触れた通り、変数は 0 と 1 の組み合わせによって表されているが、プログラムが変数の値を参照するときには、「変数の住所」の情報を持っていなければならない(でなければ、どの変数がどれか分からなくなってしまう)。 +この変数の住所のことを **アドレス** と呼ぶ。変数には「値」と「アドレス」の二種類の情報が備わっている。具体的には、メモリの前から何バイト目に変数が格納されているかの情報が与えられる。 + +アドレスは変数の前に `&` をつけることで取得することができる。 + +```cpp:line-numbers +#include +using namespace std; + +int main() { + int x = 4; + cout << x << endl; + cout << &x << endl; +} +``` + +``` +[output] +4 +0x7fffe13bd944 +``` + +アドレスは人によって・実行するたびに変わることに注意。(変わらないこともある) +出力に `0x` がついているが、これは 16 進数であることを表している。 +10~15 に当たる1桁の値として、`abcdef` が用いられる(探してみると 0-9 と a~f しかない文字列を時々見つけられる。これらは16進数である事が多い)。 + +- 「`int` 型のアドレス」の型は `int *` である。 +- アドレスから元の変数に戻すには、アドレスの前に `*` をつける。 + +```cpp:line-numbers +#include +using namespace std; + +int main() { + int x = 4; + int *p = &x; + cout << p << endl; + *p += 1; + cout << x << endl; +} +``` + +``` +[output] +0x7ffc62a31e34 +5 +``` + +ここで `*p += 1` と書いているが、`p` に `x` のアドレスが入っているので、`x += 1` と書くのと同じことが起きる。 + +## 7.4.2. [発展] 値渡しと参照渡し + +```cpp:line-numbers +#include +using namespace std; + +int triple(int x) { + x = x*3; + return 0; +} + +int main() { + int x = 4; + triple(x); + + cout << x << endl; +} +``` + +「引数」の節で、関数を呼び出した側の変数は、関数内で操作しても変わらないという事を説明した。 + +これについてもう少し詳しく触れると、関数を呼び出すとき、与えられた引数は複製され( = 新しい変数が作られて)、関数内で使用される。 + +つまり変数の値のみを渡している。この事を **値渡し** と呼ぶ。 + +`main` 関数で作った `x` を `triple` の中で変更できるようにするには、`x` のアドレス を関数に渡せば良い。 + +```cpp:line-numbers +#include +using namespace std; + +int triple(int *x) { + *x = (*x)*3; + return 0; +} + +int main() { + int x = 4; + triple(&x); + + cout << x << endl; +} +``` + +``` +[output] +12 +``` + +これを **アドレス渡し** と呼ぶ。関数にわたすのは変数の値ではなく「変数のアドレス」なので、呼び出し側は `triple(&x)` と記述する。 + +しかし、`&x` や `*x` をいちいち書くのは面倒なので、これを簡略化した記法が存在する。下のコードを書けば、上のコードと書いたのと同じことになる。 + +```cpp:line-numbers +#include +using namespace std; + +int triple(int &x) { + x = x*3; + return 0; +} + +int main() { + int x = 4; + triple(x); + + cout << x << endl; +} +``` + +``` +[output] +12 +``` + +型の部分を `int` から `int &` に変えるだけで、変数のポインタを渡したことになり、`main` 関数の `x` を `triple` +関数から変更できるようになるのである。 + +この機能を **参照** と言い、この書き方を **参照渡し** と呼ぶ。 diff --git a/docs/cpp/chapter-7/index.md b/docs/cpp/chapter-7/index.md index a5731a78..b18a166c 100644 --- a/docs/cpp/chapter-7/index.md +++ b/docs/cpp/chapter-7/index.md @@ -1 +1,3 @@ -# 7. 関数 +# [WIP] 7. 関数 + +第七章では、関数について扱います。数学における関数とプログラミングにおける関数の違いの説明から始まり、関数について必要な知識を一通りさらいます。 diff --git a/docs/cpp/chapter-8/1.md b/docs/cpp/chapter-8/1.md index 02deb48c..f664dff0 100644 --- a/docs/cpp/chapter-8/1.md +++ b/docs/cpp/chapter-8/1.md @@ -1 +1,104 @@ -# 8.1 構造体とは +# [WIP] 8.1 構造体とは + +構造体は、分散したデータを一つにまとめる事ができる型である。 + +例として、traP メンバーの名簿を考えよう。 + +今この講習に居るメンバーの traP ID、学籍番号、学年 の情報がほしいとする。 + +これらをプログラムで扱うとき、素朴には以下のように個別に変数を宣言すればよい。 + +```cpp +string trap_id, student_id; +int grade; +``` + +しかし、複数人分の情報を扱いたかったり、関数に渡したかったりするときにやや不便である。 + +例として、2人のメンバーの情報が完全に一致するか(同一か)を判定するプログラムを以下に記す。 + +```cpp:line-numbers +#include +#include + +using namespace std; + +// 引数が長すぎる…… +bool equals( +string trap_id1, string student_id1, int grade1, +string trap_id2, string student_id2, int grade2 +) { + return trap_id1 == trap_id2 + && student_id1 == student_id2 + && grade1 && grade2; +} + +int main() { + // 宣言が長すぎる…… + string trap_id1, student_id1, trap_id2, student_id2; + int grade1, grade2; + + cin >> trap_id1 >> student_id1 >> grade1 + >> trap_id2 >> student_id2 >> grade2; + + if (equals( + trap_id1, student_id1, grade1, + trap_id2, student_id2, grade2 + )) { + cout << "same" << endl; + } else { + cout << "not same" << endl; + } +} +``` + +これはあまりに長い。 +配列を使えば宣言はそれぞれの変数でまとめることはできるが、できれば学生情報は一つの変数にまとめたい。 + +ここで、複数の変数をまとめてひとつの値として扱える**構造体 (struct)** を導入し、構造体の **型** を定義する。 + +::: tip +他の言語などではクラスやオブジェクトなどと呼ばれる。 +::: + +::: tip 用語 + +- 構造体に属する変数は**メンバ変数 (member variable)** と呼ばれている。 +- ある構造体の型を持った値は、その構造体の**インスタンス (instance)** と呼ばれる。 + +::: + +構造体を用いると、コードは以下のようになる。 + +<<<@/text/chapter-6/struct-raw.cpp{cpp:line-numbers} + +少し見やすくなったように感じないだろうか? + +<<<@/text/chapter-6/struct.cpp#define{cpp:line-numbers} + +この部分で構造体 `Member` を定義している。構造体 `Member` は以下の3つのメンバ変数を持つ。 + +- `string` 型の `trap_id` +- `string` 型の `student_id` +- `int` 型の `grade` + +宣言は以下のように対応する。 + +<<<@/text/chapter-6/struct.cpp#declare{cpp:line-numbers} + +これは省略されているが、先頭から順に `trap_id = "zer0-star"`, `student_id = "22BXXXXX"`, `grade = 2` とするという宣言である。 + +次に、構造体を使用する方を見てみよう。`equals` 関数の実装は以下のとおり。 + +<<<@/text/chapter-6/struct.cpp#function{cpp:line-numbers} + +関数の引数として、 `int` 型などと同様に `Member` 型を受け取ることができる。 +`Member` 型の変数1つを渡すのは、 `trap_id` `student_id` `grade` の3変数を渡したことと同じになる。 + +`Member` 型の各変数にアクセスするには、 `member.trap_id` のように、 `.` で変数名とメンバ変数を繋げれば良い。 + +このようにしたあとは、以下のように構造体を使って `equals` を使用することができる。 + +<<<@/text/chapter-6/struct.cpp#use{cpp:line-numbers} + +なんだか仰々しいものに見えるかもしれないが、本質的には複数の変数をまとめて、わかりやすくなるように取り扱っているだけである。 diff --git a/docs/cpp/chapter-8/2.md b/docs/cpp/chapter-8/2.md index 915dc758..54830b00 100644 --- a/docs/cpp/chapter-8/2.md +++ b/docs/cpp/chapter-8/2.md @@ -1 +1,84 @@ -# 8.2 メソッド +# [WIP] 8.2 メソッド + +実は、構造体の中に宣言できるのは、変数だけではない。関数もである。 +構造体の内側で関数を宣言すると、その関数は特別視され、呼び出し元のインスタンスのメンバ変数に直接アクセスすることができる。 +このように、構造体に属しており普通の関数とは扱いが異なる関数を、構造体の**メソッド (method)** と呼ばれる。 + +メソッド内では、直接自分自身のインスタンスにアクセスする事ができる。 + +```cpp:line-numbers +#include +#include + +using namespace std; + +struct Member { + string trap_id, student_id; + int grade; + string getStudentPrefix() { + // ドットなしで自分自身のstudent_idにアクセスできる + string s = student_id; + string result = {s[0], s[1], s[2]}; + return result; + } +}; + +int main() { + Member zer0star{"zer0-star", "22BXXXXX", 2}; + Member yukikurage{"yukikurage", "21BYYYYY", 3}; + + cout << zer0star.getStudentPrefix() << endl; + cout << yukikurage.getStudentPrefix() << endl; +} +``` + +```txt +[output] +22B +21B +``` + +メソッドを経由して、インスタンスを更新することもできる。 + +```cpp:line-numbers +#include +#include + +using namespace std; + +struct Member { + string trap_id, student_id; + int grade; + void updateGrade(int _grade) { + // 宣言がかぶるので _grade とする + grade = _grade; + } +}; + +int main() { + Member zer0star{"zer0-star", "22BXXXXX", 2}; + + zer0star.updateGrade(1); + cout << zer0star.grade << endl; +} +``` + +``` +[output] +1 +``` + +::: tip +メソッド `updateGrade` は、以下のように実装することもできる。が、ここでは深くは扱わない。 + +```cpp +void updateGrade(int grade) { + this->grade = grade; +} +``` + +::: + +::: tip +関数の名前の付け方、メンバー変数の名前の付け方には色々な作法があるが、ここでは扱わない(色々学ぶよりも、まず何か作ってみる方が重要!) +::: diff --git a/docs/cpp/chapter-8/index.md b/docs/cpp/chapter-8/index.md index 6cc0e417..f2e51519 100644 --- a/docs/cpp/chapter-8/index.md +++ b/docs/cpp/chapter-8/index.md @@ -1 +1,4 @@ -# 8. 構造体 +# [WIP] 8. 構造体 + +第八章では構造体 (struct) について扱います。第7章までとは違い、第8章の知識はなくともプログラミングをする事はできます。 +これは、人間がより便利にプログラミングをする為に導入された概念です。 diff --git a/docs/cpp/chapter-8/struct-raw.cpp b/docs/cpp/chapter-8/struct-raw.cpp new file mode 100644 index 00000000..3327ea2e --- /dev/null +++ b/docs/cpp/chapter-8/struct-raw.cpp @@ -0,0 +1,25 @@ +#include +#include + +using namespace std; + +struct Member { + string trap_id, student_id; + int grade; +}; +bool equals(Member member1, Member member2) { + return member1.trap_id == member2.trap_id && + member1.student_id == member2.student_id && + member1.grade == member2.grade; +} + +int main() { + Member zer0star{"zer0-star", "22BXXXXX", 2}; + Member hoshimiya{"hoshimiya", "99B99999", 99999}; + + if (equals(zer0star, hoshimiya)) { + cout << "same" << endl; + } else { + cout << "not same" << endl; + } +} diff --git a/docs/cpp/chapter-8/struct.cpp b/docs/cpp/chapter-8/struct.cpp new file mode 100644 index 00000000..5a33c266 --- /dev/null +++ b/docs/cpp/chapter-8/struct.cpp @@ -0,0 +1,35 @@ + +#include +#include + +using namespace std; + +// #region define +struct Member { + string trap_id, student_id; + int grade; +}; +// #endregion define + +// #region function +bool equals(Member member1, Member member2) { + return member1.trap_id == member2.trap_id && + member1.student_id == member2.student_id && + member1.grade == member2.grade; +} +// #endregion function + +int main() { + // #region declare + Member zer0star{"zer0-star", "22BXXXXX", 2}; + Member hoshimiya{"hoshimiya", "99B99999", 99999}; + // #endregion declare + + // #region use + if (equals(zer0star, hoshimiya)) { + cout << "same" << endl; + } else { + cout << "not same" << endl; + } + // #endregion use +} diff --git a/docs/cpp/index.md b/docs/cpp/index.md index 11501ab1..e7ac775b 100644 --- a/docs/cpp/index.md +++ b/docs/cpp/index.md @@ -44,6 +44,20 @@ hero: - [5.1 bool型](/cpp/chapter-5/1) - [5.2 double型](/cpp/chapter-5/2) - [5.3 string型①](/cpp/chapter-5/3) +- [[WIP] 6. 繰り返し処理](/cpp/chapter-6/) + - [[WIP] 6.1 for文](/cpp/chapter-6/1) + - [[WIP] 6.2 while文](/cpp/chapter-6/2) + - [[WIP] 6.3 配列](/cpp/chapter-6/3) + - [[WIP] 6.4 string型②とchar型](/cpp/chapter-6/4) + - [[WIP] 6.5 continueとbreak](/cpp/chapter-6/5) +- [[WIP] 7. 関数](/cpp/chapter-7/) + - [[WIP] 7.1 関数とは](/cpp/chapter-7/1) + - [[WIP] 7.2 引数](/cpp/chapter-7/2) + - [[WIP] 7.3 返り値](/cpp/chapter-7/3) + - [[WIP] 7.4 [発展] 参照渡し](/cpp/chapter-7/4) +- [[WIP] 8. 構造体](/cpp/chapter-8/) + - [[WIP] 8.1 構造体とは](/cpp/chapter-8/1) + - [[WIP] 8.2 メソッド](/cpp/chapter-8/2) ::: details 2025年度版テキスト diff --git a/docs/index.md b/docs/index.md index 11501ab1..e7ac775b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -44,6 +44,20 @@ hero: - [5.1 bool型](/cpp/chapter-5/1) - [5.2 double型](/cpp/chapter-5/2) - [5.3 string型①](/cpp/chapter-5/3) +- [[WIP] 6. 繰り返し処理](/cpp/chapter-6/) + - [[WIP] 6.1 for文](/cpp/chapter-6/1) + - [[WIP] 6.2 while文](/cpp/chapter-6/2) + - [[WIP] 6.3 配列](/cpp/chapter-6/3) + - [[WIP] 6.4 string型②とchar型](/cpp/chapter-6/4) + - [[WIP] 6.5 continueとbreak](/cpp/chapter-6/5) +- [[WIP] 7. 関数](/cpp/chapter-7/) + - [[WIP] 7.1 関数とは](/cpp/chapter-7/1) + - [[WIP] 7.2 引数](/cpp/chapter-7/2) + - [[WIP] 7.3 返り値](/cpp/chapter-7/3) + - [[WIP] 7.4 [発展] 参照渡し](/cpp/chapter-7/4) +- [[WIP] 8. 構造体](/cpp/chapter-8/) + - [[WIP] 8.1 構造体とは](/cpp/chapter-8/1) + - [[WIP] 8.2 メソッド](/cpp/chapter-8/2) ::: details 2025年度版テキスト