Undergraduate
情報
【プログラミング入門】#8 ポインタの活用
11
514
0

ポインタなんて出来なくてもいいと思うかもしれませんが、どうしても必要になる時があります。
あと、よくある勘違いですがC言語には参照渡しはありません。代わりにポインタ渡しがあります。参照渡しはc++言語からの機能です。

ノートテキスト
ページ1:
プログラミング入門 #8 ポインタの活用 ©たくのろじぃ
ページ2:
前回やったこと • N進数 • コンピュータのメモリ . ポインタ 配列とポインタ 具体的な内容は前回のスライドを参照してください
ページ3:
今回やること 1. 文字列とポインタ 2. 関数とポインタ 3.変数の入れ替え(swap) 4. 配列を引数に渡す ポインタを使うと何が嬉しいのかをまとめます。一見、 「なんだ、 変数でいいじゃん」と 思うかもしれませんが、実はポインタを使わないと上手くいかないことが... ? キーワード: null文字, 文字列リテラル, 関数ポインタ, ポインタ渡し, スワップ (swap)
ページ4:
1. 文字列とポインタ 文字列は配列に格納するものであった→ char str[] = "Hello"; 配列の各要素に対して文字を割り当てることで、その変数を文字列として扱えた また、文字列の最後には文字の終端を示す null文字(EOF, ¥0) が格納される ポインタ変数を使用しても、 配列と同じように文字列として扱える 1 #include<stdio.h> 2 3 int main(){ 4 int i; 5 // 要素数を指定する配列による初期化 6 char strA[5] = {'H', 'e', '1', '1', 'o'}; 7 // 要素数を指定しない配列による初期化 8 char strB[] = "new"; 9 // ポインタ変数による初期化 10 char *strC = "World"; 11 12 13 14 15 } 16 17 18 19 20 } for(i = 0; i < 5; i++){ // strA は確保している要素数の分だけ回す必要がある printf("%c", stra[i]); // 要素数を指定しなければそのまま出力可能 // %s は文字列を出力する指定子 (文字は %c) printf("%s %s\n", strB, strC); return 0; PS F:\C> gcc Test.c PS F:\C> .\a.exe Hello new World PS F:\C> [
ページ5:
1.1 文字列を指すポインタ 文字列の指すポインタがどの領域を指しているかを調べてみる 1 12345678910111213 #include<stdio.h> int main(){ int i; char str[] = "World"; printf("str 変数を指すポインタ %p\n", &str); for(i = 0; i < sizeof(str); i++){ printf("%d 文字目を指すポインタ %p\n", i, &str[i]); } return 0; } PS F:\C> \a.exe str変数を指すポインタ 0061FF16 0 文字目を指すポインタ 0061FF16 1 文字目を指すポインタ 0061FF17 2 文字目を指すポインタ 0061FF18 3 文字目を指すポインタ 8061FF19 4 文字目を指すポインタ 0061FF1A 5 文字目を指すポインタ 0061FF1B PS F:\C> 文字列の指すポインタは文字の先頭のアドレスである 先頭のアドレスさえ分かればポインタ変数経由で読み書き可能
ページ6:
1.2 文字列リテラル "" で囲まれた文字列を文字列リテラルといい、定数扱いとするルールがある → 定数なのでポインタ変数から文字列の値(文字)の内容を変更することは禁止 char str[] = "hoge"; str[3] = 'o'; として最後の文字を書きかえるのはOK char *str = "huga"; str[3] = 'o'; として最後の文字を書きかえるのは NG(定義されていない領域の書き換えは危険) *str W 0 r d ¥0 文字列リテラル領域のコピー ポインタ変数の場合は文字列リテラル領域に 直接アクセスできるため違反を起こす str[] Wo r d ¥0 (最近のコンパイラは優秀なのでシステムは保護される) 変数(配列)の場合、 文字列リテラルの領域が変数用領域にコピーされる → 実際に書き換わるのはコピーであるため適切な処理がなされる
ページ7:
1.3 文字列の上書き 変数名から上書きするには要素ごとのアクセスが必要であり、一気に書き込めない → ポインタ変数ではアドレスをもとにアクセスできるので書き込みが容易 変数名では要素ごとの書き込みが必要 ポインタ変数では書き込める 1 #include<stdio.h> 2 3 int main(){ 4 char str[] = "Hello"; 5 6 str = "World"; 7 8 printf("%s\n", str); 9 return 0; 10 PROBLEMS 1 OUTPUT DEBUG CONSOLE TERMINAL 1 2 3 4 5 6 7 8 9 #include<stdio.h> int main(){ char *str = "Hello"; str = "World"; printf("%s\n", str); return 0; 10 } OUTPUT DEBUG CONSOLE TERI PS F:\C> gcc Test.c Test.c: In function 'main': Test.c:6:9: error: assignment to expression with array type str = "World"; PS F:\C> PROBLEMS PS F:\C> .\a.exe World PS F:\C>
ページ8:
1.4 上書きの本当の意味 文字列リテラルを変更することは禁じられているが、なぜポインタ変数で上書きできてしまうのか 1 #include<stdio.h> 2 3 int main(){ 6 7 8 9 10 11 12 } char *str = "hoge"; printf("書き換え前のポインタ %p\n", str); //確保した str ポインタ変数に代入してみる str= "huga"; printf("書き換え後のポインタ %p\n", str); return 0; PS F:\C> .\a.exe 書き換え前のポインタ 00405844 書き換え後のポインタ 0040506F PS F:\C> h O g e ¥0 書き換え前の文字列リテラルを指す ポインタ変数 *str h U g a ¥0 書き換え後の文字列リテラルを指す ポインタ変数 *str 実は上書きをしているのではなく、文字列リテラルを指すポインタが変わっているだけ → 参照するアドレスを変えているだけというトリック
ページ9:
1.5 ポインタ変数を指すポインタ ポインタ変数自体を指すポインタのアドレス領域は変わっているだろうか? 1 2 3 #include<stdio.h> int main(){ char *str = "hoge"; // & を付けるとそれ自体のアドレスを指す printf("書き換え前のポインタ %p\n", &str); // 確保した str ポインタ変数に代入してみる str= "huga"; 7 8 9 printf("書き換え後のポインタ %p\n", &str); 10 11 return 0; PS F:\C> gcc Test.c PS F:\C> .\a.exe 書き換え前のポインタ 0061FF1C 書き換え後のポインタ 0061FF1C PS F:\C> [ *str 12 h O g e ¥0 hu g a ¥0 &str ポインタ変数自体のアドレス領域は変わらない str から見れば文字列リテラルのアドレス領域はポインタのポインタ
ページ10:
1.6 複数の文字列の保持 文字列をポインタ変数として配列による宣言が可能→ 配列によって複数の文字列を扱える #include<stdio.h> int main(){ 1 2 3 4 5 6 7 8 9 10 11 } int i; char *str[] = {"white", "blue", "red", "black"}; //ポインタ変数自体を配列に格納しているので * つきで参照 for(i = 0; i < sizeof(str); i++){ } printf("%s\n", str[i]); return 0; PS F:\C> gcc Test.c PS F:\C> .\a.exe white blue red black PS F:\C> 配列で確保したアドレス領域は、その文字数 + null文字ごとに確保されている PS F:\C> .\a.exe 7 for(i = 0; i < sizeof(*str); i++){ printf("%p\n", str[i]); 8911 10 } return 0; 00405044 0040504A 0040504F 00405053 PS F:\C>
ページ11:
2. 関数とポインタ 関数:引数と返り値があり、様々な処理をまとめることができる (第3,4回を参照のこと) 関数ポインタ : 関数が確保しているアドレス領域を参照する 12 11 int main(){ 13 printf( "main関数を指すポインタ p", main); return 0; 14 PROBLEMS OUTPUT PS F:\C> gcc Test.c PS F:\C> .\a.exe DEBUG CONSOLE TERMINAL main関数を指すポインタ 8040141A PS F:\C> 変数のような具体的な値を持たないので、 関数そのものはポインタ 変数と関数は別の領域に保持されている → 関数もメモリのある領域に存在する PS F:\C> .\a.exe functionA関数を指すポインタ 00401410 functionB 関数を指すポインタ 0040141A functionc関数を指すポインタ 00401424 main関数を指すポインタ 0040142E PS F:\C> 他にも関数を作ってアドレスを見てみると・・・ 関数を指すポインタは 10 [Byte] ごとに 確保されている
ページ12:
2.1 ポインタ変数から関数を呼び出す 呼び出される関数はそのまま作成して良いが、 呼び出すときは関数ポインタを使う 関数名にポインタ演算子を付けて宣言する→返り値の型 (*関数名) (仮引数の型); あとは呼び出される関数のポインタを関数ポインタにコピーして、ここから呼び出す int main(){ 1 #include<stdio.h> 2 3 int functionA(int a, int b); 4 5 int functionA (int a, int b){ 6 return a + b; 7 } 8 9 10 11 12 13 14 15 16 17 18 // 関数ポインタ int (*funcp)(int, int); //functionAを指すポインタ funcp = functionA; printf("functionAを指すポインタ %p\n", funcp); printf("functionAから得た値 %d\n", funcp(2,3)); return 0; functionA を直接指定しなくても 関数ポインタ funcp から呼び出しができる 間接的な呼び出し PS F:\C> gcc Test.c PS F:\C> .\a.exe functionAを指すポインタ 80401410 functionAから得た値 5 PS F:\C> 19 }
ページ13:
1 2 2.2 関数の配列 関数ポインタを使えば関数そのものを配列に保持することができる メリットとしては複数の関数を配列1つで管理できること #include<stdio.h> int Add(int a, int b); 24 int main(){ //関数ポインタの配列 //配列の要素には各関数を保持 int (*funcp[])(int, int) = { Add, //加算 Sub, // 減算 Mult, // 乗算 Div //除算 for(i = 0; i < sizeof(*funcp); i++){ //配列の中に関数が入っている -> 呼び出される printf("The Answer is %d\n", funcp[i](10, 5)); PS F:\C> gcc Test.c PS F:\C> .\a.exe The Answer is 15 The Answer is 5 The Answer is 50 The Answer is 2 PS F:\C> int Sub(int a, int b); 25 int i; 5 int Mult(int a, int b); 26 6 int Div (int a, int b); 27 7 28 8 int Add(int a, int b){ 29 9 return a + b; 30 10 } 31 11 32 12 int Sub(int a, int b){ 33 }; 13 return a - b; 34 14 } 35 15 36 16 17 int Mult(int a, int b){ return a ✶ b; 37 38 } 18 39 19 40 return 0; 20 int Div(int a, int b){ 41 C 21 return a / b; 22 }
ページ14:
2.3 値渡しがうまくいかない例 値をそのまま引数として関数で2倍にしたかったが、値は変化しない 1 2 3 4 5 #include<stdio.h> int function(int value); int function(int value){ //ポインタ演算子でないので注意 return value * 2; int main(){ 7 8 } 9 10 11 int value = 5; 12 // value を関数で2倍にする 13 function (value); 14 15 16 // 関数によって value は2倍されているはずだが... printf("The value is %d", value); return 0; 17 PS F:\C> .\a.exe The value is 5 PS F:\C> [ それぞれの関数の value を指すポインタが異なるため → 変数名は同じ value でも、参照するアドレスが異なる
ページ15:
2.4 各関数の指す変数へのポインタ なぜうまくいかないかはポインタを見ればわかる value を引数に入れると仮引数に値がコピーされる→しかしコピー先は異なるポインタ コピーデータを受け取るための変数を指すポインタが定義される 1 #include<stdio.h> 2 3 int function(int value); 4 5 int function(int value) { printf("function の value へのポインタ %p\n", &value); return 0; } 6789101112131415 int main(){ int value = 5; function (value); printf( "main の value へのポインタ %p\n", &value}}; return 0; PS F:\C> \a.exe function の value へのポインタ 0061FF00 main の value へのポインタ e061FF1C PS F:\C> [
ページ16:
2.5 値渡しのポインタ 値渡しは値そのものをコピーしている→確保されるアドレスは関数によって異なる main 関数内 value (関数内定義) 5, 値そのものは保護されるが、アドレスは破棄 function 関数内 value (仮引数定義) '5 value * 2 10 printf 関数は main 関数内の value を表示しているので値が変わっていないように見える → function 関数内では適切な処理がなされているが、意図した処理ではない
ページ17:
2.6 ポインタ渡し ポインタ渡し※:関数の引数にポインタ変数をわたすこと→ function(int *pt){… } 値ではなくポインタを渡すので、参照先のアドレス領域は変わらない 1 #include<stdio.h> 2 3 int function(int *value); 4 5 //引数をポインタ変数にする 6 7 8 9 } 10 11 12 13 14 int main(){ int value = 5; // value のアドレスを渡す function(&value); int function(int *value){ printf("function の value へのポインタ %p\n", value ); return 0; PS F:\C> .\a.exe function の value へのポインタ 0061FF1C main の value へのポインタ 8061FF1C PS F:\C> [ どちらの関数も同じアドレスを指している 15 16 printf( "main の value へのポインタ %p\n", &value); return 0; 17 ※ 参考にした本には参照渡しと書いてあったが、C言語には参照渡しの機能はない。正確には「参照の値渡し」であり、 C言語にはアドレスを直接渡せる機能はない。 つまり、参照先のアドレスを値として保持しているポインタ変数に保持 アドレスを値として管理しているので、値渡しをしているように思えるが実体はポインタ (アドレス) なので正しくはポインタ渡し
ページ18:
2.7 ポインタ渡しのポインタ ポインタ渡しはアドレスをコピーしている→確保されるアドレスは同じ領域 value (関数内定義) main 関数 5 アドレスを渡すので、 値も参照可能 function 関数 value (アドレス代入) 5 value * 2 10 printf 関数で value を参照→その値を出力 ポインタ渡しを使えば1つの変数(アドレス)だけで動作するグローバル変数に近い
ページ19:
2.8 ポインタ渡しの実装 同一のアドレスを指すことを利用して実装してみる #include<stdio.h> //ポインタ変数の参照先は値であり、 それを2倍している *value = value * 2; 1 2 3 void function(int *value); 4 5 //ポインタ変数に上書きするので返り値なし 6 void function(int *value){ 7 8 9 } 10 11 int main(){ 12 int value = 5; 13 // value のアドレスを渡す 14 function(&value); 15 printf("The value is %d\n", value); 16 return 0; 17 PS F:\C> gcc Test.c PS F:\C> .\a.exe The value is 10 PS F:\C> | ポインタ変数 value の初期値は5 このアドレスを参照して2倍しているので 結果として 10 となる *value でなく value とすると、 そのアドレス自体の値を2倍しようとしてエラーになる → アドレスの値は16進数なので16進数×10進数は型が合わない
ページ20:
3. 変数の入れ替え (swap) Q. 変数を入れ替えて何の意味があるのか A. データの昇順・降順並び替え (sort) を行うのに必要 → データ整理のためのアルゴリズムの基礎になる 考え方としては元のデータを一時保存しておき、入れ替え先と交換する 同じ関数内での入れ替え → 変数の上書きで実装 異なる関数内での入れ替え→ポインタ変数の上書きで実装 変数 a, b を入れ替えて b, a とする流れ b b a b a a b a
ページ21:
OCR失敗: NoMethodError undefined method `first' for nil:NilClass
ページ22:
OCR失敗: NoMethodError undefined method `first' for nil:NilClass
ページ23:
OCR失敗: NoMethodError undefined method `first' for nil:NilClass
ページ24:
OCR失敗: NoMethodError undefined method `first' for nil:NilClass
ページ25:
OCR失敗: NoMethodError undefined method `first' for nil:NilClass
ページ26:
OCR失敗: NoMethodError undefined method `first' for nil:NilClass
ページ27:
OCR失敗: NoMethodError undefined method `first' for nil:NilClass
ページ28:
OCR失敗: NoMethodError undefined method `first' for nil:NilClass
สมุดโน้ตแนะนำ
ประวัติการเข้าดู
คำถามที่เกี่ยวข้องกับโน้ตสรุปนี้
Undergraduate
情報
プログラミングの問題です。以下の写真のように使うことの出来る変数が1つしかなく、かつ代入と複合的な代入演算子しか用いることができない場合、どうしたら良いですか😭わかる方どうか教えてくださいm(_ _)m
Undergraduate
情報
ブロックチェーンとクラウドのデータベースとでは何が違うのでしょうか? わかる方、説明お願いします🙇
Undergraduate
情報
大域変数、局所変数が理解できません。 この問題でそれらを使うと解説にあったのですが、読んでも理解できず、、 これらは何が違うのでしょうか? 以下は、どちらもaという箱に値をいれてるわけではないのでしょうか a←"B" 文字型:a←"A" 何が違うのか理解できないため教えて欲しいです。 ※答えはオでした
Undergraduate
情報
この問題の答えが18になるはずなのですが、何故18になるのか分からないので教えて欲しいです。 途中でretの値が13になる所までは理解できるのですが、、 iを1からnまで1ずつ増やすとあるのでnを4まで増やした後にret+iを出力で、13+4して答えは17ではないのでしょうか。 今回nは4なのにnを5まで増やすのは何故ですか?
Undergraduate
情報
Oracle certified Java Programmer Gold SE11 IT系ベンダー資格のOracle certified Java Programmer Gold SE11の資格勉強をしているのですが、もし、Javaに詳しい人がいるのでしたら、 第6章のJDBCとデータベース連携で問10,11,12を分かりやすく解説して頂けると助かります...! 因みにそれぞれ解答は問10はD, 問11はF, 問12は「true」となります。 宜しくお願いします🙇♂️
Undergraduate
情報
自分で計算問題を作ろうという課題なんですが、どこが違うのか分かりません💦 誰か教えてください🙇♀️
Undergraduate
情報
情報理論に関する問題です。 天気として、晴れ、雨、曇り、雪の4種類について、天気予報でそれぞれの天気になる確率がわかっている。 昨日の予報ではそれぞれの確率が、晴れ0.6、雨0.1、曇り0.2、雪0.1 今日の予報ではそれぞれの確率が、晴れ0.55、雨0.1、曇り0.25、雪0.1 であった。 情報量の単位をビットとして、今日の天気予報にはどの程度の情報があったと言えるか。 という問題です。 昨日の予報と、今日の予報、それぞれのエントロピー(平均情報量)を求めて差分だけ情報があったという回答をしようと考えたのですが、合っていますでしょうか。 よろしくお願いします。
Undergraduate
情報
線形探索と二分探索の問題です。 解説も踏まえて、教えてくれると嬉しいです。
Undergraduate
情報
2進数に関するご質問です なぜ「111」が「マイナス1」に、「110」が「マイナス2」になるのかがわかりません。 負の数を表す2進数を10進数に戻す方法がわかりません よろしくお願いします🙇🏻♀️
Undergraduate
情報
情報系、オートマトンについてわかる人がいたら教えてください。 わからないのはこの問題です。 なぜこの答えになるかかいせつが欲しいです
News
ความคิดเห็น
ยังไม่มีความคิดเห็น