09: ポインタ・文字列 C プログラミング入門 総機1 (月1) Linux にログインし、以下の講義ページ を開いておくこと

Slides:



Advertisements
Similar presentations
C 言語講座 第 7 回 ポインター. メモリとアドレス(ポインターの前 に) コンピュータのメモリには 1 バイトずつ 0 番地、 1 番地、 2 番地・・・というように 住所が割り当てられている この住所をアドレスという。 メモリはデータをしまうもので それを引き出すためには メモリに番号(アドレス)を振っておけばよいな.
Advertisements

15.1 文字列処理の基本 15.2 文字列処理用ライブラリ関数
情報基礎演習B 後半第5回 担当 岩村 TA 谷本君.
プログラミング入門2 第10回 構造体 情報工学科 篠埜 功.
プログラミング入門2 第10回 構造体 情報工学科 篠埜 功.
基礎プログラミングおよび演習 第9回
プログラミング演習Ⅱ 第12回 文字列とポインタ(1)
岩村雅一 知能情報工学演習I 第9回(C言語第3回) 岩村雅一
12: コマンドライン引数 C プログラミング入門 総機1 (月1) Linux にログインし、以下の講義ページ を開いておくこと
C言語講座 第4回 ポインタ.
12: コマンドライン引数 C プログラミング入門 基幹7 (水5) Linux にログインし、以下の講義ページ を開いておくこと
第8回 プログラミングⅡ 第8回
理由:文字数より要素数の多い配列を用いた時に,文字列の最後を示すため
理由:文字数より要素数の多い配列を用いた時に,文字列の最後を示すため
構造体.
精密工学科プログラミング基礎 第9回資料 (12/11 実施)
ファイル操作と文字列の利用.
プログラミング論 関数ポインタ と 応用(qsort)
ちょっとした練習問題① 配列iroを['R', 'W', 'R', 'R', 'W' , 'W' , 'W']を宣言して、「W」のときの配列の番号をprintfで表示するようなプログラムを記述しなさい。
関数と配列とポインタ 1次元配列 2次元配列 配列を使って結果を返す 演習問題
プログラミング応用 printfと変数.
プログラミング論 II 2008年10月30日 文字列
Cプログラミング演習 第7回 メモリ内でのデータの配置.
プログラミング 4 記憶の割り付け.
2005年度 データ構造とアルゴリズム 第3回 「C言語の復習:再帰的データ構造」
第10章 これはかなり大変な事項!! ~ポインタ~
プログラミング入門2 第8回 ポインタ 情報工学科 篠埜 功.
岩村雅一 知能情報工学演習I 第9回(後半第3回) 岩村雅一
第7回 プログラミングⅡ 第7回
地域情報学 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.
岩村雅一 知能情報工学演習I 第9回(後半第3回) 岩村雅一
岩村雅一 知能情報工学演習I 第9回(C言語第3回) 岩村雅一
岩村雅一 知能情報工学演習I 第12回(C言語第6回) 岩村雅一
メモリとメモリアドレス, ポインタ変数,関数へのポインタ渡し
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
プログラミング言語論 第六回 理工学部 情報システム工学科 新田直也.
岩村雅一 知能情報工学演習I 第9回(後半第3回) 岩村雅一
プログラミング基礎a 第6回 C言語によるプログラミング入門 配列と文字列(その2)
15.1 文字列処理の基本 15.2 文字列処理用ライブラリ関数
プログラミング入門2 第9回 ポインタ 情報工学科 篠埜 功.
アルゴリズムとプログラミング (Algorithms and Programming)
11: 動的メモリ確保 C プログラミング入門 総機1 (月1) Linux にログインし、以下の講義ページ を開いておくこと
プログラミング 3 2 次元配列.
文字列へのポインタの配列 static char *lines[MAXLINES]; lines[0] NULL
第13回 ポインタ 1 1.
プログラミング論 ポインタ
09: ポインタ・文字列 C プログラミング入門 基幹7 (水5) Linux にログインし、以下の講義ページを開いておく こと
12: コマンドライン引数 C プログラミング入門 基幹7 (水5) Linux にログインし、以下の講義ページを開いておく こと
アルゴリズムとデータ構造1 2009年6月15日
11: 動的メモリ確保 C プログラミング入門 基幹7 (水5) Linux にログインし、以下の講義ページを開いておく こと
第5回 プログラミングⅡ 第5回
11: 動的メモリ確保 C プログラミング入門 基幹7 (水5) Linux にログインし、以下の講義ページ を開いておくこと
プログラミング論 文字列
プログラミング 4 文字列.
アルゴリズムとデータ構造 2010年6月17日
岩村雅一 知能情報工学演習I 第12回(後半第6回) 岩村雅一
11: 動的メモリ確保 C プログラミング入門 基幹2 (月4) Linux にログインし、以下の講義ページ を開いておくこと
2005年度 データ構造とアルゴリズム 第2回 「C言語の復習:配列」
情報処理Ⅱ 2005年11月25日(金).
プログラミング演習II 2004年11月 16日(第5回) 理学部数学科・木村巌.
15.1 文字列処理の基本 15.2 文字列処理用ライブラリ関数
プログラミング演習II 2004年11月 2日(第3回) 理学部数学科・木村巌.
プログラミング入門2 第5回 配列 変数宣言、初期化について
情報処理Ⅱ 小テスト 2005年2月1日(火).
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
プログラミング 3 ポインタ(1).
12: コマンドライン引数 C プログラミング入門 基幹2 (月4) Linux にログインし、以下の講義ページ を開いておくこと
岩村雅一 知能情報工学演習I 第9回(C言語第3回) 岩村雅一
Presentation transcript:

09: ポインタ・文字列 C プログラミング入門 総機1 (月1) Linux にログインし、以下の講義ページ を開いておくこと http://www-it.sci.waseda.ac.jp/ teachers/w483692/CPR1/ 2015-06-08

関数できなかったこと 配列を引数として渡す, 戻り値として返す 文字列を扱う 呼び出し元の変数を直接書き換える 複数の値を返す 例: 2 つの変数の値を入れ替える関数 例: scanf() はそのようなことを行う関数の一つ 複数の値を返す ⇒ポインタにより実現 2015-06-08 C プログラミング入門 総機1 (月1)

メモリには 1 byte ごとにアドレス (番地; address) という数値が振られている メモリとアドレス メモリには 1 byte ごとにアドレス (番地; address) という数値が振られている 変数は変数名を介してメモリの操作をするのでアドレスを意識することはない コンパイラが生成するマシン語はアドレスを使ってメモリ操作を行っている int year double pi char c 2014 -3.14159 'C' 5000 5001 5002 5003 アドレスの例 2015-06-08 C プログラミング入門 総機1 (月1)

printf() で表示するには %p を使う 実験:アドレスの確認 変数のアドレスはアドレス演算子 & で取得 printf() で表示するには %p を使う a at 0x7fff3bee15fc b at 0x7fff3bee15e0 b[0] at 0x7fff3bee15e0 b[1] at 0x7fff3bee15e4 c at 0x7fff3bee15d8 { int a, b[4]; double c; printf("a at %p\n", &a); printf("b at %p\n", &b); printf("b[0] at %p\n", &b[0]); printf("b[1] at %p\n", &b[1]); printf("c at %p\n", &c); ? double c int b[4] ? ? int a このアドレスを変数に保存することで、具体的な値を気にしなくてもよくなる 0x7fff3bee15d8 0x7fff3bee15fc 2015-06-08 C プログラミング入門 総機1 (月1)

ポインタ 2015-06-08 C プログラミング入門 総機1 (月1)

ポインタ変数 (pointer) アドレスを格納するための変数 変数宣言時に * を名前の前につける メモリの位置を指し示すのでポインタという 「何の値を指しているか」を表すために型を持つ 変数宣言時に * を名前の前につける ポインタ変数の表示方法 普通の変数の宣言 ? int a 5000 int* p { int a; int *p; p = &a; 5000 ポインタ変数の宣言 非常に古いプログラムでは、ポインタ変数のサイズ (アドレスのサイズ) とint 型のサイズが同じであることを仮定して書かれてたものがある。しかし、64bit アーキテクチャではまず正しく動作しない。 ポインタ変数 p に変数 a のアドレスを代入 2015-06-08 C プログラミング入門 総機1 (月1)

ポインタ変数の宣言は普通の変数の宣言と混在可能 型と * の関係に注意 ポインタの宣言 ポインタ変数の宣言は普通の変数の宣言と混在可能 型と * の関係に注意 { int a, *p; int *q, b; int* c; int* d, e; int *r, *s; int* と続けて書くと、「int へのポインタ」を表すように見えるので、好まれることもある。 しかし、あくまでも変数名それぞれに * を付けるのが C の文法なので注意 int* 型ではなく、 int 型の変数となる 2015-06-08 C プログラミング入門 総機1 (月1)

ポインタの初期化と代入 ポインタ変数の定義時に初期化が可能 通常の式では、代入演算子 = が使用可能 ? 初期化 { int a int* p 初期化 { int a, *p = &a; int b, *q; q = &b; 初期化をしないポインタ変数はどこを指しているか不明 ? int b int* q 代入演算子による書き換え。ポインタ変数に * は付けない 2015-06-08 C プログラミング入門 総機1 (月1)

ポインタ変数にデリファレンス演算子 * を付けることで、ポインタが指すメモリ領域にアクセスできる ポインタ変数を通したメモリアクセス ポインタ変数にデリファレンス演算子 * を付けることで、ポインタが指すメモリ領域にアクセスできる 間接演算子、参照はがしなどの別名がある ? int a int* p { int a, *p = &a; a = 100; // (1) *p = 120; // (2) printf("%d\n", a); printf("%d\n", *p); の代入で 100 となり、 の代入で 120 となる デリファレンス演算子 デリファレンス演算子 2015-06-08 C プログラミング入門 総機1 (月1)

配列変数名は、配列の先頭アドレスに変換される 配列のアドレス 配列変数名は、配列の先頭アドレスに変換される 0 番要素のアドレス 1 int a[3] int* p int* q { int a[3]; int *p = a; int *q = &a[0]; // p == q が成り立つ // 以下の操作はすべて同じ a[0] = -5; *a = -5; p[0] = -5; *p = -5; ? ? a[0] a[1] a[2] 配列変数名そのまま書いた場合 配列の 0 番要素のアドレス 2015-06-08 C プログラミング入門 総機1 (月1)

アドレスの演算 以下の2つの計算だけが許されている アドレスに整数を加減 アドレス同士の差 「型」のサイズだけアドレスが移動する バイト単位で変化するわけではない アドレス同士の差 「型」のサイズの倍数 説明は省略 { int a[3], *p = a; *p = 1; p++; *p = 3; *(p+1) = 5; int* p 1 int a[3] 3 5 a[0] a[1] a[2] 5000 5004 2015-06-08 C プログラミング入門 総機1 (月1)

配列で使う [] はアドレス演算の一種である 添字演算子 配列で使う [] はアドレス演算の一種である 添字演算子 (subscript operator, indexer) 配列専用の記法ではない これは、配列の宣言なので演算子ではない 一般にアドレス a と整数 n に対して a[n] == *(a+n) が成り立つ { int a[3], *p = a; // 以下はすべて等価 *p = 1; *a = 1; *(p+0) = 1; *(a+0) = 1; p[0] = 1; a[0] = 1; 1 int a[3] ? ? a[0] a[1] a[2] 実は仕様上 0[p] などと書いても同じ意味になる。しかし、この記法が役立つことは多分ない。 配列のアクセスは常に *(a+0) と解釈される 先頭アドレス 2015-06-08 C プログラミング入門 総機1 (月1)

関数から直接変数を操作することはふつうできない 例題:変数の入れ替え 関数から直接変数を操作することはふつうできない int main(void) { int a = 1, b = 5; int temp; // swap a and b // 一時的に別の変数に入れて行う temp = a; a = b; b = temp; // swap(a, b); // v1 と v2 を入れ替える void swap(int v1, int v2) { // この関数には、値のコピーが // 渡されるので、 main 関数の // a, b を書き換えることは // 絶対にできない… } 仮引数名は実引数と関係がないので、 a, b に変えても効果はない この計算を関数化したい 2015-06-08 C プログラミング入門 総機1 (月1)

関数から直接変数を操作することはふつうできない 例題:変数の入れ替え 関数から直接変数を操作することはふつうできない int main(void) { int a = 1, b = 5; int temp; // swap a and b // 一時的に別の変数に入れて行う // temp = a; a = b; b = temp; swap(&a, &b); // v1 と v2 を入れ替える void swap(int *v1, int *v2) { int temp; temp = *v1; *v1 = *v2; *v2 = temp; } 仮引数の型をポインタにして、アドレスのコピーを受け取る ポインタの指す値を操作するので、 * が必要 それぞれのアドレスを渡す 2015-06-08 C プログラミング入門 総機1 (月1)

配列の先頭のアドレスを渡すことで疑似的に可能 配列を渡す 配列そのものを関数に渡す機能はない 配列の先頭のアドレスを渡すことで疑似的に可能 配列のサイズは関数からはわからない サイズなどは個別に情報として渡す ポインタ変数で、アドレスのコピーを受け取る func(a, 3) 1 int a[3] ? ? void func(int *arr, int n) { ... a[0] a[1] a[2] 配列の先頭アドレス (& はいらない) 2015-06-08 C プログラミング入門 総機1 (月1)

例題:配列の総和 配列の先頭アドレスとサイズを渡す int main(void) { int a[] = { 1, 2, 3, 4, 5 }; printf("%d\n", sum(a, 5)); ... // arr から n 個分の総和 int sum(int *arr, int n) { int s = 0, i; for(i = 0; i < n; ++i) s += arr[i]; return s; } 配列の先頭アドレス (& はいらない) n 個の情報が本当にあるかどうかを確かめることはできない 配列サイズを自動的に計算するには、 sizeof(a)/sizeof(int) という式を使う int arr[] と書くこともできる。ただし、配列として認識されるわけではないので、サイズを調べることはできない。 2015-06-08 C プログラミング入門 総機1 (月1)

関数の引数にポインタがある場合、値のコピーではなくそのメモリの場所を直接アクセスしようとしている const ポインタ 関数の引数にポインタがある場合、値のコピーではなくそのメモリの場所を直接アクセスしようとしている const キーワードによって読み込みしかしないことを表せる int main(void) { int a[] = { 1, 2, 3, 4, 5 }; int s; s = sum(a, 5); // もし const がないと、 // この時点で配列 a が書き換え //られているかもしれない… // arr から n 個分の総和 int sum(const int *arr, int n) { int s = 0, i; for(i = 0; i < n; ++i) s += arr[i]; return s; } 読むだけ 2015-06-08 C プログラミング入門 総機1 (月1)

文字列 (1) 2015-06-08 C プログラミング入門 総機1 (月1)

文字列は、文字型 char の列として扱われる 文字列リテラルが式中に書かれると 文字列 (string) 文字列は、文字型 char の列として扱われる 文字列リテラルが式中に書かれると システムによって自動的にメモリに配置され 末尾には null 文字 ('\0') が付き その先頭のアドレスを表す ナル文字と読まれるのが普通 null 文字で終わる { const char *str = "Hello, world!\n"; システムのメモリ領域 (書き換え禁止) 'H' 'e' 'l' 'd' '!' '\n' '\0' char* str 文字列リテラルはシステム領域のアドレスになる システム領域を書き換えることはできないので、 const を付ける方がよい 2015-06-08 C プログラミング入門 総機1 (月1)

文字列の関数での扱い 文字列は以下のどちらかの引数で受け取る char * 文字列を書き換える const char * 文字列は読むだけである const は * の前ならどの位置でも可 printf() のプロトタイプ null 文字で終わる int printf(const char *format, ...); { const char *str = "Hello, world!\n"; printf("Hello, world!\n"); printf(str); printf("%s", str); ... システムのメモリ領域 'H' 'e' 'l' 'd' '!' '\n' '\0' char* str 文字列を表示する指定 2015-06-08 C プログラミング入門 総機1 (月1)

文字配列 配列の要素として文字列を書いたもの 専用の初期化記法を用いる 配列変数の宣言 初期値として文字列リテラルを指定 { char greeting[] = "Hello!"; printf("Greeting: %s\n", greeting); Greeting: Hello! ■ null 文字が自動的に付加される char greeting[7] 'H' 'e' 'l' 'l' 'o' '!' '\0' greeting[6] null 文字を入れて 7 要素の配列として確保される 2015-06-08 C プログラミング入門 総機1 (月1)

文字配列の初期化では末尾に null 文字が自動的に付加される 文字列リテラルの指すアドレスによるポインタ変数の初期化との違いに注意 { char greeting[] = "Hello!"; // 以下の様に書くのと等価 // char greeting[] // = { 'H', 'e', 'l', 'l', 'o', '!', '\0' }; const char *greeting_ptr = "Hello!"; システムのメモリ領域 "Hello!" char greeting[7] "Hello!" char *greeting_ptr システム領域を書き換えることはできないので、 常に const を付ける方がよい 2015-06-08 C プログラミング入門 総機1 (月1)

文字配列変数は配列の一種なので、自由に書き換えることができる 文字配列と文字列へのポインタの違い 文字配列変数は配列の一種なので、自由に書き換えることができる 文字列へのポインタ変数は、指し示す場所が配列変数の領域なのか、システム領域なのかは区別しない システムのメモリ領域 "Hello!" char greeting[7] "Hello!" char *greeting_ptr 2015-06-08 C プログラミング入門 総機1 (月1)

文字列の末尾は常に null 文字があるので、それが出現するまでの文字数をカウントする 例題:文字列の長さを調べる (#1) 文字列の末尾は常に null 文字があるので、それが出現するまでの文字数をカウントする int length(const char *str) { int len = 0; // 文字列の長さ while(str[len] != '\0') ++len; } return len; 2015-06-08 C プログラミング入門 総機1 (月1)

文字列の末尾は常に null 文字があるので、それが出現するまでの文字数をカウントする 例題:文字列の長さを調べる (#2) 文字列の末尾は常に null 文字があるので、それが出現するまでの文字数をカウントする int length(const char *str) { int len = 0; // 文字列の長さ for(len = 0 ; str[len] != '\0'; ++len) // do nothing } return len; 2015-06-08 C プログラミング入門 総機1 (月1)

文字列を扱う標準ライブラリ関数 暗記の必要はない 次回、いくつかは練習する 関数名 操作 strlen 文字列の長さを計算する strcmp, strncmp 文字列が同じかどうか比較する n 付きは、比較の長さを指定 strchr, strrchr 文字列の先頭(末尾)から特定の1文字を検索する strspn, strcspn 文字列から文字群を含む(含まない)最大の長さを調べる strpbrk 文字列から文字群のいずれかを含む最初の位置を探す strstr 文字列から指定した部分文字列の位置を探す strtok 文字列を指定した区切り文字で区切って、それぞれの位置を調べる(トークンという) strcpy, strncpy 文字列を別の領域にコピーする n 付きは、コピーの長さを指定 strcat, strncat 文字列を別の文字列の末尾に連結する n 付きは、連結の長さを指定 <string.h> には多くの文字列操作関数が含まれる 暗記の必要はない 次回、いくつかは練習する 2015-06-08 C プログラミング入門 総機1 (月1)

先ほどの例題は、 strlen() を使うとよい 例題:文字列の長さを調べる (#3) 先ほどの例題は、 strlen() を使うとよい strlen() のプロトタイプ #include <string.h> size_t strlen(const char *s); メモリ上のサイズを十分表せる無符号整数型 渡したアドレスの先を書き換えないことが明示されている 2015-06-08 C プログラミング入門 総機1 (月1)

次回予告 ファイルに文字列を出力する 文字列を数値に変換する 複雑な文字列を作成する 文字列をファイルから読み込む 2015-06-08 C プログラミング入門 総機1 (月1)