第1回 プログラミング演習3 センサーアプリケーション

Similar documents
Microsoft Word - no202.docx

Microsoft Word - 3new.doc

02: 変数と標準入出力

Prog1_10th

7 ポインタ (P.61) ポインタを使うと, メモリ上のデータを直接操作することができる. 例えばデータの変更 やコピーなどが簡単にできる. また処理が高速になる. 7.1 ポインタの概念 変数を次のように宣言すると, int num; メモリにその領域が確保される. 仮にその開始のアドレスを 1

02: 変数と標準入出力

Microsoft PowerPoint - 11.pptx

memo

PowerPoint Presentation

char int float double の変数型はそれぞれ 文字あるいは小さな整数 整数 実数 より精度の高い ( 数値のより大きい より小さい ) 実数 を扱う時に用いる 備考 : 基本型の説明に示した 浮動小数点 とは数値を指数表現で表す方法である 例えば は指数表現で 3 書く

kiso2-09.key

PowerPoint プレゼンテーション

Taro-ポインタ変数Ⅰ(公開版).j

PowerPoint プレゼンテーション - 物理学情報処理演習

Microsoft PowerPoint - 5Chap15.ppt

Microsoft PowerPoint - kougi7.ppt

C プログラミング演習 1( 再 ) 2 講義では C プログラミングの基本を学び 演習では やや実践的なプログラミングを通して学ぶ

プログラミング実習I

memo

memo

PowerPoint プレゼンテーション

PowerPoint プレゼンテーション

コマンドラインから受け取った文字列の大文字と小文字を変換するプログラムを作成せよ 入力は 1 バイトの表示文字とし アルファベット文字以外は変換しない 1. #include <stdio.h> 2. #include <ctype.h> /*troupper,islower,isupper,tol

program7app.ppt

PowerPoint プレゼンテーション - 物理学情報処理演習

バイオプログラミング第 1 榊原康文 佐藤健吾 慶應義塾大学理工学部生命情報学科

Microsoft Word - Cプログラミング演習(1)_2012

02: 変数と標準入出力

今までの復習 プログラムで最低限必要なもの 入力 ( キーボードから ファイルから ) 出力 ( 画面へ ファイルへ ) 条件分岐 : 条件の成立 不成立により 異なる動作をする 繰り返し : 一定の回数の繰返し 条件成立の間の繰返し 関数の定義 関数の呼び出し C ではそれ以外に ポインタ データ

プログラミング実習I

ポインタ変数

cp-7. 配列

gengo1-11

2006年10月5日(木)実施

CプログラミングI

Taro-ファイル処理(公開版).jtd

C プログラミング 1( 再 ) 第 4 回 講義では C プログラミングの基本を学び 演習では やや実践的なプログラミングを通して学ぶ 1

書式に示すように表示したい文字列をダブルクォーテーション (") の間に書けば良い ダブルクォーテーションで囲まれた文字列は 文字列リテラル と呼ばれる プログラム中では以下のように用いる プログラム例 1 printf(" 情報処理基礎 "); printf("c 言語の練習 "); printf

gengo1-8

Prog1_12th

Prog1_6th

プログラミングI第5回

演算増幅器

£Ã¥×¥í¥°¥é¥ß¥ó¥°ÆþÌç (2018) - Â裵²ó ¨¡ À©¸æ¹½Â¤¡§¾ò·ïʬ´ô ¨¡

/*Source.cpp*/ #include<stdio.h> //printf はここでインクルードして初めて使えるようになる // ここで関数 average を定義 3 つの整数の平均値を返す double 型の関数です double average(int a,int b,int c){

PowerPoint プレゼンテーション

Microsoft PowerPoint - kougi9.ppt

Microsoft Word - Cプログラミング演習(12)

02: 変数と標準入出力

PowerPoint プレゼンテーション

Microsoft Word - no103.docx

< F2D837C E95CF CF68A4A94C5816A2E6A>

02: 変数と標準入出力

第9回 配列(array)型の変数

プログラミングI第10回

02: 変数と標準入出力

Microsoft PowerPoint - prog04.ppt

<4D F736F F D20438CBE8CEA8D758DC F0939A82C282AB2E646F63>

Microsoft PowerPoint - exp2-02_intro.ppt [互換モード]

PowerPoint プレゼンテーション

講習No.12

Microsoft PowerPoint pptx

Microsoft PowerPoint ppt

Microsoft PowerPoint - lec10.ppt

gengo1-2

Microsoft Word - Cプログラミング演習(11)

Microsoft Word - Cプログラミング演習(10)

プログラミング及び演習 第1回 講義概容・実行制御

<4D F736F F D20438CBE8CEA8D758DC03389F0939A82C282AB2E646F63>

Microsoft Word - no12.doc

Microsoft PowerPoint - program.ppt [互換モード]

Microsoft PowerPoint - 09.pptx

ファイル入出力

プログラミング演習3 - Cプログラミング -

gengo1-12

ファイル入出力

PowerPoint プレゼンテーション

Microsoft Word - no11.docx

02: 変数と標準入出力

Microsoft PowerPoint - 計算機言語 第7回.ppt

PowerPoint Presentation

今回のプログラミングの課題 ( 前回の課題で取り上げた )data.txt の要素をソートして sorted.txt というファイルに書出す ソート (sort) とは : 数の場合 小さいものから大きなもの ( 昇順 ) もしくは 大きなものから小さなもの ( 降順 ) になるよう 並び替えること

02: 変数と標準入出力

プログラミング基礎

Microsoft Word - Cプログラミング演習(9)

情報処理演習 B8クラス

PowerPoint プレゼンテーション

プログラミング方法論 II 第 14,15 回 ( 担当 : 鈴木伸夫 ) 問題 17. x 座標と y 座標をメンバに持つ構造体 Point を作成せよ 但し座標 は double 型とする typedef struct{ (a) x; (b) y; } Point; 問題 18. 問題 17 の

Microsoft PowerPoint - CproNt02.ppt [互換モード]

プログラミング演習3 - Cプログラミング -

Prog1_6th

02: 変数と標準入出力

Cプログラミング1(再) 第2回

02: 変数と標準入出力

C 言語第 3 回 2 a と b? 関係演算子 a と b の関係 関係演算子 等しい a==b 等しくない a!=b より大きい a>b 以上 a>=b より小さい a<b 以下 a<=b 状態 真偽 値 条件が満たされた場合 TRUE( 真 ) 1(0 以外 ) 条件が満たされなかった場合 F

PowerPoint Presentation

gengo1-12

C 言語の式と文 C 言語の文 ( 関数の呼び出し ) printf("hello, n"); 式 a a+4 a++ a = 7 関数名関数の引数セミコロン 3 < a "hello" printf("hello") 関数の引数は () で囲み, 中に式を書く. 文 ( 式文 ) は

Microsoft PowerPoint - prog03.ppt

解答編 第 9 章文字データの取り扱い 演習問題 9.1 文法事項 1 ) コンピュータにおける 文字データの取り扱いについて説明しなさい コンピュータでは 文字に整数の番号を割り当てて ( コード化して ) 文字コードとして扱います 実際に用いられる文字コードとして ASCII コード EUC コ

Transcription:

C プログラミング - ポインタなんて恐くない! - 藤田悟 fujita_s@hosei.ac.jp

目標 C 言語プログラムとメモリ ポインタの関係を深く理解する C 言語プログラムは メモリを素のまま利用できます これが原因のエラーが多く発生します メモリマップをよく頭にいれて ポインタの動きを理解できれば C 言語もこわくありません

1. ポインタ入門編

ディレクトリの作成と移動 mkdir chapter1 cd chapter1

前提知識 : printf C 言語のコンソール出力の基本形 printf( 書式, 変数 1, 変数 2, ); 書式には 表示したい文字列と 書式文字列 (%d など ) を書く 書式制御文字列と同じ数だけの変数を後ろに続ける %d : int の表示 %ld : long の表示 %f : float, double の表示 %x : 整数の 16 進数表示 %lx: long 整数の 16 進数表示 %s : 文字列表示 %c : 文字表示 n : 改行

prog0.c #include <stdio.h> // 入出力を使うときに宣言 // argc は引数の数 argv は引数の文字列の配列 int main(int argc, char *argv[]) { int x = 10; printf( %d n, x); コンパイル : gcc prog0.c o prog0 実行 :./prog0

変数の大きさ 変数のアドレス 変数を格納するためには メモリが必要 メモリには 1 バイト単位に アドレス ( 番地 ) がある int x; という変数に対して &x と指定すると 変数 x のアドレス ( ポインタアドレス ) がわかる アドレスを画面に表示するには printf( %lx n, &x); // %lx は long の 16 進数表示 変数を格納するためには メモリが何バイト必要か 変数が何バイトか知るには sizeof x を用いる printf( %d n, sizeof x);

prog1.c int の変数 x, y の値と アドレスと 大きさをまとめて表示する #include <stdio.h> int main(int argc, char *argv[]) { int x = 112; int y = 115; // sizeof x は sizeof(x) のように書いても良い printf("%d %lx %d n", x, &x, sizeof x); printf("%d %lx %d n", y, &y, sizeof y);

演習 1: メモリマップを完成させよう! prog1.c を実行して x や y が 何番地のメモリに格納されていたかを確認して メモリマップを作成せよ 0 7fff3c4f92a8 7fff3c4f92a9 7fff3c4f92aa 7fff3c4f92ab x と y は どちらが小さいアドレスにある? x と y のサイズとメモリは一致しているか? 7fff3c4f92ac 7fff3c4f92ad 7fff3c4f92ae 7fff3c4f92af 7fffffffffff PC が 4G バイトのメモリを持っているとすると 4G = 0x100000000 (16 進数で 0 が 8 個 ) 表示は 7fff から始まっているけれど 本当はそんなに大きなメモリはない

4 バイト境界のメモリマップ 1 バイトずつだと 縦に長くなりすぎるので 4 バイトずつ横に並べて メモリマップを書く 0 7fff3c4f92a8 7fff3c4f92ac 00 00 00 00 00 00 00 変数 y 00 変数 x なぜか 上下が逆!! 7ffffffffffc

演習 2: prog2.c int x; に加えて long y; float f; double d; char c; を宣言して 値 メモリアドレス サイズを表示して メモリマップを作成せよ char c の値は int で表示してよい (%d を使う ) x, y, f, d, c は どの順番に格納されるのか ゴミの値が入っていないか? ゴミとは 初期値が 0 でない変な値

prog2.c #include <stdio.h> 変ですねぇ int main(int argc, char **argv) { int x; long y; float f; double d; char c; 7fff3c4f9298 printf("%d %lx %d n", x, &x, sizeof(x)); 7fff3c4f929c printf("%ld %lx %d n", y, &y, sizeof(y)); printf("%f %lx %d n", f, &f, sizeof(f)); 7fff3c4f92a0 printf("%f %lx %d n", d, &d, sizeof(d)); 7fff3c4f92a4 printf("%d %lx %d n", c, &c, sizeof(c)); 7fff3c4f92a8 7fff3c4f92ac 0 7fff3c4f928c 7fff3c4f9290 7fff3c4f9294 c d f y x 7ffffffffffc

配列のメモリを見る 配列の宣言は int array[4]; のように行う 配列要素のメモリアドレスは &array[0], &array[1] のようにして 得られる 配列の先頭アドレスは &array でわかる 配列の全体のサイズは sizeof array 配列の一要素のサイズは sizeof array[0] &array のサイズは sizeof &array

prog3.c #include <stdio.h> int main(int argc, char **argv) { int x; int array[4]; printf("%d %lx %d n", x, &x, sizeof(x)); printf("%d %lx %d n", array[0], &array[0], sizeof array[0]); printf("%d %lx %d n", array[1], &array[1], sizeof array[1]); printf("%d %lx %d n", array[2], &array[2], sizeof array[2]); printf("%d %lx %d n", array[3], &array[3], sizeof array[3]); // & の位置や sizeof の位置に注意!! 書式の %lx にも注意!! printf("%lx %lx %d %d n", array, &array, sizeof array, sizeof &array); 演習 3: メモリマップを描け

ポインタ変数 アドレスを格納するための変数をポインタ変数と呼ぶ int *xp; // 整数へのポインタを格納する変数 ポインタ変数へのアドレスの代入には 元の型の変数のアドレスを代入する int *xp; int x; xp = &x; // x のアドレスを xp に代入 演習 4: ポインタ変数の sizeof と 上記のプログラムで格納されたポインタ変数の値を確認し メモリマップを作成せよ

演習 4 prog4.c #include <stdio.h> int main(int argc, char **argv) { int x; int *xp; xp = &x; x = 3; printf("x = %d %lx n", x, &x); printf( xp = %lx %lx n, xp, &xp); // &xp は変数 xp のアドレス

ポインタ変数を使った値の書き換え int x = 3; int *xp; xp = &x; という状況で xp は x の変数のアドレスが入っている ここで *xp = 5; と実行すると xp が指し示すアドレスの先のデータが 5 に書き換わる すなわち x の値が書き換わる

prog5.c #include <stdio.h> int main(int argc, char *argv[]) { int x = 3; int *xp; xp = &x; printf("x = %d %lx n", x, &x); printf( xp = %lx %lx n", xp, &xp); *xp = 5; printf("x = %d %lx n", x, &x); printf( xp = %lx %lx n", xp, &xp); 演習 5: メモリマップを作成せよ

配列要素のアドレスをポインタ変数に格納 配列要素のアドレスは &array[0] のようにして得られる ポインタ変数の宣言は int *xp; したがって xp = &array[0]; のように書くと 配列の第 0 要素のアドレスが xp に代入される *xp = 12; のように書いて 配列の中身をすべて 12 に書き換え メモリマップで確認しよう

演習 6: prog6.c #include <stdio.h> int main(int argc, char *argv[]) { int array[4]; int *xp; printf("%lx %lx n", xp, &xp); int i; for(i = 0; i < 4; i++) { xp = &array[i]; printf("before: %d %lx %d n", array[i], &array[i], sizeof array[i]); *xp = 12; printf("after: %d %lx %d n", array[i], &array[i], sizeof array[i]);

ポインタ変数のインクリメント ポインタ変数に 1 を足すと ポインタの指し示す 型 の大きさ分だけアドレスが足される int *xp; の時 xp++ はアドレスに 4 を足す long *xp; の時 xp++ はアドレスに 8 を足す ポインタ変数を加算しながら 指し示すアドレスの内容を書き換えてみよう *xp = 12; xp++; まとめて書くと *xp++ = 12; と書くことができる

演習 7: prog7.c #include <stdio.h> int main(int argc, char *argv[]) { int array[4]; int *xp; printf("%lx %lx n", xp, &xp); int i; xp = &array[0]; for(i = 0; i < 4; i++) { printf("before: %d %lx n", array[i], xp); *xp = 12; xp++; printf("after: %d %lx n", array[i], xp);

文字列は char の配列 C 言語には Java のような String 型はなく 文字 (char: 8bits=1byte) の配列として扱う 文字列の長さもわからないので char の値に 0 が来たら 文字列の終了とみなす char univ[] = hosei ; univ h o s e i 0 0 と書いたのは 文字の 0 と区別するため 文字の 0 は整数の 0x20 であるが 0 は整数の 0

演習 8: prog8.c #include <stdio.h> int main(int argc, char *argv[]) { char univ[] = "hosei"; int i = 0; char *cp; cp = univ; printf("%c %d %x %lx %lx n", *cp, *cp, *cp, cp, &cp); for(i = 0; i < 6; i++) { printf("%c %d %x %lx n", *cp, *cp, *cp, cp); cp++;

整数はどのように格納される? int *xp の値や x の値は アドレスにどのような順番で格納されているのだろうか これを確認するためには 1 バイトずつデータを見なければならない 1 バイトのデータは char 型です 特に 符号なしの 1 バイトとしたい場合 unsigned char と書きます

演習 9: prog9.c #include <stdio.h> int main(int argc, char *argv[]) { int x = 0x12345678; long lx = 0x123456789abcdef0; unsigned char *cp; printf("%lx n", &cp); int i = 0; cp = (unsigned char *)&x; for(i = 0; i < 4; i++) { printf("%x %lx n", *cp, cp); cp++; cp = (unsigned char *)&lx; for(i = 0; i < 8; i++) { printf("%x %lx n", *cp, cp); cp++;

演習 10: prog10.c 下記のプログラムを実行して 何が起こっているかを メモリマップに示して説明せよ #include <stdio.h> int main(int argc, char *argv[]) { long lx = 0x6965736f68; char *cp; cp = (char *)&lx; printf("%s n", cp);

関数呼び出し 関数呼び出しをしたとき 変数はどのようにメモリにマッピングされているか 特に 再帰呼び出しした時にメモリマップはどうなるのか 関数から戻ってきたときに 関数内で使っていたローカル変数はどうなるのか 別の関数が呼び出された時のローカル変数領域になり 上書きされる ローカル変数領域へのポインタを 呼び出し元の関数に引き渡すと 大きなエラーになる

演習 11: prog11.c #include <stdio.h> int fact(int x) { int result; printf("x = %d %lx n", x, &x); printf("result = %d %lx n", result, &result); if(x == 0) { result = 1; else { result = x * fact(x - 1); return result; int main(int argc, char **argv) { int x; x = 2; printf("x = %d %lx n", x, &x); int y = fact(x); printf("y = %d %lx n", y, &y); printf("%d n", y);

グローバル変数 malloc() 関数内のローカル変数ではなく 関数の外に定義するグローバル変数 malloc() で動的に確保されるメモリは どこに確保されるのであろうか

演習 12: prog12.c #include <stdio.h> #include <stdlib.h> // malloc を使うときに必要 int ex; int main(int argc, char **argv) { int x; char *cp = "hosei"; char cparray[] = "hosei"; char *mp = (char *)malloc(8); printf("x = %d %lx n", x, &x); printf("ex = %d %lx n", ex, &ex); printf("cp = %lx %lx n", cp, &cp); printf("cparray = %lx %lx n", cparray, &cparray); printf("mp = %lx %lx n", mp, &mp);

ここまででのまとめ

C 言語はメモリが命 ポインタはメモリのアドレス ( 番地 ) である int は 4 バイト long は 8 バイトで 変数宣言すると 適当に隙間も作りながらメモリが割り当てられる ポインタ変数は 64bitsOS の時 64bits(8 バイト ) ポインタ変数を +1 すると 変数の型に合わせて 数バイトずつアドレスが増加する ローカル変数は 関数呼び出しごとにメモリが割り当てられる ローカル変数で配列を確保すると大変なことに

2. ポインタ応用編

ディレクトリを変更します cd.. mkdir chapter2 cd chapter2

2 次元配列 unsigned char array[3][3]; と宣言したら どのようなメモリが割り当てられるのか array の値と sizeof は? array[0] の値と sizeof は? array[0][0] の値と sizeof は? &array の値はと sizeof は? &array[0] の値と sizeof は? &array[0][0] の値と sizeof は? &array[0][1] の値は? &array[1][0] の値は? &array[1] の値は?

演習 1: prog1.c #include <stdio.h> int main(int argc, char *argv[]) { unsigned char array[3][3]; int i; int j; for(i = 0; i < 3; i++) { for(j = 0; j < 3; j++) { printf("%d %lx n", array[i][j], &array[i][j]); printf("array = %lx %d n", array, sizeof array); printf("array[0] = %lx %d n", array[0], sizeof array[0]); printf("array[0][0] = %lx %d n", array[0][0], sizeof array[0][0]); printf("&array = %lx %d n", &array, sizeof &array); printf("&array[0] = %lx %d n", &array[0], sizeof &array[0]); printf("&array[0][0] = %lx %d n", &array[0][0], sizeof &array[0][0]); printf("&array[0][1] = %lx %d n", &array[0][1], sizeof &array[0][1]); printf("&array[1][0] = %lx %d n", &array[1][0], sizeof &array[1][0]); printf("&array[1] = %lx %d n", &array[1], sizeof &array[1]);

演習 2: prog2.c #include <stdio.h> int main(int argc, char *argv[]) { unsigned char array[3][3]; unsigned char *cp; // char 配列をアクセスし 1 バイトごとに増えるポインタ編巣 unsigned char (*cpp)[3]; // char 配列 3 個分ずつ増えるポインタ変数 unsigned char (*cppp)[3][3]; // char 配列 3*3 個分ずつ増えるポインタ変数 cp = array[0]; printf("cp = %lx n", cp); cp++; printf("cp = %lx n", cp); cpp = array; printf("cpp = %lx n", cpp); cpp++; printf("cpp = %lx n", cpp); cppp = &array; printf("cppp = %lx n", cppp); cppp++; printf("cppp = %lx n", cppp);

配列とポインタ prog3.c で何が起きているのか 演習 3: プログラムを実行し メモリマップを作成した上で 動作を説明せよ

prog3.c #include <stdio.h> int main(int argc, char *argv[]) { unsigned char array[3][3]; unsigned char *cp; unsigned char (*cpp)[3]; unsigned char (*cppp)[3][3]; cp = array[0]; cp++; *cp = 'h'; cp[1] = 'o'; cpp = array; cpp++; *cpp[0] = 's'; cpp[0][2] = 'e'; cppp = &array; (*cppp)[2][2] = 'i'; int i, j; for(i = 0; i < 3; i++) { for(j = 0; j < 3; j++) { printf("%c %lx n", array[i][j], &array[i][j]);

演習 4 30 バイトの unsigned char の配列を calloc() で確保し 0 を含む 3 の倍数番目の要素を 0xff に書き換えよ #include <stdlib.h> unsigned char *cp = (unsigned char *)calloc(30, 1); 3 の倍数番目の要素に どのようにして 値をセットするか?

演習 5 30 バイトの unsigned char の配列を calloc() で確保し 0 を含む 3 の倍数番目の要素を 1 3 の倍数 +1 番目の要素を 2 3 の倍数 +2 番目の要素を 3 にセットせよ 別途 10 バイトの unsigned char の配列を calloc() で用意し 上記の 30 バイトの配列から 3 バイトずつ加算をした結果を格納せよ 結果的には 値 6 の入った 10 バイトの配列ができる