プログラミング論 ポインタ http://www.ns.kogakuin.ac.jp/~ct13140/Prog/

Slides:



Advertisements
Similar presentations
メモリとポインタ. プログラムの前提 コンピュータは、0と1で計算をし、 0と1でデータを保存している。 メモリを学ぶのに必要な知識である。
Advertisements

第6章 ポインタ ポインタが分からずにC言語を投げ出す人が数多くいます。 その半面、使いこなせば強力な武器となります。 しっかりと学習していきましょう C 言語 最難関文法 C 言語 最難関文法 1 第 6 章 ポインタ.
C 言語講座 第 7 回 ポインター. メモリとアドレス(ポインターの前 に) コンピュータのメモリには 1 バイトずつ 0 番地、 1 番地、 2 番地・・・というように 住所が割り当てられている この住所をアドレスという。 メモリはデータをしまうもので それを引き出すためには メモリに番号(アドレス)を振っておけばよいな.
プログラミング演習II 2004年11月 30日(第6回) 理学部数学科・木村巌.
情報基礎演習B 後半第5回 担当 岩村 TA 谷本君.
データ構造とアルゴリズム 第10回 mallocとfree
プログラミング入門2 第10回 構造体 情報工学科 篠埜 功.
プログラミング論 I 行列の演算
基礎プログラミングおよび演習 第9回
プログラミング演習Ⅱ 第12回 文字列とポインタ(1)
プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ, 情報処理言語Ⅰ(実習を含む。)
C言語講座 第4回 ポインタ.
第8回 プログラミングⅡ 第8回
第6章 2重ループ&配列 2重ループと配列をやります.
構造体.
プログラミング論 II 電卓,逆ポーランド記法電卓
第4回放送授業.
プログラミング演習Ⅰ 課題2 10進数と2進数 2回目.
岩村雅一 知能情報工学演習I 第8回(後半第2回) 岩村雅一
プログラミング論 関数ポインタ と 応用(qsort)
プログラミング2 関数
プログラミング論 ファイル入出力
第10回関数 Ⅱ (ローカル変数とスコープ).
プログラミング論 II 2008年10月30日 文字列
Cプログラミング演習 第7回 メモリ内でのデータの配置.
プログラミング 4 記憶の割り付け.
アルゴリズムとデータ構造 補足資料11-1 「mallocとfree」
第10章 これはかなり大変な事項!! ~ポインタ~
プログラミング入門2 第8回 ポインタ 情報工学科 篠埜 功.
アルゴリズムとデータ構造 補足資料5-2 「サンプルプログラムsetop.c」
プログラミング入門2 第11回 情報工学科 篠埜 功.
第13章 文字の取り扱い方 13.1 文字と文字型関数 13.2 文字列 13.3 文字型配列への文字列の代入
第7回 プログラミングⅡ 第7回
復習 前回の関数のまとめ(1) 関数はmain()関数または他の関数から呼び出されて実行される.
プログラミング言語論 第五回 理工学部 情報システム工学科 新田直也.
アルゴリズムとデータ構造 補足資料5-1 「メモリとポインタ」
地域情報学 C言語プログラミング 第5回 ポインタ、関数、ファイル入出力 2017年11月17日
プログラミング論 ファイル入出力
第11回 プログラミングⅡ 第11回
P n ポインタの基礎 5 q m 5 7 int* p; int 型の変数を指すポインタ int* q; int 型の変数を指すポインタ int n=5, m=7; int 型の変数 int array[3]; int* pArray[3]; p = &n; ポインタにアドレスを代入しているのでOK.
09: ポインタ・文字列 C プログラミング入門 総機1 (月1) Linux にログインし、以下の講義ページ を開いておくこと
メモリとメモリアドレス, ポインタ変数,関数へのポインタ渡し
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
プログラミング言語論 第六回 理工学部 情報システム工学科 新田直也.
C言語 はじめに 2016年 吉田研究室.
プログラミング入門2 第9回 ポインタ 情報工学科 篠埜 功.
アルゴリズムとプログラミング (Algorithms and Programming)
プログラミング 3 2 次元配列.
地域情報学 C言語プログラミング 第2回 変数・配列、型変換、入力 2017年10月20日
文字列へのポインタの配列 static char *lines[MAXLINES]; lines[0] NULL
ポインタとポインタを用いた関数定義.
第13章 文字の取り扱い方 13.1 文字と文字型変数 13.2 文字列 13.3 文字型配列への文字列の代入
第13回 ポインタ 1 1.
09: ポインタ・文字列 C プログラミング入門 基幹7 (水5) Linux にログインし、以下の講義ページを開いておく こと
アルゴリズムとデータ構造1 2009年6月15日
ネットワーク・プログラミング Cプログラミングの基礎.
第5回 プログラミングⅡ 第5回
オブジェクト指向言語論 第五回 知能情報学部 新田直也.
プログラミング論 構造体
プログラミング論 文字列
アルゴリズムとデータ構造 2010年6月17日
2005年度 データ構造とアルゴリズム 第2回 「C言語の復習:配列」
情報処理Ⅱ 2005年11月25日(金).
プログラミング演習II 2004年11月 2日(第3回) 理学部数学科・木村巌.
プログラミング入門2 第5回 配列 変数宣言、初期化について
情報処理Ⅱ 小テスト 2005年2月1日(火).
モバイルプログラミング第3回 Cプログラミングの基礎( 2 )
プログラミング演習I 補講用課題
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
プログラミング 3 ポインタ(1).
Presentation transcript:

プログラミング論 ポインタ http://www.ns.kogakuin.ac.jp/~ct13140/Prog/

見つかった誤り if( x = 3 ){ ... } "x=3"は比較ではなく、代入. x += 2 は、"xが2増える"が,

見つかった誤り x = x+1; はOK. x+1 = x; はNG. 代入は 右の値を左に入れる. x = 3 はOKだが, 3 = x はNG.

見つかった誤り #define MAX 100 は,MAXを100に置き換えてからコンパイルする MAXという変数があるわけではない! i=MAX; #define MAX 100 MAX=30; MAXという変数が あるわけではない i=100; これを コンパイル 100=30; これを コンパイル????

概要 前期最重要事項 : 関数 後期最重要事項 : pointer ポインタ ちなみに… 難易度が高いが,極めて重要 & と * の2種類しかない. ちなみに… 前期最重要事項 : 関数 後期最重要事項 : pointer 5

ポインタ なにかを「指す」ものがポインタである. C言語ではアドレスを理解すれば良い. 一般に理解が難しいとされる

メモリとアドレス 計算機はメモリにデータを保存している. 各バイトには,固有の通し番号(住所,アドレス)がついている. 通常1バイト(8bit)単位で管理される. 1バイトの「記憶領域=入れ物」が大量にある. 各バイトには,固有の通し番号(住所,アドレス)がついている. それぞれのメモリに 1バイトのデータを 記憶できる. 各1バイトに固有の 住所がついている. 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 7 100 56 23 53 45 95 2 65 1バイトの記憶領域=入れ物が10個ある例.

変数の記憶領域 変数を作ると,当然メモリ上にそのための記憶領域が自動的に確保される. char abc; /* char型変数を1個用意 */ 変数abcの内容は, 4番地に記録する ことに決めたとする. (計算機が自動で行う) abc 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 7 100 56 23 53 45 95 2 65

変数の記憶領域 変数を作ると,当然メモリ上にそのための記憶領域が自動的に確保される. char abc; /* char型変数を1個用意 */ char *p; abc = 7; p = &abc; &abc は,「変数abcは メモリの何番地か」を返す. pには「4番地」が入る. 7では無いことに注意! abc 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 7 100 56 23 7 45 95 2 65

&演算子 と *演算子 (重要!) &演算子は「変数のアドレス」を取得する演算子 *演算子は「メモリの中身」を取得する演算子 変数は,メモリのどこかに対応している &演算子は「変数のアドレス」を取得する演算子 使い方:変数名の前に&記号をつける 意味:その変数の位置、アドレス 例 p=&abc; /*変数pにアドレスを代入*/ *演算子は「メモリの中身」を取得する演算子 使い方:アドレス(アドレス変数)の前に*記号を付ける 意味:「そのアドレスのメモリ」の中身 例 x=*p; /*pにはアドレスが格納されているとする*/

&演算子 &演算子は変数のアドレスを調べる演算子. 変数の前に&を付けると,その変数アドレスが得られる. void main(){ char abc; char *p; p = &abc; printf("abcのアドレスは%pです.\n", p); } 実行結果 abcのアドレスは0013FF7Cです.

&演算子 void main(){ char abc, def; char *p; p = &abc; printf("abcのアドレスは%pです.\n", p); p = &def; printf("defのアドレスは%pです.\n", p); } 実行結果 abcのアドレスは0013FF7Cです. defのアドレスは0013FF78です.

&演算子 void main(){ char abc, def, ghi; printf("アドレスは%p %p %pです.\n", } 実行結果 アドレスは0013FF7C 0013FF78 0013FF74です.

&演算子 void main(){ char abc[5]; printf("アドレスは%p %p\n%p %p %pです.\n", &(abc[0]), &(abc[1]), &(abc[2]), &(abc[3]), &(abc[4])); } 実行結果 アドレスは0013FF78 0013FF79 0013FF7A 0013FF7B 0013FF7Cです.

&演算子 void main(){ int abc[5]; printf("アドレスは%p %p\n%p %p %pです.\n", &(abc[0]), &(abc[1]), &(abc[2]), &(abc[3]), &(abc[4])); } int型は4バイト 実行結果 アドレスは0013FF6C 0013FF70 0013FF74 0013FF78 0013FF7Cです.

アドレス型変数(ポインタ変数) アドレスを格納する変数 char ch; /* char型を格納する */ 整数を格納できる変数があるように, アドレスを格納する変数がある. char ch; /* char型を格納する */ char *p; /* char型変数のアドレスを格納 */ p = &ch; /* ch は"4番地"なので,pには"4番地"が入る ch 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 7 100 56 23 7 45 95 2 65

ポインタ(アドレス型)をprintfで表示するには, アドレス型変数(ポインタ変数) アドレスを格納する変数 整数を格納できる変数があるように, アドレスを格納する変数がある. char ch; /* char型を格納する */ char *p; /* char型変数のアドレスを格納 */ p = &ch; printf("アドレスは%pです\n", p); ポインタ(アドレス型)をprintfで表示するには, %p を使う.

*演算子 アドレスの間接参照演算子. アドレスの中身にアクセスする. char a, b; char *p; p b a 変数a,b,pを宣言した. それぞれ,メモリの 6番地,5番地,4番地が 割与えられたとする. a,bは「整数(char型の値)」を入れる入れ物. pは「アドレス」を入れる入れ物. 注意:実際はアドレスは1バイトではない. アドレスの間接参照演算子. アドレスの中身にアクセスする. char a, b; char *p; p b a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地

*演算子 アドレスの間接参照演算子. アドレスの中身にアクセスする. char a, b; char *p; a = 53; つまり,メモリの6番地に 53を格納. p b a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 53

*演算子 アドレスの間接参照演算子. アドレスの中身にアクセスする. char a, b; char *p; a = 53; p = &a; &aで変数aのアドレスを得る. 変数aは6番地なので, pには6番地が代入される. 注意:実際は「番地」という 単位はつかない. p b a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 6 番地 53

*演算子 アドレスの間接参照演算子. アドレスの中身にアクセスする. char a, b; char *p; a = 53; p = &a; b = *p; アドレス(ポインタ)に * を付けると, そのアドレスの中身を 意味する. pは「6番地」. *pは「6番地の中身」 すなわち53 p b a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 6 番地 53 53

aは「整数(char)」を入れる入れ物. &演算子 と *演算子 (1) char a; char *p; aが5番地に, pが4番地に 割り当てられたとする. aは「整数(char)」を入れる入れ物. pは「アドレス」を入れる入れ物. p a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地

&演算子 と *演算子 (1) char a; char *p; p = &a; &a で, aのアドレスを獲得. 格納される. (メモリの4番地に "5番地"が格納される) p a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 5 番地

&演算子 と *演算子 (1) char a; char *p; p = &a; a = 8; aに8を代入. aは5番地なので, メモリの5番地に 8が格納される. p a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 5 番地 8

&演算子 と *演算子 (1) char a; char *p; p = &a; a = 8; printf("%d", *p); printf("%p", p); pは5番地. *pは「5番地の中身」. 5番地には, "8"が格納されているので, 8が表示される. 実質,a と *p は同一. 実行結果 8 p a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 5 番地 8

&演算子 と *演算子 (2) char a; char *p; aが5番地に, pが4番地に 割り当てられたとする. p a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地

&演算子 と *演算子 (2) char a; char *p; p = &a; &a で, aのアドレスを獲得. 格納される. (メモリの4番地に "5番地"が格納される) p a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 5 番地

&演算子 と *演算子 (2) char a; char *p; p = &a; *p = 8; pは5番地. *pは「5番地の中身」. 「5番地の中身」を "8"にする. 結果,aが"8"になる. p a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 5 番地 8

&演算子 と *演算子 (2) char a; char *p; p = &a; *p = 8; printf("%d", a); 実行結果 当然,"8"が 表示される. 実質,「aへの代入」と 「*pへの代入」は 同一である. aへの値の代入は 行っていないが, aの値が変わる. char a; char *p; p = &a; *p = 8; printf("%d", a); 実行結果 8 p a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 5 番地 8

&演算子 と *演算子 (3) char a; char *p; p = &a; a = 7; aに"7"を代入. すなわち, メモリの5番地に "7"を格納. p a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 5 番地 7

&演算子 と *演算子 (3) char a; char *p; p = &a; a = 7; *p = 8; pは「5番地」 「メモリ5番地を8にする」 aの値が変わってしまった. p a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 5 番地 8

&演算子 と *演算子 (3) char a; char *p; p = &a; a = 7; *p = 8; printf("%d", a); aの値を表示する. 当然,"8"が表示される. 実行結果 8 p a 0番地 1番地 2番地 3番地 4番地 5番地 6番地 7番地 8番地 9番地 5 番地 8

アドレス型変数(ポインタ変数) アドレスを格納する変数 整数を格納できる変数があるように, アドレスを格納する変数がある. char ch; /* 整数や文字コードの入れ物 */ int i; /* 整数の入れ物 */ char *p0; /* char型変数のアドレスの入れ物 */ int *p1; /* int型変数のアドレスの入れ物 */

不慣れなうちは,この様な記述は避けるのが無難? アドレス型変数(ポインタ変数) char a; /* a は char型 */ char *b; /* b は アドレス型 */ char c, *d, e, *f; /* cは char型,d はアドレス型, eは char型,f はアドレス型 */ char *g, h; /* g はアドレス型, hはchar型 */ 不慣れなうちは,この様な記述は避けるのが無難?

アドレス型変数(ポインタ変数) 変数の宣言 と 変数の使用 int i; 「int型」の変数iを作成. int *p; 「int*」型の変数pを作成. p = &i; 作成済み変数pに値を代入. *p = 4; *pというメモリに4を代入.

練習0 void main(){ int i, *j, k; i = 3; j = &i; k = i; *j = 4; k = 5; printf("i=%d\n", i); }

配列とアドレス(ポインタ) 配列の場合,アドレスは連続している. char ch[10]が100番地~109番地に割り振られた例. 101 番地 102 番地 103 番地 104 番地 105 番地 106 番地 107 番地 108 番地 109 番地

配列とアドレス(ポインタ) void main(){ char abc[5]; printf("アドレスは%p %p\n%p %p %pです.\n", &(abc[0]), &(abc[1]), &(abc[2]), &(abc[3]), &(abc[4])); } 実行結果 アドレスは0013FF78 0013FF79 0013FF7A 0013FF7B 0013FF7Cです.

配列とアドレス(ポインタ) void main(){ int abc[5]; printf("アドレスは%p %p\n%p %p %pです.\n", &(abc[0]), &(abc[1]), &(abc[2]), &(abc[3]), &(abc[4])); } 実行結果 アドレスは0013FF6C 0013FF70 0013FF74 0013FF78 0013FF7Cです.

配列とアドレス(ポインタ) 配列の場合,アドレスは連続している. int i[3]が100番地~111番地に割り振られた例 (この例では) int型は 4バイトである. i[0] i[1] i[2] 100 番地 101 番地 102 番地 103 番地 104 番地 105 番地 106 番地 107 番地 108 番地 109 番地 110 番地 111 番地

配列とアドレス(ポインタ) char ch[12]; char *p; 実行結果 0013FF74 p = &(ch[0]); printf("%p\n", p); p = ch; 実行結果 0013FF74 添え字を付けずに ([0]などを付けずに) 配列名だけを書くと, 配列の先頭のアドレス を意味する. この場合,ch と &(ch[0]) は同義.

ポインタ演算 char ch[10]; char *p; p = ch; printf("%p\n", p); p++; 実行結果 0013FF74 0013FF75

ch[10]が100番地~109番地に割り当てられたとする. 配列とアドレス(ポインタ) char ch[10]; char *p; p = ch; pには, ch[10]の先頭アドレスである 100番地が代入される. ch[10]が100番地~109番地に割り当てられたとする. ch[0] ch[1] ch[2] ch[3] ch[4] ch[5] ch[6] ch[7] ch[8] ch[9] 100 番地 101 番地 102 番地 103 番地 104 番地 105 番地 106 番地 107 番地 108 番地 109 番地

配列とアドレス(ポインタ) char ch[10]; char *p; p = ch; *p = 1; pが100番地なので, 「100番地の内容」を "1"にする. つまり,ch[0]が "1"になる. ch[0] ch[1] ch[2] ch[3] ch[4] ch[5] ch[6] ch[7] ch[8] ch[9] 100 番地 101 番地 102 番地 103 番地 104 番地 105 番地 106 番地 107 番地 108 番地 109 番地 1

配列とアドレス(ポインタ) char ch[10]; char *p; p = ch; *p = 1; p++; 「101番地」になる. ch[0] ch[1] ch[2] ch[3] ch[4] ch[5] ch[6] ch[7] ch[8] ch[9] 100 番地 101 番地 102 番地 103 番地 104 番地 105 番地 106 番地 107 番地 108 番地 109 番地 1

配列とアドレス(ポインタ) char ch[10]; char *p; p = ch; *p = 1; p++; *p = 2; 101番地の中身が "2"になる. すなわちch[1]が ch[0] ch[1] ch[2] ch[3] ch[4] ch[5] ch[6] ch[7] ch[8] ch[9] 100 番地 101 番地 102 番地 103 番地 104 番地 105 番地 106 番地 107 番地 108 番地 109 番地 1 2

配列とアドレス(ポインタ) char ch[10]; char *p; p = ch; *p = 1; p++; *p = 2; 100 番地 101 番地 102 番地 103 番地 104 番地 105 番地 106 番地 107 番地 108 番地 109 番地 1 2

ポインタ演算 int i[10]; int *p; p = i; printf("%p\n", p); p++; 差が4 実行結果 0013FF74 0013FF78 差が4 i[0] i[1] i[2] 0013 FF74 番地 0013 FF75 番地 0013 FF76 番地 0013 FF77 番地 0013 FF78 番地 0013 FF79 番地 0013 FF7a 番地 0013 FF7b 番地 0013 FF7c 番地 0013 FF7d 番地 0013 FF7e 番地 0013 FF7f 番地

ポインタ演算 int型変数のアドレスに1足すと, 4バイト分増加して,次のintのアドレスになる. char型変数のアドレスを1足すと, 減算も同様. char型変数のアドレスを1足すと, 1バイト分増加して,次のcharのアドレスになる. ただし, int型が4バイトとは限らない. 処理系による.ほとんどの例で4バイト. char型は必ず1バイト.

ポインタ演算 p++を行うと, char *p; の場合,アドレスが1番地進む. int *p; の場合,アドレスが4番地進む. double *p の場合,アドレスが8番地進む. (ただし,int型が4バイト,double型が8バイトの場合)

ポインタ演算 以下の様な操作が可能. 以下の様な操作は行えない. アドレスに整数を加算/減算して,次の/前のアドレスに格納されているデータにアクセス. アドレス同士の引き算をし,何個離れているか調べる 以下の様な操作は行えない. アドレスに乗算/除算を行う. アドレス同士の加算.

ポインタ演算 (OK) char *p, *q; int i; の場合, p++; OK. pのアドレスが1番地増える. p=q+2; OK. pはqの2番地後. p=q-2; OK. pはqの2番地前. i=p-q; OK. iにはpとqの差が代入される.

ポインタ演算 (NG) char *p, *q, *r; の場合, p=q+r; NG.アドレス同士の加算はNG. p=q*2; NG.アドレスに乗算と除算はNG. p=q/2; NG.アドレスに乗算と除算はNG. p=q+1.2; NG.加算減算は整数のみ.

*演算子 と [] int *p; int x[10]; p = x; のとき p と &(x[0]) と x は同義.

*演算子 と [] int *p; int x[10]; p = x; のとき *p と x[0] と *x は同義.

練習1 void main(){ int i[2], *j; i[0]=3; i[1]=7; j=i; j++; printf("%d\n", *j); }

練習2 void main(){ int i[2], *j; i[0]=3; i[1]=7; j=i; (*j)++; printf("%d\n", *j); }

注意 int *p, x[10]; p = x; のとき p++; ←これはOK. x++; ←これはNG. x は &(x[0]) という定数. よって,読めるが書けない.

関数の仮引数と実引数(値渡し) void func(int a){ a = 7; } void main(){ int x = 3; func(x); printf("%d\n", x); x 100 番地 3

関数の仮引数と実引数(値渡し) void func(int a){ a = 7; } void main(){ int x = 3; func(x); printf("%d\n", x); x 100 番地 3

関数の仮引数と実引数(値渡し) void func(int a){ a = 7; } void main(){ int x = 3; func(x); printf("%d\n", x); x a 100 番地 104 番地 3 3 別のメモリに割り当てられた 別の変数

関数の仮引数と実引数(値渡し) void func(int a){ a = 7; } void main(){ int x = 3; func(x); printf("%d\n", x); x a 100 番地 104 番地 3 7 別のメモリに割り当てられた 別の変数

関数の仮引数と実引数(値渡し) void func(int a){ a = 7; } void main(){ int x = 3; func(x); printf("%d\n", x); x 100 番地 3 実行結果 3

関数の仮引数と実引数(値渡し) void func(int x){ x = 7; } void main(){ int x = 3; この2個を 同じ名前にしても 結果は変わらない. void func(int x){ x = 7; } void main(){ int x = 3; func(x); printf("%d\n", x); x x 100 番地 104 番地 3 3 実行結果 別のメモリに割り当てられた 別の変数 3

関数の仮引数と実引数(参照渡し) void func(int *p){ *p = 7; } void main(){ int x = 3; int *px; px = &x; func(px); printf("%d\n", x); x px 100 番地 104 番地 3

関数の仮引数と実引数(参照渡し) void func(int *p){ *p = 7; } void main(){ int x = 3; int *px; px = &x; func(px); printf("%d\n", x); x px 100 番地 104 番地 3 100 番地

関数の仮引数と実引数(参照渡し) void func(int *p){ *p = 7; } void main(){ int x = 3; int *px; px = &x; func(px); printf("%d\n", x); x px 100 番地 104 番地 3 100 番地

関数の仮引数と実引数(参照渡し) void func(int *p){ *p = 7; } void main(){ int x = 3; int *px; px = &x; func(px); printf("%d\n", x); x px p 100 番地 104 番地 108 番地 3 100 番地 100 番地

関数の仮引数と実引数(参照渡し) void func(int *p){ *p = 7; } void main(){ int x = 3; int *px; px = &x; func(px); printf("%d\n", x); x px p 100 番地 104 番地 108 番地 7 100 番地 100 番地

関数の仮引数と実引数(参照渡し) void func(int *p){ *p = 7; } void main(){ int x = 3; int *px; px = &x; func(px); printf("%d\n", x); x px 100 番地 104 番地 7 100 番地 実行結果 7

応用例:値を2個返す関数 C言語では,戻り値は1個しか用意できない. void sq_cu(int n, int *ps, int *pc){ *ps = n*n; *pc = n*n*n; } void main(){ int square, cube; sq_cu(3, &square, &cube); printf("%d %d\n", square, cube); squareが100番地, cubeが104番地で あったとする.

squareが100番地,cubeが104番地であったとする. 応用例:値を2個返す関数 C言語では,戻り値は1個しか用意できない. void sq_cu(int n, int *ps, int *pc){ *ps = n*n; *pc = n*n*n; } void main(){ int square, cube; sq_cu(3, &square, &cube); printf("%d %d\n", square, cube); 引数を 3,100番地,104番地 として,関数にジャンプ. squareが100番地,cubeが104番地であったとする.

squareが100番地,cubeが104番地であったとする. 応用例:値を2個返す関数 C言語では,戻り値は1個しか用意できない. void sq_cu(int n, int *ps, int *pc){ *ps = n*n; *pc = n*n*n; } void main(){ int square, cube; sq_cu(3, &square, &cube); printf("%d %d\n", square, cube); nが3, psが100番地, pcが104番地として 関数sq_cuを開始. squareが100番地,cubeが104番地であったとする.

squareが100番地,cubeが104番地であったとする. 応用例:値を2個返す関数 C言語では,戻り値は1個しか用意できない. void sq_cu(int n, int *ps, int *pc){ *ps = n*n; *pc = n*n*n; } void main(){ int square, cube; sq_cu(3, &square, &cube); printf("%d %d\n", square, cube); psは"100番地". 「100番地の中身を 3*3にする」 という処理. これにより squareの値が 変わる. squareが100番地,cubeが104番地であったとする.

squareが100番地,cubeが104番地であったとする. 応用例:値を2個返す関数 C言語では,戻り値は1個しか用意できない. void sq_cu(int n, int *ps, int *pc){ *ps = n*n; *pc = n*n*n; } void main(){ int square, cube; sq_cu(3, &square, &cube); printf("%d %d\n", square, cube); pcは"104番地". 「104番地の中身を 3*3*3にする」 という処理. これにより cubeの値が 変わる. squareが100番地,cubeが104番地であったとする.

squareが100番地,cubeが104番地であったとする. 応用例:値を2個返す関数 C言語では,戻り値は1個しか用意できない. void sq_cu(int n, int *ps, int *pc){ *ps = n*n; *pc = n*n*n; } void main(){ int square, cube; sq_cu(3, &square, &cube); printf("%d %d\n", square, cube); 実行結果 9 27 squareが100番地,cubeが104番地であったとする.

やってはいけないこと void main(){ int i=0, j[2]; j[0] = 7; j[1] = 8; /* OK! */ j[2] = 9; /* NG! */ printf("%d\n", i); } 実行結果 9 j[0] j[1] i 84 番地 85 番地 86 番地 87 番地 88 番地 89 番地 90 番地 91 番地 92 番地 93 番地 94 番地 95 番地 7 8 9

やってはいけないこと void main(){ int i[2], *p; p = i; /* OK. pはi[0]のアドレス */ *p = 7; /* OK. i[0] が 7 になる */ p++; /* OK. pはi[1]のアドレス */ *p = 8; /* OK. i[1] が 8 になる */ p++; /* NG.pはi[2]のアドレス? */ *p = 9; /* NG! i[2]相当の場所に 書き込みをしてしまう! */ }

ポインタへのポインタ int i; int *p; int **pp; iは int(整数)を入れる箱. 難しい! ポインタへのポインタ int i; int *p; int **pp; iは int(整数)を入れる箱. pは 「intの箱のアドレス」を入れる箱. ppは「『アドレスを入れる箱』のアドレス」を入れる箱. i p pp 84 番地 85 番地 86 番地 87 番地 88 番地 89 番地 90 番地 91 番地 92 番地 93 番地 94 番地 95 番地

ポインタへのポインタ int i; int *p; int **pp; i = 3; 難しい! i p pp 3 84 番地 85 番地 86 番地 87 番地 88 番地 89 番地 90 番地 91 番地 92 番地 93 番地 94 番地 95 番地 3

ポインタへのポインタ int i; int *p; int **pp; i = 3; p = &i; 難しい! i p pp 3 84番地 85 番地 86 番地 87 番地 88 番地 89 番地 90 番地 91 番地 92 番地 93 番地 94 番地 95 番地 3 84番地

ポインタへのポインタ int i; int *p; int **pp; i = 3; p = &i; pp = &p; 難しい! i p 84 番地 85 番地 86 番地 87 番地 88 番地 89 番地 90 番地 91 番地 92 番地 93 番地 94 番地 95 番地 3 84番地 88番地

ポインタへのポインタ int i; int *p; int **pp; i = 3; p = &i; 実行結果 pp = &p; 難しい! ポインタへのポインタ int i; int *p; int **pp; i = 3; p = &i; pp = &p; printf("%d\n", **pp); 実行結果 3 i p pp 84 番地 85 番地 86 番地 87 番地 88 番地 89 番地 90 番地 91 番地 92 番地 93 番地 94 番地 95 番地 3 84番地 88番地

ポインタへのポインタ int i, *p, **pp; i = 3; p = &i; pp = &p; 難しい! ポインタへのポインタ *ppは「88番地の中身」 なので, 「84番地」を意味する. *(*pp) は *(84番地) なので, 「84番地の中身」となり "3"を意味する. int i, *p, **pp; i = 3; p = &i; pp = &p; printf("%d\n", **pp); i p pp 84 番地 85 番地 86 番地 87 番地 88 番地 89 番地 90 番地 91 番地 92 番地 93 番地 94 番地 95 番地 3 84番地 88番地

多次元配列の配置 int a[3][2]は, 「長さ2の配列」が3本であって, 「長さ3の配列」が2本ではない?

復習 void main(){ int i=10, j=11; int *p; p=&j; printf("*p=%d\n",*p); printf("i=%d j=%d",i,j); } 100 番地 104 番地 108 番地 10 11 104番地 の中身 10 11 10 11 104 10 11 104 10 7 104 10 7 104 104番地 の中身 *p=11 i=10 j=7 実行結果

練習 3 何と表示される? void main(){ int x=10, y=11, z=12; int *p; printf("x=%d y=%d z=%d\n",x,y,z); p=&x; *p=5; p=&y; *p=6; p=&z; *p=7; }

練習 4 何と表示される? void main(){ int x[3]; int *p; x[0]=10; x[1]=20; x[2]=30; printf("x[0]=%d x[1]=%d x[2]=%d\n",x[0],x[1],x[2]); p=x; /* p=&(x[0]) の意味 */ *p=7; p++; *p=6; *p=5; }

練習 5 何と表示される? void main(){ int x[3]; int *p; x[0]=10; x[1]=20; x[2]=30; printf("x[0]=%d x[1]=%d x[2]=%d\n",x[0],x[1],x[2]); p=x; /* p=&(x[0]) の意味 */ *p=7; (*p)++; *p=6; }