プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ, 情報処理言語Ⅰ(実習を含む。) C言語入門 第13週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ, 情報処理言語Ⅰ(実習を含む。)
関数定義の引数と配列
配列のサイズ sizeof で得られるバイト数 int a[N]; 講義資料 第3週p.53. : 0x?? a = &a[ -1] sizeof(a[0]) = sizeof(int) 0x?? a + 1 = &a[ 1] 0x?? a + 2 = &a[ 2] sizeof(a) = sizeof(int) * N 0x?? : arraysizetest.c 0x?? : int a[10]; // 要素数10のint型の配列変数 printf("%d\n", sizeof(int)); //int型の割り当てバイト数 printf("%d\n", sizeof(a)); //配列変数aの割り当てバイト数 printf("%d\n", sizeof(a[0])); //変数a[0]の割り当てバイト数 printf("%d\n", sizeof(a)/sizeof(a[0])); //配列変数aの要素数 0x?? a+N-1 = &a[N-1] 0x?? a+N = &a[N ] 0x?? a+N+1 = &a[N+1] mintty + bash + GNU C : $ gcc -g arraysizetest.c && ./a 4 40 10 オレンジ色は 未割当のメモリ sizeof(int)
関数定義の引数と配列 引数名直後の1次元はポインタ扱いになる [1] pp.121-122. pointertest7.c mintty + bash + GNU C 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 int main() { // 0 1 2 3 4 5 6 7 8 9 int a[10] = { 2, 3, 5, 7,11,13,17,19,23,29}; int b[2][5] = { 2, 3, 5, 7,11,13,17,19,23,29}; printf("sizeof(a) = %d\n", sizeof(a)); func_with_array1d(a); func_with_pointer(a); func_with_array2d(b); func_with_pointer(b); //a += 1; // It's impossible. Because a is an array. //b += 1; // It's impossible. Because b is an array. return EXIT_SUCCESS; } $ gcc pointertest7.c && ./a pointertest7.c: 関数 ‘main’ 内: pointertest7.c:36:3: 警告: 互換性のないポインタ型から 1 番目の ‘func_with_pointer’ の引数に渡しています [デフォルトで有効] func_with_pointer(b); ^ pointertest7.c:20:6: 備考: expected ‘int *’ but argument is of type ‘int (*)[5]’ void func_with_pointer(int *p) sizeof(a) = 40 sizeof(x) = 8 sizeof(x[0]) = 4 sizeof(p) = 8 sizeof(y) = 8 sizeof(y[0]) = 20 sizeof(y[0][0]) = 4 pointertest7.c 関数引数に与えた int b[2][5] が int (*)[5] 扱い されていることが分かる 20 21 22 23 void func_with_pointer(int *p) { printf("sizeof(p) = %d\n", sizeof(p)); }
関数定義の引数と配列 ポインタ扱いなので配列サイズは取れない [1] pp.121-122. pointertest7.c mintty + bash + GNU C 4 5 6 7 8 9 void func_with_array1d(int x[10]) { printf("sizeof(x) = %d\n", sizeof(x)); printf("sizeof(x[0]) = %d\n", sizeof(x[0])); x += 1; // x はポインタなので変更出来る } $ gcc pointertest7.c && ./a pointertest7.c: 関数 ‘main’ 内: pointertest7.c:36:3: 警告: 互換性のないポインタ型から 1 番目の ‘func_with_pointer’ の引数に渡しています [デフォルトで有効] func_with_pointer(b); ^ pointertest7.c:20:6: 備考: expected ‘int *’ but argument is of type ‘int (*)[5]’ void func_with_pointer(int *p) sizeof(a) = 40 sizeof(x) = 8 sizeof(x[0]) = 4 sizeof(p) = 8 sizeof(y) = 8 sizeof(y[0]) = 20 sizeof(y[0][0]) = 4 sizeof(int[10]) ではなく sizeof(int*) pointertest7.c 11 12 13 14 15 16 17 18 void func_with_array2d(int y[2][5]) { printf("sizeof(y) = %d\n", sizeof(y)); printf("sizeof(y[0]) = %d\n", sizeof(y[0])); printf("sizeof(y[0][0]) = %d\n", sizeof(y[0][0])); y += 1; // y はポインタなので変更出来る //y[0] += 1; // y[0] は配列なので変更来ない } sizeof(int[2][5]) ではなく sizeof(int(*)[5]) sizeof(y[0])は sizeof(int[5])になっている
関数定義の引数と配列 ポインタ扱いなのでアドレスが変更出来る [1] pp.121-122. pointertest7.c mintty + bash + GNU C 4 5 6 7 8 9 void func_with_array1d(int x[10]) { printf("sizeof(x) = %d\n", sizeof(x)); printf("sizeof(x[0]) = %d\n", sizeof(x[0])); x += 1; // x はポインタなので変更出来る } $ gcc pointertest7.c && ./a pointertest7.c: 関数 ‘main’ 内: pointertest7.c:36:3: 警告: 互換性のないポインタ型から 1 番目の ‘func_with_pointer’ の引数に渡しています [デフォルトで有効] func_with_pointer(b); ^ pointertest7.c:20:6: 備考: expected ‘int *’ but argument is of type ‘int (*)[5]’ void func_with_pointer(int *p) sizeof(a) = 40 sizeof(x) = 8 sizeof(x[0]) = 4 sizeof(p) = 8 sizeof(y) = 8 sizeof(y[0]) = 20 sizeof(y[0][0]) = 4 pointertest7.c 11 12 13 14 15 16 17 18 void func_with_array2d(int y[2][5]) { printf("sizeof(y) = %d\n", sizeof(y)); printf("sizeof(y[0]) = %d\n", sizeof(y[0])); printf("sizeof(y[0][0]) = %d\n", sizeof(y[0][0])); y += 1; // y はポインタなので変更出来る //y[0] += 1; // y[0] は配列なので変更来ない } 最終次元はポインタだが、 y[0] は int[5] 配列だから アドレスの変更が出来ない アンコメントして コンパイル出来ない事を 確認しましょう 定義は配列に見えるが実はポインタなので アドレスが変更出来る
確認: 関数定義の引数と配列 以下の個所を変えてコンパイルし確認せよ [1] pp.121-122. pointertest7.c int x[10] ではなく int x[20] や int x[] にしても 問題なくコンパイル出来る事を確認せよ 4 5 6 7 8 9 void func_with_array1d(int x[10]) { printf("sizeof(x) = %d\n", sizeof(x)); printf("sizeof(x[0]) = %d\n", sizeof(x[0])); x += 1; // It's possible. Because x is pointer. } pointertest7.c int y[2][5] ではなく int y[20][5], int y[][5]にしても 問題なくコンパイル出来る事を確認せよ int y[2][10] だと コンパイル出来ないことを確認せよ 11 12 13 14 15 16 17 18 void func_with_array2d(int y[2][5]) { printf("sizeof(y) = %d\n", sizeof(y)); printf("sizeof(y[0]) = %d\n", sizeof(y[0])); printf("sizeof(y[0][0]) = %d\n", sizeof(y[0][0])); y += 1; // It's possible. Because y is a pointer to an array. //y[0] += 1; // It's impossible. Because y is an array. } 変数名に一番近い次元はポインタ扱いされるので 要素数が意味を持たなくなっていることを確認せよ
関数定義の引数と配列 引数名直後の1次元は要素数を省略すべき [1] pp.121-122. pointertest7.c int x[10] という 引数の宣言は int *x と同じ意味 4 5 6 7 8 9 void func_with_array1d(int x[10]) { printf("sizeof(x) = %d\n", sizeof(x)); printf("sizeof(x[0]) = %d\n", sizeof(x[0])); x += 1; // It's possible. Because x is pointer. } pointertest7.c int y[2][5] という 引数の宣言は int (*y)[5] と同じ意味 11 12 13 14 15 16 17 18 void func_with_array2d(int y[2][5]) { printf("sizeof(y) = %d\n", sizeof(y)); printf("sizeof(y[0]) = %d\n", sizeof(y[0])); printf("sizeof(y[0][0]) = %d\n", sizeof(y[0][0])); y += 1; // It's possible. Because y is a pointer to an array. //y[0] += 1; // It's impossible. Because y is an array. } 無意味な数値は なるべく 書くべきでない int *x int (*y)[5] と書くのが最も正確 int x[10] より int x[] int y[2][5] より int y[][5] と書く方が実態に合っている 簡易表記?
関数定義の引数と配列 関数定義の仮引数では以下の定義は同義 [1] pp.121-122. 1次元配列 2次元配列 3次元配列 ... = int sub(char s[N]) { // ... } int sub(char s[M][N]) { // ... } int sub(char s[L][M][N]) { // ... } ... = = = int sub(char s[]) { // ... } int sub(char s[][N]) { // ... } int sub(char s[][M][N]) { // ... } ... = = = int sub(char *s) { // ... } int sub(char (*s)[N]) { // ... } int sub(char (*s)[M][N]) { // ... } ...
関数定義の引数と配列 関数定義の仮引数では以下の定義は同義 [1] pp.121-122. 関数の引数では 配列の最初の次元は 無視されてポインタ扱いになる 関数定義の仮引数では以下の定義は同義 1次元配列 2次元配列 3次元配列 int sub(char s[N]) { // ... } int sub(char s[M][N]) { // ... } int sub(char s[L][M][N]) { // ... } ... = = = int sub(char s[]) { // ... } int sub(char s[][N]) { // ... } int sub(char s[][M][N]) { // ... } ... = = = int sub(char *s) { // ... } int sub(char (*s)[N]) { // ... } int sub(char (*s)[M][N]) { // ... } ...
並べ替え(sort)
演習: miniv.c 要素数n個のint型のデータから最小値を検索する関数 miniv を作成せよ 教科書pp.180-183 演習: miniv.c 要素数n個のint型のデータから最小値を検索する関数 miniv を作成せよ ただし最小値はn個のデータのうちi番目以降から検索するものとする miniv_test.c と共にコンパイルして動作を確認せよ 引数は以下の順とする int *v: 最小値を検索するデータへのポインタ int n: 最小値を検索するデータ(v[])の要素数 int i: 最小値を検索し始める要素番号(v[i]以降のデータから最小値を検索する) 戻り値: 見つけた最小値の要素番号をint型で返す 要素番号とはv[i]に対するiの数値 訂正2014-07-11 「int型で」を追記
演習: miniv.c ヒント 教科書 pp.85-108. n 個 v[0] ? v[1] ? v[2] ? v[3] ? v[n-1] ... i 番目から n-1 番目までの中から探せばよい ループの基本 無限ループ for (i = 0; i < n; i++) { // i増やしながら0~n-1の範囲でループさせる } for (;;) { // for文()内の式は省略可能 }
演習: selection_sorti.c 要素数n個のint型のデータを選択ソートにより小さい順に並べ替える関数 sorti を作成せよ 教科書pp.184-187 演習: selection_sorti.c 要素数n個のint型のデータを選択ソートにより小さい順に並べ替える関数 sorti を作成せよ sorti_test.c と共にコンパイルして動作を確認せよ 引数は以下の順とする int *v: ソートするデータへのポインタ int n: ソートするデータ(v[])の要素数 戻り値: なし ヒント: 最小値の検索に miniv.c を、値の交換に swapi.c を用いるとループ内は少なくとも2行あれば書ける
選択ソート 残りのデータから最小値を検索し、 残りのデータの先頭と最小値を交換 残りのデータがなくなるまで1.から繰り返す 教科書pp.184-187 選択ソート 残りのデータから最小値を検索し、 残りのデータの先頭と最小値を交換 残りのデータがなくなるまで1.から繰り返す 残りデータ v[0] ? v[1] ? v[2] ? v[3] ? v[n-1] ? ... 値の交換 残りデータの先頭 残りデータの最小値
選択ソート 残りのデータから最小値を検索し、 残りのデータの先頭と最小値を交換 残りのデータがなくなるまで1.から繰り返す 教科書pp.184-187 選択ソート 残りのデータから最小値を検索し、 残りのデータの先頭と最小値を交換 残りのデータがなくなるまで1.から繰り返す ソート済みデータ 残りデータ v[0] ? v[1] ? v[2] ? v[3] ? v[n-1] ? ... 値の交換 残りデータの先頭 残りデータの最小値
代表的なソートのアルゴリズム 選択ソート、ヒープソート マージソート バブルソート、クイックソート バケットソート etc,,,
標準ライブラリの qsort 関数 要素サイズsize,要素数nのデータbaseを比較関数cmpの結果に従い並べ替える 引数 戻り値 [1] pp.144-148, 319. 標準ライブラリの qsort 関数 void qsort(void *base, size_t n, size_t size, int (*cmp)(const void *, const void *)); 要素サイズsize,要素数nのデータbaseを比較関数cmpの結果に従い並べ替える 引数 base: データへのポインタ n: データの要素数 size: 1要素当りのバイト数 cmp: 比較に用いる関数へのポインタ 戻り値 なし void * 型は 任意の方へのポインタ 並べ替えの順序 昇順、降順 データ型 int, double, 文字列 関数へのポインタ 並べ替えの際、 比較を可換にすることで 汎用性を持たせている。
qsort 利用の例 比較関数を用意すれば任意データに使える qsort_test1.c int 型のデータの並べ替えの例 28 29 30 31 int compi(const int *a, const int *b) { return *a - *b; } int 型のデータの比較関数 qsort_test1.c 47 48 49 printiv(v, n); qsort(v, n, sizeof(int), (int (*)(const void *, const void *)) compi); mintty + bash + GNU C const void * を 引数とする関数として キャストする必要がある $ gcc qsort_test1.c && ./a n = ? 10 v[] = 197 22 155 489 71 47 137 364 486 70 v[] = 22 47 70 71 137 155 197 364 486 489
qsort 利用の例 比較関数を用意すれば任意データに使える 配列へのポインタ並べ替えの例 qsort_test2.c 標準ライブラリ関数 47 48 49 50 int strcmp_wrapper(const char **a, const char **b) { return strcmp(*a, *b); } 標準ライブラリ関数 strcmp のラッパー関数 qsort_test2.c 67 68 69 printsv(v, n); qsort(v, n, sizeof(char*), (int (*)(const void*,const void*))strcmp_wrapper); mintty + bash + GNU C const void * を 引数とする関数として キャストする必要がある $ gcc qsort_test2.c && ./a n = ? 2 v[0] = "yocchzcmwhmufrdvde" v[1] = "iipfziuhu" v[0] = "iipfziuhu" v[1] = "yocchzcmwhmufrdvde"
関数へのポインタ 作り方 例: 関数のプロトタイプ宣言を書き写す 関数名を変数名に書き変える 変数名を ( ) で囲む 変数名の前に * を付ける 例: 格納したい関数のプロトタイプ宣言 int compi(const int *a, const int *b); 関数へのポインタの宣言 int (*fnc)(const int *, const int *); 関数へのポインタによる関数の呼び出し (*fnc)(&a, &b); 引数名は省略可能 fnc という変数を宣言 戻り値が int int *型の引数を2つ取る 関数のアドレスを 格納出来る fnc という変数に 格納されたアドレスにある 関数に引数を渡して実行
関数へのポインタ 関数へのポインタの例 pointertest8.c mintty + bash + GNU C 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int compi(const int *a, const int *b) { return *a - *b; } int main() int a, b; int (*fnc)(const int *a, const int *b) = compi; fprintf(stderr, "a = ? "); scanf("%d", &a); fprintf(stderr, "b = ? "); scanf("%d", &b); printf("(*fnc)(&a, &b) = %d\n", (*fnc)(&a, &b)); return EXIT_SUCCESS; $ gcc pointertest8.c && ./a a = ? 10 b = ? 20 (*fnc)(&a, &b) = -10
データ構造
スタック構造 積み木式のデータ構造 push(一番上に格納) pop(一番上から取り出し) LIFO(Last-In-First-Out) 教科書pp.177-179. スタック構造 積み木式のデータ構造 LIFO(Last-In-First-Out) FILO(First-In-Last-Out) 後入れ先出し 先入れ後出し push(一番上に格納) *p に格納 p--; pop(一番上から取り出し) p++; *p から取出し 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 push 6 6 6 pop 7 7 7 9 8 p 8 8 9 p 9 9 p 9 10 10 10 11 11 11 サブルーチン呼び出し時の ローカル変数作成等で利用されている Wikipedia / スタック
スタック構造 積み木式のデータ構造 push(一番上に格納) pop(一番上から取り出し) LIFO(Last-In-First-Out) 教科書pp.177-179. スタック構造 積み木式のデータ構造 LIFO(Last-In-First-Out) FILO(First-In-Last-Out) 後入れ先出し 先入れ後出し push(一番上に格納) *p に格納 p++; pop(一番上から取り出し) p--; *p から取出し p 1 1 p 1 2 p 2 2 2 3 3 3 2 4 4 4 push 5 5 5 pop 6 6 6 7 7 7 8 8 8 9 9 9 10 10 10 11 11 11 サブルーチン呼び出し時の ローカル変数作成等で利用されている Wikipedia / スタック
キュー構造 待ち行列構造 enqueue(先頭に格納) dequeue(末尾から取り出し) FIFO(First-In-First-Out) 先入れ先出し enqueue(先頭に格納) p2++ *p2 に格納 dequeue(末尾から取り出し) *p1 から取出し p1++; リングバッファ キューの先頭と末尾を繋げ、 輪のようにして使う 1 1 dequeue 1 2 2 2 p1 3 p1 3 3 3 4 4 p1 4 5 5 5 6 6 6 7 7 7 p2 8 8 8 9 9 p2 9 p2 9 10 10 10 enqueue 11 11 11 Wikipedia / キュー
キュー構造 リングバッファ enqueue 時に はみ出したら 反対側に繋げる enqueue dequeue 1 2 11 1 p1 3 enqueue dequeue 10 2 4 5 9 3 6 p2 9 3 p1 7 8 8 4 p2 9 10 7 5 11 6 YouTube 等、ストリーミングの先読みバッファ等で利用 Wikipedia / リングバッファ
二分ヒープ構造 要素 i について 格納値: 常に親<=子とする 例: i = 1 親: (n-1)/2 左の子: 2n+1 親: 0 左の子: 3 右の子: 4 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 ヒープソート等で利用 Wikipedia / 二分ヒープ
第10,11,12週の演習の消化 演習
N進整数文字列の数値化 文字列による整数の表現は以下のようになるはず ここで 例: [符号][N進数ヘッダ]N進数表現の整数 符号: 講義資料 第10週 p.62. N進整数文字列の数値化 文字列による整数の表現は以下のようになるはず [符号][N進数ヘッダ]N進数表現の整数 ここで 符号: +, - の何れかで省略可能 N進数ヘッダ: 基数が与えられていな場合(基数=0の場合)に利用 2,8,16進に対して0b,0,0xの何れかで省略時は10進数扱い N進数表現の数値: Nを2~36とすると1桁の数値は0~9,a~z,A~Zの何れか 例: 2進数: 0b11(=3), 8進数: 077(=63), 10進数: 99, 16進数: 0xff(=255), 36進数: zz(=1295) 符号の有無: +0x123, -0x123, 0x123
N進整数文字列の数値化 N進整数の文字列sに対して 講義資料 第10週 p.62. 符号の処理 N進数ヘッダの処理 *s は? 基数の指示が ある? N *s は? *s は? Y '-' s++ 符号は-1 '0' s++ 'b' 2進 '+' s++ 'x' 16進 その他 符号は+1 その他 10進 その他 8進 s++ 符号の処理 N進数ヘッダの処理 N進整数数値化処理本体へ
N進整数文字列の数値化 N進整数の文字列sに対して 講義資料 第10週 p.62. *sがN進整数1桁として 数値化出来ない場合 Y *s を数値化 変換済み数値に符号を付加 *sがN進整数1桁として 数値化出来ない場合 エラー処理が必要 N倍した変換済み数値に加算 s++ 変換済み数値を返して終了 N進整数数値化処理本体
N進整数文字列の数値化 以下の関数がそれぞれの機能に該当 上記4つの関数を利用すると以下の関数が完成出来るはず 講義資料 第10週 p.62. N進整数文字列の数値化 以下の関数がそれぞれの機能に該当 第12週 p.21.: strtosign.c (符号の処理) 第12週 p.22.: strtobase.c (N進数ヘッダの処理) 第11週 p.30.: base36toint.c (36進整数1桁の数値化) 第11週 p.31.: basetoint.c (N進整数1桁の数値化) 上記4つの関数を利用すると以下の関数が完成出来るはず 第10週 p.62.: strtoi.c
講義資料 第12週 p.21.から移動 演習: strtosign.c 文字列sの先頭1文字を確認し、符号の識別子('-'または'+')の有無に応じて-1,+1の何れかを返す関数 strtosign を作成せよ 関数のプロトタイプ宣言はmyfunc_week12.hに作成せよ strtosign_test.c と共にコンパイルして動作を確認せよ 引数 const char *s: 確認する文字列 char **endp: 未処理の文字列へのポインタを返すために用いる 戻り値 符号の識別子がある場合'-'なら-1、'+'なら+1、それ以外なら+1をint型で返す endp が NULL 以外の時は以下の値を*endpに返す 符号の識別子がなかった場合先頭文字(つまりs[0])へのポインタ 符号の識別子があった場合符号識別子の次の文字(つまりs[1])へのポインタ
演習: strtosign.c ヒント: *endp には s[0] または s[1] へのポインタが入る s[0] へのポインタは s, s[1] へのポインタは、s++ した後の s でも良い mintty + bash + GNU C $ gcc strtosign_test.c strtosign.c && ./a s = -5 s = "-5"(0x22a6c0) strtosign(s, &endp) = -1 endp = "5"(0x22a6c1)
講義資料 第12週 p.22.から移動 演習: strtobase.c 文字列sの先頭2文字を確認し、0b,0,0xなら2,8,16進数、それ以外なら10進数と判別する関数 strtobase を作成せよ 関数のプロトタイプ宣言はmyfunc_week12.hに作成せよ strtobase_test.c と共にコンパイルして動作を確認せよ 引数 const char *s : 確認する文字列 char **endp : 未処理の文字列へのポインタを返すために用いる 戻り値 文字列sの先頭2文字に応じて、2,8,10,16の何れかをint型で返す endp が NULL 以外の時は以下の値を*endpに返す 基数識別子(N進数ヘッダ: 0, 0b, 0x)がない場合、先頭文字(つまりs[0])へのポインタ 基数識別子がある場合、基数識別子の次の文字(つまりs[1]またはs[2])へのポインタ
演習: strtobase.c ヒント: 本資料p.31.のフローチャートを見てみよう 講義資料 第12週 p.22.から移動 mintty + bash + GNU C $ gcc strtobase_test.c strtobase.c && ./a s = 0x123 s = "0x123"(0x22a6c0) strtobase(s, &endp) = 16 endp = "123"(0x22a6c2)
講義資料 第11週 p.30.から移動 演習: base36toint.c 36進数で用いられる「0~9,A~Z,a~z(文字コード: 0x30~0x39, 0x41~0x5a, 0x61~0x7a)」までの文字をint型の数値0~35に変換する関数 base36toint を作成せよ 関数のプロトタイプ宣言はmyfunc_week11.hに作成せよ エラーの際、DEBUGマクロが定義されていたら、標準エラー出力に警告メッセージを表示せよ base36toint_test.cと共にコンパイルして動作を確認する事 引数 int c : 数値に変換する文字コード 戻り値 cで与えられた文字コードに対応する数値0~35をint型で返す cが0~9を0~9,A~Zとa~zは共に10~35に変換しそのいずれでもない場合はエラーとなる エラーの場合は-1を返す mintty + bash + GNU C $ gcc base36toint_test.c base36toint.c && ./a c = ? z 35
演習: base36toint.c ヒント: cが'0'~'9'である場合、c-'0'とすると、0~9の数値に変換出来る 講義資料 第11週 p.30.から移動 演習: base36toint.c ヒント: cが'0'~'9'である場合、c-'0'とすると、0~9の数値に変換出来る cが'a'~'z'の場合、 c-'a'はいくらだろう? a~z と A~Z は tolower関数または toupper 関数 で大文字か小文字に変換してしまうと大文字小文字の場合分けが必要なくなる mintty + bash + GNU C $ gcc base36toint_test.c base36toint.c && ./a c = ? z 35
講義資料 第11週 p.31.から移動 演習: basetoint.c 「0~9,A~Z,a~z」の文字をN進数表現の1桁としてint型の数値に変換する関数 basetointを 作成せよ 関数のプロトタイプ宣言はmyfunc_week11.hに作成せよ エラーの際、DEBUGマクロが定義されていたら、標準エラー出力に警告メッセージを表示せよ basetoint_test.cと共にコンパイルして動作を確認する事 引数 int c : 数値に変換する文字コード int base : N進数表現の基数(つまりN=base)、2~36 戻り値 cで与えられた文字コードに対応する数値0~base-1をint型で返す 変換結果やbaseの値が範囲外の場合はエラーとなる エラーの場合は-1を返す mintty + bash + GNU C ヒント: base36toint.c を 用いると簡単に 作成出来る $ gcc basetoint_test.c basetoint.c base36toint.c && ./a c = ? z base = ? 10 -1
演習: strtoi.c N進整数を表現した文字列をint型の値に変換する関数 strtoint を作成せよ 講義資料 第10週 p.62.から移動 演習: strtoi.c 訂正2014-07-25 誤: strtoint 正: strtoi N進整数を表現した文字列をint型の値に変換する関数 strtoint を作成せよ 関数のプロトタイプ宣言はmyfunc_week10.hに作成せよ strtoi_test.cと共にコンパイルして動作を確認する事 引数 const char *s : 変換する文字列 char **endp : 未処理の文字列へのポインタを返すために用いる int base : N進数表現の基数(つまりN=base)、2~36 戻り値 文字列sを基数baseとして数値に変換した結果をint型で返す 文字列先頭に符号識別子('-','+')がある場合、変換結果の±に反映される baseに0が与えられた場合、文字列先頭に0b,0,0xがあれば、2,8,16進数、それ以外なら10進数として扱う。 endp が NULL 以外の時は以下の値を*endpに返す 変換出来た最後の文字の次の文字へのポインタ 訂正2014-07-25 標準ライブラリと 齟齬があったので 直しました
講義資料 第10週 p.62.から移動 演習: strtoi.c ヒント: strtosign.c, strtobase.c, basetoint.c を利用すると比較的簡単に作成できる mintty + bash + GNU C $ gcc strtoi_test.c strtoi.c strtosign.c strtobase.c basetoint.c base36toint.c && ./a s = -0xff base = 0 s = "-0xff"(0x22a6c0) str(s, &endp, base) = -255 endp = ""(0x22a6c5)
参考文献 [1] B.W.カーニハン/D.M.リッチー著 石田晴久 訳、プログラミング言語C 第2版 ANSI 規格準拠、共立出版(1989)