c言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/clangi2014/clangi2014...c言語入門 第11週...

33
C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ, 情報処理言語Ⅰ(実習を含む。) 1

Upload: others

Post on 20-May-2020

5 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

C言語入門 第11週

プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

情報処理言語Ⅰ(実習を含む。)

1

Page 2: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

復習

コンパイル時のエラーのパターン

2

Page 3: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

gcc のエラーメッセージ

• 環境変数LANGを設定すると言語が変わる

3

mintty + bash + GNU C $ LANG=C gcc error1.c error1.c: In function 'main': error1.c:7:3: error: expected ';' before 'printf' printf("world¥n"); ^

mintty + bash + GNU C $ LANG=ja_JP.UTF-8 gcc error1.c error1.c: 関数 ‘main’ 内: error1.c:7:3: エラー: expected ‘;’ before ‘printf’ printf("world¥n"); ^

ロケール(locale)と呼ばれる 多言語化の仕組み JM: locale (7)

Page 4: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

mintty の locale の設定

• mintty左上のアイコンからoption→Text

4

ここに Locale: ja_JP Cahaacter set: UTF-8 を設定して「OK」しておく

Page 5: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

文末の「;」忘れ

• 文末に「;」を忘れると次の文字でエラーに

5

error1.c int main() { printf("hello, ") printf("world¥n"); return EXIT_SUCCESS; }

4 5 6 7 8 9 10

mintty + bash + GNU C $ gcc error1.c error1.c: 関数 ‘main’ 内: error1.c:7:3: エラー: expected ‘;’ before ‘printf’ printf("world¥n"); ^

エラーが生じたのは7行目の3文字目だが、エラーの原因は6行目の行末。 C言語では、スペースや改行は人間に読み易くするための位置調整に過ぎないので printf("hello, ") printf("world¥n"); は printf("hello, ")printf("world¥n"); と同じ意味。

ここに「;」があるべきだが 「;」の前に「p」が現れたことが エラーの原因

Page 6: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

ブロック開始終端の不整合

• 「{」が1つ多い

6

error2_1.c int main() { { printf("hello, world¥n"); return EXIT_SUCCESS; }

4 5 6 7 8 9 10

mintty + bash + GNU C $ gcc error2_1.c error2_1.c: 関数 ‘main’ 内: error2_1.c:10:1: エラー: expected declaration or statement at end of input } ^

「}」が1つ足らない状態で、ファイルの終端(入力の終端)に達している。

Page 7: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

ブロック開始終端の不整合

• 「}」が1つ多い

7

error2_2.c int main() { printf("hello, world¥n"); } return EXIT_SUCCESS; }

4 5 6 7 8 9

mintty + bash + GNU C $ gcc error2_2.c error2_2.c:8:3: エラー: expected identifier or ‘(’ before ‘return’ return EXIT_SUCCESS; ^ error2_2.c:9:1: エラー: expected identifier or ‘(’ before ‘}’ token } ^

エラーが生じたのは8行目の3文字目だが、エラーの原因は7行目。 「}」が1つ多いので、main関数の定義が7行目で終わっている。 つまり、8~9行目は、何もないところにいきなり以下のように書いたのと同じ。

return EXIT_SUCCESS; }

8 9

Page 8: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

ブロック開始終端の不整合

• 関数名のミススペル

8

error3.c int main() { print("hello, world¥n"); return EXIT_SUCCESS; }

4 5 6 7 8 9

mintty + bash + GNU C $ gcc error3.c /tmp/ccopiKHp.o:error3.c:(.text+0x15): `print' に対する定義されていない参照です /tmp/ccopiKHp.o:error3.c:(.text+0x15): 再配置がオーバーフローしないように切り詰められました: R_X86_64_PC32 (未定義シンボル `print' に対して) /usr/lib/gcc/x86_64-pc-cygwin/4.8.2/../../../../x86_64-pc-cygwin/bin/ld: /tmp/ccopiKHp.o: 誤った再配置アドレス 0x0 がセクション `.pdata' 内にあります /usr/lib/gcc/x86_64-pc-cygwin/4.8.2/../../../../x86_64-pc-cygwin/bin/ld: 最終リンクに失敗しました: 無効な操作です collect2: エラー: ld はステータス 1 で終了しました

コンパイルではなくリンクに失敗したと言われている。 分割コンパイルによる、外部関数のリンクの際、print が見つからなかった。 printf のつもりが print とミススペルしている。 標準ライブラリに print 関数は用意されていない。

Page 9: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

ポインタ

9

Page 10: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

call by pointer (ポインタ渡し)

• 呼び出し元の変数の内容を変更したい場合

10

swapi.c void swapi(int *a, int *b) { int c; c = *a; *a = *b; *b = c; }

講義資料 第4週pp.42-44., 第9週p.6., 教科書p.171.

void swapi(int *a, int *b); 関数 *a と *b の値を入れ替える

swapi_test.c int a, b; fprintf(stderr, "a = ? "); scanf("%d", &a); fprintf(stderr, "b = ? "); scanf("%d", &b); swapi(&a, &b); printf("a = %d¥n", a); printf("b = %d¥n", b);

訂正2014-07-11 誤:swap 正:swapi

Page 11: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

call by pointer (ポインタ渡し)

• 2つ以上の値を返したい場合

• 戻り値は1つしかないので、関数の引数にポインタを渡して、値を返す

11

modf.c double modf(double x, double *iptr) { *iptr = x < 0 ? ceil(x) : floor(x); return x < 0 ? *iptr - x : x - *iptr; }

講義資料 第4週pp.42-44., 第9週p.6., 教科書p.171.

double modf(double x, double *iptr); 関数 戻り値: x の小数部を戻り値に x の整数部を *iptr に返す 戻り値は共に x と同じ符号を持つ

JM: modf (3)

Page 12: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

call by pointer (ポインタ渡し)

• 任意の長さの配列を渡したい場合

• 例えば文字列等

12

strlen.c size_t strlen(const char *s) { size_t len = 0; while(s[len] != '¥0') { len++; } return len; }

講義資料 第4週pp.42-44., 第9週p.6., 教科書p.171.

size_t strlen(const char *s); 関数 '¥0'で終端された文字列の長さを返す

strlen_test.c char s[1024] = ""; fprintf(stderr, "s = ? "); scanf("%1023[^¥n]", &s); printf("strlen(s) = %d¥n", strlen(s));

値が書き変えられては困る場合 const char への * (ポインタ) にしておくと、この関数を使っても 与えた内容が変更されないことを ある程度保証することが出来る

Page 13: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

const 修飾子

• const 型: 変数の値を変更出来なくなる

13

const_test1.c const int i = 0; // 初期化は出来る int const j = 0; // const int も int const も同じ意味 i = 1; // const を付けた変数には代入出来ない j = 1; // const を付けた変数には代入出来ない

6 7 8 9

mintty + bash + GNU C $ gcc const_test1.c const_test1.c: 関数 ‘main’ 内: const_test1.c:8:3: エラー: 読み取り専用変数 ‘i’ への代入です i = 1; // Error: i is const ^ const_test1.c:9:3: エラー: 読み取り専用変数 ‘j’ への代入です j = 1; // Error: j is const ^

const 修飾子は型修飾子(type-qualifier)の一種 型修飾子は型名の前後どちらにつけても良い

変更しようとするとコンパイル時に エラーになるので 本来書き変えてはいけない値を 書き変えてしまうことで生じるバグを 未然に防げる

教科書p.308,310,314., [1] pp.240,257,261-262.

Page 14: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

const 修飾子

• const char 型へのポインタ

14

const_test2.c char s[] = "hello, world"; const char *p; // ポインタの場合 *p が const になる p = s; // ポインタへは代入出来る *p = 'H'; // ポインタ指し示す先の変数には代入出来ない p[7] = 'W'; // ポインタ指し示す先の変数には代入出来ない *(char *) p = 'H'; // const のない型へ cast してやると代入出来る

6 7 8 9 10 11

mintty + bash + GNU C $ gcc const_test2.c const_test2.c: 関数 ‘main’ 内: const_test2.c:9:3: エラー: 読み取り専用位置 ‘*p’ への代入です *p = 'H'; // Errpr: *p is const ^ const_test2.c:10:3: エラー: 読み取り専用位置 ‘*(p + 7u)’ への代入です p[7] = 'W'; // Errpr: p[x] is const ^

ただし、わざわざ const を付けているということは 変更してはいけない、 または変更しないことを前提としているのであるから 普通は、特別に理由がない限り無理矢理書き変えてはいけない

教科書p.308,310,314., [1] pp.240,257,261-262.

char const * 型 const char * 型

Page 15: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

const 修飾子

• char 型への const ポインタ

15

const_test3.c char s[] = "hello, world"; char *const p = s; // p が const で *p が char になる p = s; // p が const なのでポインタへは代入出来ない *p = 'H'; // ポインタ指し示す先の変数には代入出来る p[7] = 'W'; // ポインタ指し示す先の変数には代入出来る

6 7 8 9 10

mintty + bash + GNU C $ gcc const_test3.c const_test3.c: 関数 ‘main’ 内: const_test3.c:8:3: エラー: 読み取り専用変数 ‘p’ への代入です p = s; // Error: p is const ^

const char *p と char const *p は同じだが char * const p は意味が異なる 前者は *p が const char つまり p は変数で *p が定数 後者は *const p が char つまり p は定数で *p は変数 である

* がどこに係っているか よく考えるましょう

教科書p.308,310,314., [1] pp.240,257,261-262.

char * const 型

Page 16: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

文字列操作とポインタ操作

標準ライブラリ関数を例にした実例

16

Page 17: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

ポインタを用いた文字列操作の例

• strlen 関数の大まかな仕組み

17

strlen_with_idx.c size_t strlen(const char *s) { size_t len = 0; while (s[len] != '¥0') len++; return len; }

文字列の長さは 先頭から終端文字('¥0')の手前までの 文字数

strlen_with_ptr1.c size_t strlen(const char *s) { const char *s0 = s; while (*s != '¥0') s++; return s - s0; }

strlen_with_ptr2.c size_t strlen(const char *s) { const char *s0 = s; while (*(s++) != '¥0') ; return s - s0 - 1; }

Page 18: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

ポインタを用いた文字列のコピーの例

• strcpy 関数の大まかな仕組み

18

strcpy_with_idx.c char *strcpy(char *dst, const char *src) { int i; for (i = 0; (dst[i] = src[i]) != '¥0'; i++) ; return dst; }

strcpy_with_ptr.c char *strcpy(char *dst, const char *src) { char *dst0 = dst; while ((*(dst++) = *(src++)) != '¥0') ; return dst0; }

文字列のコピーは 先頭から終端文字('¥0')までを コピーすれば良い

訂正2014-07-04 誤:'0' 正:'¥0'

Page 19: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

ポインタを用いた文字列のコピーの例

• strncpy 関数の大まかな仕組み

19

strncpy_with_idx.c char *strncpy(char *dst, const char *src, size_t n) { size_t i = 0; for (; i < n && (dst[i] = src[i]) != '¥0'; i++) ; for (; i < n; i++) dst[i] = '¥0'; return dst; }

strncpy_with_ptr.c char *strncpy(char *dst, const char *src, size_t n) { char *dst0 = dst; while(0 < n-- && (*(dst++) = *(src++)) != '¥0') ; while(0 < n--) *(dst++) = '¥0'; return dst0; }

strncpy は strcpy に加えて 終端文字('¥0')以降を'¥0'で埋める

論理演算は左から右に評価され、 真偽値が確定すると評価を終了する。 つまり i < n や dst < dst0 + n が偽なら、 そこで真偽値が確定するので それより右にある (dst[i] = src[i]) != '¥0' や (*(dst++) = *(src++)) != '¥0' は 実行されない。

Page 20: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

ポインタを用いた文字列の比較の例

• strcmp 関数の大まかな仕組み

20

strcmp_with_idx.c int strcmp(const char *s1, const char *s2) { size_t i; for (i = 0; s1[i] != '¥0' && s2[i] != '¥0' && s1[i] == s2[i]; i++) ; return s1[i] - s2[i]; }

strcmp_with_ptr.c int strcmp(const char *s1, const char *s2) { while (*s1 != '¥0' && *s2 != '¥0' && *s1 == *s2) { s1++; s2++; } return *s1 - *s2; }

どちらかが終端文字('¥0')になるか 異なる値が出てくるまで比較し 終了位置を比較すれば良い

Page 21: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

ポインタを用いた文字列の比較の例

• strncmp 関数の大まかな仕組み

21

strncmp_with_idx.c int strncmp(const char *s1, const char *s2, size_t n) { size_t i; if (n <= 0) return 0; for (i = 0; i < n - 1 && s1[i] != '¥0' && s2[i] != '¥0' && s1[i] == s2[i]; i++) ; return s1[i] - s2[i]; }

strncmp_with_ptr.c int strncmp(const char *s1, const char *s2, size_t n) { if (n <= 0) return 0; while (0 < --n && *s1 != '¥0' && *s2 != '¥0' && *s1 == *s2) { s1++; s2++; } return *s1 - *s2; }

strncmp は strcmp の 比較文字数を最大n文字に限定する

Page 22: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

ポインタを用いた文字列の連結の例

• strcat 関数の大まかな仕組み

22

strcat_with_idx.c char *strcat(char *dst, const char *src) { int i, len = strlen(dst); for (i = 0; (dst[len + i] = src[i]) != '¥0'; i++) ; return dst; }

strcat_with_ptr.c char *strcat(char *dst, const char *src) { char *dst0 = dst; dst += strlen(dst); while ((*(dst++) = *(src++)) != '¥0') ; return dst0; }

dst の終端位置に src をコピーする

Page 23: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

ポインタを用いた文字列の連結の例

• strncat 関数の大まかな仕組み

23

strncat_with_idx.c char *strncat(char *dst, const char *src, size_t n) { int i, len = strlen(dst); for (i = 0; i < n && src[i] != '¥0'; i++) dst[len + i] = src[i]; dst[len + i] = '¥0'; return dst; }

strncat_with_ptr.c char *strncat(char *dst, const char *src, size_t n) { char *dst0 = dst; dst += strlen(dst); while (0 < n-- && *src != '¥0') *(dst++) = *(src++); *dst = '¥0'; return dst0; }

strncat は strcat の 連結文字を最大n文字に限定する ただしsrcがn文字以上の場合 終端文字が+1文字され 合計n+1バイト追記される

Page 24: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

複雑なポインタの宣言

• *を付けると何になるか?

24

pointertest5.c int *(p1[7]); // int *p1[7]; と同義 int (*p2)[7]; printf("sizeof( p1)=%2d¥n", sizeof( p1)); printf("sizeof( p2)=%2d¥n", sizeof( p2)); printf("sizeof(*p1)=%2d¥n", sizeof(*p1)); printf("sizeof(*p2)=%2d¥n", sizeof(*p2));

教科書.pp.235-239., [1]pp.148-153.

mintty + bash + GNU C $ gcc pointertest5.c && ./a sizeof( p1)=56 sizeof( p2)= 8 sizeof(*p1)= 8 sizeof(*p2)=28

[] は * よりも優先順位の高い演算子 p1[x]に*が付く、つまり*p1[x]がint型になる 従ってp1[x]はint*型、つまりp1は要素数7のint*型配列 p2に*が付く、つまり*p2が要素数7のint型配列になる 従ってp2は「「要素数7のint型配列」へのポインタ」

宣言の読み方 int *(p1[7]); → *(p1[x])がint int (*p2)[7]; → *p2がint[7]

要注意

Page 25: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

ポインタ配列の初期化

• char * の配列の初期化

25

char *s[] = {"one", "two", "three"};

教科書.pp.235-239., [1]pp.148-153.

0x~00 o

0x~01 n

0x~02 e

0x~03 ¥0

0x~04 t

0x~05 w

0x~06 o

0x~07 ¥0

0x~08 t

0x~09 h

0x~0a r

0x~0b e

0x~0c e

0x~0d ¥0

0x~00 00

0x~01 ~

0x~02 ~

0x~03 ~

0x~04 04

0x~05 ~

0x~06 ~

0x~07 ~

0x~08 08

0x~09 ~

0x~0a ~

0x~0b ~

s[0]

s[1]

s[2]

s[0] は "one" s[1] は "two"

s[0][0] は 'o' s[0][1] は 'n' s[0][2] は 'e' s[0][3] は '¥0'

s は char* 型で要素数3の配列 s[x] は char* 型 *s[x] は char 型

メモリ上のどこかに配置された 文字列

Page 26: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

配列とポインタの初期値と文字列

• 配列とポインタで扱いが異なることに注意

26

pointertest6.c void sub() { char s[] = "hello"; char *p = "world"; ...

教科書.pp.235-239., [1]pp.148-153.

objdump の結果 セクション .rdata の内容: ... 403060 776f726c 64007320 3d202225 73220a00 world.s = "%s".. ... void sub(void) { ... char s[] = "hello"; 401196: c7 45 ee 68 65 6c 6c movl $0x6c6c6568,-0x12(%ebp) 40119d: 66 c7 45 f2 6f 00 movw $0x6f,-0xe(%ebp) char *p = "world"; 4011a3: c7 45 f4 60 30 40 00 movl $0x403060,-0xc(%ebp) ...

sへは"hello"の文字コード 68,65,6c,6c,6fが代入されているが pへは.rdataセクションに予め 用意してある文字列"world"の アドレス403060が代入されている

.rdataセクションに用意されたデータは 書き変えてはいけない

一般にポインタに初期値として与えた 文字列定数は書き変えてはいけない

Page 27: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

配列とポインタの初期値と文字列

• 配列とポインタで扱いが異なることに注意

27

pointertest6.c void sub(void) { char s[] = "hello"; char *p = "world"; printf("s = ¥"%s¥"¥n", s); printf("p = ¥"%s¥"¥n", p); s[0] = 'H'; p[0] = 'W'; } int main() { sub(); sub(); return EXIT_SUCCESS; }

教科書.pp.235-239., [1]pp.148-153.

Cygwin + GNU C $ gcc pointertest6.c && ./a s = "hello" p = "world" Segmentation fault (コアダンプ)

Borland C++ >bcc32 pointertest6.c && pointertest6 Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland pointertest6.c: Turbo Incremental Link 5.00 Copyright (c) 1997, 2000 Borland s = "hello" p = "world" s = "hello" p = "World"

sub()が実行された際、 sは毎回"hello"だが pは2回目以降"World"になってしまう もしくは.rdataへの不正な書き込みで 異常終了してしまう

Page 28: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

演習: print_str_with_ptr.c

• char型へのポインタ変数pを用いて、char型配列sに保存された文字列を表示せよ

• 文字列末尾で改行すること

• 用いて良い変数はpのみとする

• 表示にはputchar関数を使用すること。printf関数は使用しないこと

• 用いて良いループ構造はwhile文のみとする

• print_str_with_ptr_tmp.c を元に指定箇所に作成せよ。

28

mintty + bash + GNU C $ gcc print_str_with_ptr.c && ./a s = ? hello, world hello, world JM: putchar (3)

Page 29: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

演習: print_str.c

• 文字列を表示する関数print_str(s)を実装せよ • 与えられた文字列末尾で改行すること • 関数のプロトタイプ宣言はmyfunc_week11.hに作成

せよ • print_str_test.c と共にコンパイルして動作を確

認すること • 引数

• const char *s : 文字列へのポインタ

• 戻り値 • なし(void)

29

mintty + bash + GNU C $ gcc print_str_test.c print_str.c && ./a s = ? hello, world hello, world

ヒント: print_str_with_ptr.c から 切り出して関数化すれば良い

Page 30: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

演習: base36toint.c

• 36進数で用いられる「0~9,A~Z,a~z」までの文字をint型の数値0~35に変換する関数base36toint(c)を実装せよ

• 関数のプロトタイプ宣言はmyfunc_week11.hに作成せよ • エラーの際、DEBUGマクロが定義されていたら、標準エラー出力に

警告メッセージを表示せよ • base36toint_test.cと共にコンパイルして動作を確認する事 • 引数

• int c : 「0~9,A~Z,a~z」までの文字コード (0x30~0x39,0x41~0x5a,0x61~0x7a)

• 戻り値 • cで与えられた文字コードに対応する数値0~35をint型で返す • エラーの場合は-1を返す

30

mintty + bash + GNU C $ gcc base36toint_test.c base36toint.c && ./a c = ? z 35 第13週へ移動+改訂

Page 31: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

演習: basetoint.c

• 「0~9,A~Z,a~z」の文字をN進数表現の1桁としてint型の数値に変換する関数basetoint(c,base)を実装せよ

• 関数のプロトタイプ宣言はmyfunc_week11.hに作成せよ • エラーの際、DEBUGマクロが定義されていたら、標準エラー出力に警告

メッセージを表示せよ • basetoint_test.cと共にコンパイルして動作を確認する事 • 引数

• int c : N進数表現の1桁を表す文字「0~9,A~Z,a~z」の文字コード(0x30~0x39,0x41~0x5a,0x61~0x7a)

• int base : N進数表現の基数(つまりbase進数)、最大36

• 戻り値 • cで与えられた文字コードに対応する数値0~最大35をint型で返す • エラーの場合は-1を返す

31

mintty + bash + GNU C $ gcc basetoint_test.c basetoint.c && ./a c = ? z base = ? 10 -1 第13週へ移動+改訂

Page 32: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

ポインタへのポインタ

• 関数の引数でポインタを返したい場合はポインタ変数へのポインタを用いる

32

strtoui.c unsigned int strtoui(const char *s, char **endp, int base) { int v; unsigned int r = 0; while (0 <= (v = basetoint(*(s++), base))) { r = r * base + v; } if (endp != NULL) *endp = (char *) s; return r; }

教科書 pp.243-250.

main.c char s[] = "ffz"; char *endp; printf("strtoui(s, &endp, 16);

この例だと endp に "z" へのポインタ つまり&s[2]が返ってくる

Page 33: C言語入門web.cc.yamaguchi-u.ac.jp/~okadalab/CLangI2014/CLangI2014...C言語入門 第11週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ,

参考文献

• [1] B.W.カーニハン/D.M.リッチー著 石田晴久 訳、プログラミング言語C 第2版 ANSI 規格準拠、共立出版(1989)

33