プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ, 情報処理言語Ⅰ(実習を含む。) C言語入門 第14週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ, 情報処理言語Ⅰ(実習を含む。)
関数の引数(値渡し、参照渡し) 復習
関数の引数(値渡し、参照渡し) 変数のスコープ(有効範囲) $ gcc scopetest.c && ./a 第8週資料pp.21-23. 関数の引数(値渡し、参照渡し) 変数のスコープ(有効範囲) scopetest.c mintty + bash + GNU C 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 int gl = 100; void sub(int lo) { int lo = 400; printf("%-4s : %3d: gl=%d, lo=%d\n", __func__, __LINE__, ++gl, ++lo); } int main() int lo = 200; sub(300); return EXIT_SUCCESS; $ gcc scopetest.c && ./a sub : 14: gl=101, lo=401 sub : 16: gl=102, lo=301 main : 23: gl=103, lo=201
関数の引数(値渡し、参照渡し) 変数のスコープ(有効範囲) $ gcc scopetest.c && ./a 第8週資料pp.21-23. 関数の引数(値渡し、参照渡し) 注: rbpレジスタの退避や メモリ配置時のアライメントの問題等もあるため 実際のメモリーの状況とは若干異なります。 変数のスコープ(有効範囲) scopetest.c mintty + bash + GNU C 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 int gl = 100; void sub(int lo) { int lo = 400; printf("%-4s : %3d: gl=%d, lo=%d\n", __func__, __LINE__, ++gl, ++lo); } int main() int lo = 200; sub(300); return EXIT_SUCCESS; gl 100 0x00000001004071c0 $ gcc scopetest.c && ./a sub : 14: gl=101, lo=401 sub : 16: gl=102, lo=301 main : 23: gl=103, lo=201 lo 200 0x000000000023cb24 呼び出し元アドレス 0x0000000180048551 0x000000000023cb28
関数の引数(値渡し、参照渡し) 変数のスコープ(有効範囲) $ gcc scopetest.c && ./a 第8週資料pp.21-23. 関数の引数(値渡し、参照渡し) 注: rbpレジスタの退避や メモリ配置時のアライメントの問題等もあるため 実際のメモリーの状況とは若干異なります。 変数のスコープ(有効範囲) scopetest.c mintty + bash + GNU C 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 int gl = 100; void sub(int lo) { int lo = 400; printf("%-4s : %3d: gl=%d, lo=%d\n", __func__, __LINE__, ++gl, ++lo); } int main() int lo = 200; sub(300); return EXIT_SUCCESS; gl 100 0x00000001004071c0 $ gcc scopetest.c && ./a sub : 14: gl=101, lo=401 sub : 16: gl=102, lo=301 main : 23: gl=103, lo=201 呼び出し元アドレス scopetest.c 22 行目 0x000000000023cb18 lo 300 0x000000000023cb20 lo 200 0x000000000023cb24 呼び出し元アドレス 0x0000000180048551 0x000000000023cb28
関数の引数(値渡し、参照渡し) 変数のスコープ(有効範囲) $ gcc scopetest.c && ./a 第8週資料pp.21-23. 関数の引数(値渡し、参照渡し) 注: rbpレジスタの退避や メモリ配置時のアライメントの問題等もあるため 実際のメモリーの状況とは若干異なります。 変数のスコープ(有効範囲) scopetest.c mintty + bash + GNU C 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 int gl = 100; void sub(int lo) { int lo = 400; printf("%-4s : %3d: gl=%d, lo=%d\n", __func__, __LINE__, ++gl, ++lo); } int main() int lo = 200; sub(300); return EXIT_SUCCESS; gl 100 0x00000001004071c0 $ gcc scopetest.c && ./a sub : 14: gl=101, lo=401 sub : 16: gl=102, lo=301 main : 23: gl=103, lo=201 lo 400 0x000000000023cb14 呼び出し元アドレス scopetest.c 22 行目 0x000000000023cb18 lo 300 0x000000000023cb20 lo 200 0x000000000023cb24 呼び出し元アドレス 0x0000000180048551 0x000000000023cb28
関数の引数(値渡し、参照渡し) 変数のスコープ(有効範囲) $ gcc scopetest.c && ./a 第8週資料pp.21-23. 関数の引数(値渡し、参照渡し) 注: rbpレジスタの退避や メモリ配置時のアライメントの問題等もあるため 実際のメモリーの状況とは若干異なります。 変数のスコープ(有効範囲) scopetest.c mintty + bash + GNU C 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 int gl = 100; void sub(int lo) { int lo = 400; printf("%-4s : %3d: gl=%d, lo=%d\n", __func__, __LINE__, ++gl, ++lo); } int main() int lo = 200; sub(300); return EXIT_SUCCESS; gl 101 0x00000001004071c0 $ gcc scopetest.c && ./a sub : 14: gl=101, lo=401 sub : 16: gl=102, lo=301 main : 23: gl=103, lo=201 lo 401 0x000000000023cb14 呼び出し元アドレス scopetest.c 22 行目 0x000000000023cb18 lo 300 0x000000000023cb20 lo 200 0x000000000023cb24 呼び出し元アドレス 0x0000000180048551 0x000000000023cb28
関数の引数(値渡し、参照渡し) 変数のスコープ(有効範囲) $ gcc scopetest.c && ./a 第8週資料pp.21-23. 関数の引数(値渡し、参照渡し) 注: rbpレジスタの退避や メモリ配置時のアライメントの問題等もあるため 実際のメモリーの状況とは若干異なります。 変数のスコープ(有効範囲) scopetest.c mintty + bash + GNU C 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 int gl = 100; void sub(int lo) { int lo = 400; printf("%-4s : %3d: gl=%d, lo=%d\n", __func__, __LINE__, ++gl, ++lo); } int main() int lo = 200; sub(300); return EXIT_SUCCESS; gl 101 0x00000001004071c0 $ gcc scopetest.c && ./a sub : 14: gl=101, lo=401 sub : 16: gl=102, lo=301 main : 23: gl=103, lo=201 lo 401 0x000000000023cb14 呼び出し元アドレス scopetest.c 22 行目 0x000000000023cb18 lo 300 0x000000000023cb20 lo 200 0x000000000023cb24 呼び出し元アドレス 0x0000000180048551 0x000000000023cb28
関数の引数(値渡し、参照渡し) 変数のスコープ(有効範囲) $ gcc scopetest.c && ./a 第8週資料pp.21-23. 関数の引数(値渡し、参照渡し) 注: rbpレジスタの退避や メモリ配置時のアライメントの問題等もあるため 実際のメモリーの状況とは若干異なります。 変数のスコープ(有効範囲) scopetest.c mintty + bash + GNU C 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 int gl = 100; void sub(int lo) { int lo = 400; printf("%-4s : %3d: gl=%d, lo=%d\n", __func__, __LINE__, ++gl, ++lo); } int main() int lo = 200; sub(300); return EXIT_SUCCESS; gl 102 0x00000001004071c0 $ gcc scopetest.c && ./a sub : 14: gl=101, lo=401 sub : 16: gl=102, lo=301 main : 23: gl=103, lo=201 呼び出し元アドレス scopetest.c 22 行目 0x000000000023cb18 lo 301 0x000000000023cb20 lo 200 0x000000000023cb24 呼び出し元アドレス 0x0000000180048551 0x000000000023cb28
関数の引数(値渡し、参照渡し) 変数のスコープ(有効範囲) $ gcc scopetest.c && ./a 第8週資料pp.21-23. 関数の引数(値渡し、参照渡し) 注: rbpレジスタの退避や メモリ配置時のアライメントの問題等もあるため 実際のメモリーの状況とは若干異なります。 変数のスコープ(有効範囲) scopetest.c mintty + bash + GNU C 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 int gl = 100; void sub(int lo) { int lo = 400; printf("%-4s : %3d: gl=%d, lo=%d\n", __func__, __LINE__, ++gl, ++lo); } int main() int lo = 200; sub(300); return EXIT_SUCCESS; gl 102 0x00000001004071c0 $ gcc scopetest.c && ./a sub : 14: gl=101, lo=401 sub : 16: gl=102, lo=301 main : 23: gl=103, lo=201 呼び出し元アドレス scopetest.c 22 行目 0x000000000023cb18 lo 301 0x000000000023cb20 lo 200 0x000000000023cb24 呼び出し元アドレス 0x0000000180048551 0x000000000023cb28
関数の引数(値渡し、参照渡し) 変数のスコープ(有効範囲) $ gcc scopetest.c && ./a 第8週資料pp.21-23. 関数の引数(値渡し、参照渡し) 注: rbpレジスタの退避や メモリ配置時のアライメントの問題等もあるため 実際のメモリーの状況とは若干異なります。 変数のスコープ(有効範囲) scopetest.c mintty + bash + GNU C 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 int gl = 100; void sub(int lo) { int lo = 400; printf("%-4s : %3d: gl=%d, lo=%d\n", __func__, __LINE__, ++gl, ++lo); } int main() int lo = 200; sub(300); return EXIT_SUCCESS; gl 103 0x00000001004071c0 $ gcc scopetest.c && ./a sub : 14: gl=101, lo=401 sub : 16: gl=102, lo=301 main : 23: gl=103, lo=201 lo 201 0x000000000023cb24 呼び出し元アドレス 0x0000000180048551 0x000000000023cb28
関数の引数(値渡し、参照渡し) 値渡し: 呼出し元の値のコピーを渡す $ gcc call_by_value.c && ./a lo=100 第8週資料pp.21-23. 教科書p.171. 関数の引数(値渡し、参照渡し) 値渡し: 呼出し元の値のコピーを渡す call_by_value.c 4 5 6 7 8 9 10 11 12 13 14 15 16 void sub(int lo) { lo = 200; } int main() int lo = 100; sub(lo); printf("lo=%d\n", lo); return EXIT_SUCCESS; 引数で受け取った変数を変更しても 呼び出し元には反映されない mintty + bash + GNU C $ gcc call_by_value.c && ./a lo=100
関数の引数(値渡し、参照渡し) 値渡し: 呼出し元の値のコピーを渡す $ gcc call_by_value.c && ./a lo=100 第8週資料pp.21-23. 教科書p.171. 関数の引数(値渡し、参照渡し) 注: rbpレジスタの退避や メモリ配置時のアライメントの問題等もあるため 実際のメモリーの状況とは若干異なります。 値渡し: 呼出し元の値のコピーを渡す call_by_value.c 4 5 6 7 8 9 10 11 12 13 14 15 16 void sub(int lo) { lo = 200; } int main() int lo = 100; sub(lo); printf("lo=%d\n", lo); return EXIT_SUCCESS; 引数で受け取った変数を変更しても 呼び出し元には反映されない lo 100 0x000000000023cb24 mintty + bash + GNU C $ gcc call_by_value.c && ./a lo=100 呼び出し元アドレス 0x0000000180048551 0x000000000023cb28
関数の引数(値渡し、参照渡し) 値渡し: 呼出し元の値のコピーを渡す $ gcc call_by_value.c && ./a lo=100 第8週資料pp.21-23. 教科書p.171. 関数の引数(値渡し、参照渡し) 注: rbpレジスタの退避や メモリ配置時のアライメントの問題等もあるため 実際のメモリーの状況とは若干異なります。 値渡し: 呼出し元の値のコピーを渡す 訂正2015-07-25 誤: 11行目 正: 12行目 call_by_value.c 4 5 6 7 8 9 10 11 12 13 14 15 16 void sub(int lo) { lo = 200; } int main() int lo = 100; sub(lo); printf("lo=%d\n", lo); return EXIT_SUCCESS; 引数で受け取った変数を変更しても 呼び出し元には反映されない 呼び出し元アドレス call_by_value.c 12 行目 0x000000000023cb18 lo 100 0x000000000023cb20 mintty + bash + GNU C lo 100 0x000000000023cb24 $ gcc call_by_value.c && ./a lo=100 呼び出し元アドレス 0x0000000180048551 0x000000000023cb28
関数の引数(値渡し、参照渡し) 値渡し: 呼出し元の値のコピーを渡す $ gcc call_by_value.c && ./a lo=100 第8週資料pp.21-23. 教科書p.171. 関数の引数(値渡し、参照渡し) 注: rbpレジスタの退避や メモリ配置時のアライメントの問題等もあるため 実際のメモリーの状況とは若干異なります。 値渡し: 呼出し元の値のコピーを渡す 訂正2015-07-25 誤: 11行目 正: 12行目 call_by_value.c 4 5 6 7 8 9 10 11 12 13 14 15 16 void sub(int lo) { lo = 200; } int main() int lo = 100; sub(lo); printf("lo=%d\n", lo); return EXIT_SUCCESS; 引数で受け取った変数を変更しても 呼び出し元には反映されない 呼び出し元アドレス cal_by_value.c 12 行目 0x000000000023cb18 lo 200 0x000000000023cb20 mintty + bash + GNU C lo 100 0x000000000023cb24 $ gcc call_by_value.c && ./a lo=100 呼び出し元アドレス 0x0000000180048551 0x000000000023cb28
関数の引数(値渡し、参照渡し) 値渡し: 呼出し元の値のコピーを渡す $ gcc call_by_value.c && ./a lo=100 第8週資料pp.21-23. 教科書p.171. 関数の引数(値渡し、参照渡し) 注: 実際にはrbpレジスタの退避等もあるため もう少し余分な物もスタックに積まれます 値渡し: 呼出し元の値のコピーを渡す 訂正2015-07-25 誤: 11行目 正: 12行目 call_by_value.c 4 5 6 7 8 9 10 11 12 13 14 15 16 void sub(int lo) { lo = 200; } int main() int lo = 100; sub(lo); printf("lo=%d\n", lo); return EXIT_SUCCESS; 引数で受け取った変数を変更しても 呼び出し元には反映されない 呼び出し元アドレス call_by_value.c 12 行目 0x000000000023cb18 lo 200 0x000000000023cb20 mintty + bash + GNU C lo 100 0x000000000023cb24 $ gcc call_by_value.c && ./a lo=100 呼び出し元アドレス 0x0000000180048551 0x000000000023cb28
関数の引数(値渡し、参照渡し) 値渡し: 呼出し元の値のコピーを渡す $ gcc call_by_value.c && ./a lo=100 第8週資料pp.21-23. 教科書p.171. 関数の引数(値渡し、参照渡し) 値渡し: 呼出し元の値のコピーを渡す call_by_value.c 4 5 6 7 8 9 10 11 12 13 14 15 16 void sub(int lo) { lo = 200; } int main() int lo = 100; sub(lo); printf("lo=%d\n", lo); return EXIT_SUCCESS; 引数で受け取った変数を変更しても 呼び出し元には反映されない mintty + bash + GNU C lo 100 0x000000000023cb24 $ gcc call_by_value.c && ./a lo=100 呼び出し元アドレス 0x0000000180048551 0x000000000023cb28
関数の引数(値渡し、参照渡し) 参照渡し: 呼出し元の値の格納場所を渡す $ gcc call_by_pointer.c && ./a これは正確には ポインタ渡しと言う 4 5 6 7 8 9 10 11 12 13 14 15 16 void sub(int *lo) { *lo = 200; } int main() int lo = 100; sub(&lo); printf("lo=%d\n", lo); return EXIT_SUCCESS; 引数で受け取った変数を変更すると 呼び出し元にも反映される scanf で見たことがある書き方! &: アドレス演算子 変数loのアドレスを 渡している mintty + bash + GNU C $ gcc call_by_pointer.c && ./a lo=200
関数の引数(値渡し、参照渡し) 参照渡し: 呼出し元の値の格納場所を渡す $ gcc call_by_pointer.c && ./a 4 5 6 7 8 9 10 11 12 13 14 15 16 void sub(int *lo) { *lo = 200; } int main() int lo = 100; sub(&lo); printf("lo=%d\n", lo); return EXIT_SUCCESS; 呼び出し元アドレス call_by_pointer.c 12 行目 0x000000000023cb14 lo 0x000000000023cb24 0x000000000023cb1c mintty + bash + GNU C lo 100 0x000000000023cb24 $ gcc call_by_pointer.c && ./a lo=200 呼び出し元アドレス 0x0000000180048551 0x000000000023cb28
関数の引数(値渡し、参照渡し) 参照渡し: 呼出し元の値の格納場所を渡す $ gcc call_by_pointer.c && ./a 4 5 6 7 8 9 10 11 12 13 14 15 16 void sub(int *lo) { *lo = 200; } int main() int lo = 100; sub(&lo); printf("lo=%d\n", lo); return EXIT_SUCCESS; ポインターを介して呼び出し元の lo を書き換え 呼び出し元アドレス cal_by_pointer.c 12 行目 0x000000000023cb18 lo 0x000000000023cb24 0x000000000023cb18 mintty + bash + GNU C lo 200 0x000000000023cb24 $ gcc call_by_pointer.c && ./a lo=200 呼び出し元アドレス 0x0000000180048551 0x000000000023cb28
関数の引数(値渡し、参照渡し) 参照渡し: 呼出し元の値の格納場所を渡す $ gcc call_by_pointer.c && ./a 4 5 6 7 8 9 10 11 12 13 14 15 16 void sub(int *lo) { *lo = 200; } int main() int lo = 100; sub(&lo); printf("lo=%d\n", lo); return EXIT_SUCCESS; 呼び出し元アドレス call_by_pointer.c 12 行目 0x000000000023cb14 lo 0x000000000023cb24 0x000000000023cb1c mintty + bash + GNU C lo 200 0x000000000023cb24 $ gcc call_by_pointer.c && ./a lo=200 呼び出し元アドレス 0x0000000180048551 0x000000000023cb28
標準ライブラリ関数を例にした実例 文字列操作とポインタ操作
ポインタを用いた文字列操作の例 strlen 関数の大まかな仕組み strlen_with_idx.c strlen_with_ptr1.c 文字列の長さは 先頭から終端文字('\0')の手前までの 文字数 size_t strlen(const char *s) { size_t len = 0; while (s[len] != '\0') len++; return len; } strlen_with_ptr1.c strlen_with_ptr2.c size_t strlen(const char *s) { const char *s0 = s; while (*s != '\0') s++; return s - s0; } size_t strlen(const char *s) { const char *s0 = s; while (*(s++) != '\0') ; return s - s0 - 1; }
ポインタを用いた文字列のコピーの例 strcpy 関数の大まかな仕組み strcpy_with_idx.c 文字列のコピーは 先頭から終端文字('\0')までを コピーすれば良い 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; }
ポインタを用いた文字列のコピーの例 strncpy 関数の大まかな仕組み strncpy_with_idx.c strncpy は strcpy に加えて 終端文字('\0')以降を'\0'で埋める 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; } 論理演算は左から右に評価され、 真偽値が確定すると評価を終了する。 つまり i < n や dst < dst0 + n が偽なら、 そこで真偽値が確定するので それより右にある (dst[i] = src[i]) != '\0' や (*(dst++) = *(src++)) != '\0' は 実行されない。 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; }
ポインタを用いた文字列の比較の例 strcmp 関数の大まかな仕組み strcmp_with_idx.c strcmp_with_ptr.c どちらかが終端文字('\0')になるか 異なる値が出てくるまで比較し 終了位置を比較すれば良い 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;
ポインタを用いた文字列の比較の例 strncmp 関数の大まかな仕組み strncmp_with_idx.c strncmp は strcmp の 比較文字数を最大n文字に限定する 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;
ポインタを用いた文字列の連結の例 strcat 関数の大まかな仕組み strcat_with_idx.c strcat_with_ptr.c dst の終端位置に src をコピーする 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; }
ポインタを用いた文字列の連結の例 strncat 関数の大まかな仕組み strncat_with_idx.c strncat は strcat の 連結文字を最大n文字に限定する ただしsrcがn文字以上の場合 終端文字が+1文字され 合計n+1バイト追記される 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; }
ポインタへのポインタ 関数の引数でポインタを返したい場合はポインタ変数へのポインタを用いる 教科書 pp.243-250. 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; この例だと endp に "z" へのポインタ つまり&s[2]が返ってくる main.c char s[] = "ffz"; char *endp; printf("strtoui(s, &endp, 16);
main 関数の引数 コマンドライン引数と文字列
コマンドライン引数 main 関数の引数として取得出来る。 教科書 pp.265-272., [1] pp.139-144., 第8週資料 p.4. コマンドライン引数 main 関数の引数として取得出来る。 arg_ex1.c #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int i; printf("argc = %d\n", argc); for (i = 0; i < argc; i++) { printf("argv[%d] = \"%s\"\n", i, argv[i]); } return EXIT_SUCCESS; main の引数名は 自由につけて良いが 以下の名前を使うのが 慣例になっている argc: ARGument Count argv: ARGument Vector argument は英語で 引数を意味する
コマンドライン引数 main 関数の引数として取得出来る。 教科書 pp.265-272., [1] pp.139-144., 第8週資料 p.4. コマンドライン引数 main 関数の引数として取得出来る。 コマンドプロンプト >arg_ex1 a b "c d" "e\"f" argc = 5 argv[0] = "C:\Users\kou\Desktop\CLangI2015S1\week14\arg_ex1.exe" argv[1] = "a" argv[2] = "b" argv[3] = "c d" argv[4] = "e"f" mintty + bash コマンドライン空白で分割されて argvに格納される。 argvに空白を含めたい場合は 「"」(ダブルクォーテーション)で囲む 「"」を含めたい場合は「\」でエスケープする argv[0]は実行中のコマンド名 $ ./a a b "c d" "e\"f" argc = 5 argv[0] = "./argtest" argv[1] = "a" argv[2] = "b" argv[3] = "c d" argv[4] = "e"f"
コマンドライン引数 char *argv[] は char* 型の配列 教科書 pp.265-272., [1] pp.139-144., 第8週資料 p.4. コマンドライン引数 char *argv[] は char* 型の配列 mintty + bash 正確には 関数の引数で最初の [] は * と同じだったので char *argv[] は char **argv と同じ つまり char 型へのポインタへのポインタ $ ./a a b "c d" "e\"f" argc = 5 argv[0] = "./argtest" argv[1] = "a" argv[2] = "b" argv[3] = "c d" argv[4] = "e"f" argv[1] argv[2] argv[3] argv[4] 'a' '\0' 'b' 'c' ' ' 'd' 'e' '"' f
文字列とポインタ
ポインタ配列の初期化 char * の配列の初期化 教科書.pp.235-239., [1]pp.148-153. メモリ上のどこかに配置された 文字列 char * の配列の初期化 char *s[] = {"one", "two", "three"}; 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 0x~00 o 0x~01 n 0x~02 e 0x~03 \0 0x~04 t 0x~05 w 0x~06 0x~07 0x~08 0x~09 h 0x~0a r 0x~0b 0x~0c 0x~0d s は char* 型で要素数3の配列 s[x] は char* 型 *s[x] は char 型 s[0] s[0] は "one" s[1] は "two" s[1] s[0][0] は 'o' s[0][1] は 'n' s[0][2] は 'e' s[0][3] は '\0' s[2]
配列とポインタの初期値と文字列 配列とポインタで扱いが異なることに注意 教科書.pp.235-239., [1]pp.148-153. pointer_ex9.c 一般にポインタに初期値として与えた 文字列定数は書き変えてはいけない void sub() { char s[] = "hello"; char *p = "world"; ... .rdataセクションに用意されたデータは 書き変えてはいけない objdump の結果 sへは"hello"の文字コード 68,65,6c,6c,6fが代入されているが pへは.rdataセクションに予め 用意してある文字列"world"の アドレス0x403060が代入されている セクション .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)
配列とポインタの初期値と文字列 配列とポインタで扱いが異なることに注意 教科書.pp.235-239., [1]pp.148-153. pointer_ex9.c Cygwin + GNU 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(); return EXIT_SUCCESS; $ gcc pointer_ex9.c && ./a s = "hello" p = "world" Segmentation fault (コアダンプ) Borland C++ >bcc32 pointer_ex9.c && pointertest6 Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland pointer_ex9.c: Turbo Incremental Link 5.00 Copyright (c) 1997, 2000 Borland s = "hello" p = "world" p = "World" sub()が実行された際、 sは毎回"hello"だが pは2回目以降"World"になってしまう もしくは.rdataへの不正な書き込みで 異常終了してしまう
数値を表す文字列を数値に変換する 演習
ASCII文字コード表 教科書 p.51., 第2週資料 p.67. 下位4ビット 1 2 3 4 5 6 7 8 9 A B C D E 1 2 3 4 5 6 7 8 9 A B C D E F NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO SI DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC → ← ↑ ↓ SP ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~ DEL 上位4ビット http://ja.wikipedia.org/wiki/ASCII 赤字は制御コード
10進法文字列を数値に変換する "1234" のような文字列があった時 1234 に変換したい。 "1234" は char 型の配列に'\0'で終端された文字コード {0x31, 0x32, 0x33, 0x34, 0x00} という値を持つ。 まず、先頭から数字('0'~'9')のみを取り出す {0x31, 0x32, 0x33, 0x34} を得る 各文字コードから 0x30(='0') を引く {0x31-0x30, 0x32-0x30, 0x61-0x30, 0x62-0x30} より {1, 2, 3, 4} が得られる 次に末尾から𝑛桁目(0≦𝑛)に 10 𝑛 を掛ける {1*1000, 2*100, 3*10, 4*1} より {1000, 200, 30, 4} が得られる 各値を加算する 1000+200+30+4=1234 が得られる
16進法文字列を数値に変換する "0x12ab" のような文字列があった時 0x12ab=(4779) に変換したい。 "0x12ab" は char 型の配列に'\0'で終端された文字コード {0x30, 0x78, 0x31, 0x32, 0x61, 0x62, 0x00} という値を持つ。 まず、先頭から16進ヘッダを取り除いて、16進法の数字('0'~'9','a'~'f')のみを取り出す {0x31, 0x32, 0x61, 0x62} を得る 各文字コードのうち'0'~'9'からは 0x30(='0') を引き、'a'~'f'からは 0x61(='a') を引き 0xa(=10) を足す {0x31-0x30, 0x32-0x30, 0x61-0x61+0xa, 0x62-0x61+0xa} より {1, 2, 10, 11} が得られる 次に末尾から𝑛桁目(0≦𝑛)に 16 𝑛 を掛ける {1*4096, 2*256, 10*16, 11*1} より {4096, 512, 160, 11} が得られる 各値を加算する 4096+512+160+11=4779 が得られる 訂正2015-07-17 誤:0x61-0x60+0xa 正:0x61-0x61+0xa
N進法文字列を数値に変換する "12ab" のような文字列があった時、これをN進法とみなして数値に変換したい。 "12ab" は char 型の配列に'\0'で終端された文字コード {0x31, 0x32, 0x61, 0x62, 0x00} という値を持つ。 まず、先頭からN進法の数字('0'~'9','a'~'z')のみを取り出す {0x31, 0x32, 0x61, 0x62} を得る 各文字コードのうち'0'~'9'からは 0x30(='0') を引き、'a'~'f'からは 0x61(='a') を引き 0xa(=10) を足す {0x31-0x30, 0x32-0x30, 0x61-0x61+0xa, 0x62-0x61+0xa} より {1, 2, 10, 11} が得られる 次に末尾から𝑛桁目(0≦𝑛)に 𝑁 𝑛 を掛ける {1* 𝑁 3 , 2* 𝑁 2 , 10* 𝑁 1 , 11* 𝑁 0 } が得られる 各値を加算する 1* 𝑁 3 +2* 𝑁 2 +10* 𝑁 1 +11* 𝑁 0 が得られる 訂正2015-07-17 誤:0x61-0x60+0xa 正:0x61-0x61+0xa 訂正2015-07-17 誤: {0x30, 0x78, 0x31, 0x32, 0x61, 0x62, 0x00} 正: {0x31, 0x32, 0x61, 0x62, 0x00} 結局10進の場合も、16進の場合も N進の場合で一般化出来る ↓ N進変換のルーチンさえ作れば 全て変換出来る
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に対して 符号があれば読み飛ばし、+1 または -1 を得る 符号の処理 符号があれば読み飛ばし、+1 または -1 を得る N進法ヘッダの処理 N進法ヘッダがあれば読み飛ばし、基数を得る N進整数数値化処理本体 N進整数文字列を数値に変換する
N進整数文字列の数値化 N進整数の文字列sに対して 何らかの方法で呼び出し元に 決定した符号を返してやる必要がある 戻り値が2つ必要 符号の処理 *s は? 何らかの方法で呼び出し元に 決定した符号を返してやる必要がある 戻り値が2つ必要 '-' s++ 符号は-1 何らかの方法で呼び出し元に sの現在位置を返してやる必要がある '+' s++ その他 符号は+1
N進整数文字列の数値化 N進整数の文字列sに対して strtoi.c では この部分も必要となる strtobase.c はこの部分 基数の指示が ある? N *s は? *s は? Y '0' s++ 'b' s++ 2進 'x' s++ 16進 その他 10進 その他 8進
N進整数文字列の数値化 N進整数の文字列sに対して ヒント '0'~'9' から 0x30(='0')を引く 'A'~'Z' から 0x41(='A')を引き 0xa(=10)を足す 'a'~'z' から 0x61(='a')を引き 0xa(=10)を足す *s は N進文字か? N Y *s を数値化 変換済み数値に符号を付加 例えば "0x1234" なら基数 N=16 であり result = 0; result += result * 16 + 4; // result は 0x4 result += result * 16 + 3; // result は 0x34 result += result * 16 + 2; // result は 0x234 result += result * 16 + 1; // result は 0x1234 とすると result に 0x1234 が得られる N倍した変換済み数値に加算 s++ 変換済み数値を返して終了 訂正2015-07-17 1,2,3,4 の加算順が逆だった(次項参照)
N進整数文字列の数値化 N進整数の文字列sに対して ヒント '0'~'9' から 0x30(='0')を引く 訂正2015-07-25 誤: result += result * ... 正: result = result * ... N進整数数値化処理本体 ヒント '0'~'9' から 0x30(='0')を引く 'A'~'Z' から 0x41(='A')を引き 0xa(=10)を足す 'a'~'z' から 0x61(='a')を引き 0xa(=10)を足す *s は N進文字か? N Y *s を数値化 変換済み数値に符号を付加 例えば "0x1234" なら基数 N=16 であり result = 0; result = result * 16 + 1; // result は 0x1 result = result * 16 + 2; // result は 0x12 result = result * 16 + 3; // result は 0x123 result = result * 16 + 4; // result は 0x1234 とすると result に 0x1234 が得られる N倍した変換済み数値に加算 s++ 変換済み数値を返して終了 訂正2015-07-17 1,2,3,4 の加算順が逆だった(前頁参照)
例題: strtosign.c 文字列sの先頭1文字を確認し、符号の識別子('-'または'+')の有無に応じて-1,+1の何れかを返す関数 strtosign を作成せよ 関数のプロトタイプ宣言はmyfunc_week14.hに作成せよ strtosign_test.c と共にコンパイルして動作を確認せよ 引数 const char *s: 確認する文字列 char **endp: 未処理の文字列へのポインタを返すために用いる 戻り値 符号の識別子がある場合'-'なら-1、'+'なら+1、それ以外なら+1をint型で返す endp が NULL 以外の時は以下の値を*endpに返す 符号の識別子がなかった場合先頭文字(つまりs[0])へのポインタ 符号の識別子があった場合符号識別子の次の文字(つまりs[1])へのポインタ
例題: strtosign.c フローチャートから書き起こす strtosign.c 符号の処理 int strtosign(const char *s, char **endp) { int sign; switch (*s) { case '-': s++; sign = -1; break; case '+': default: sign = +1; } if (endp != NULL) *endp = (char *) s; return sign; *s は? '-' s++ 符号は-1 '+' s++ その他 符号は+1 mintty + bash + GNU C $ gcc strtosign_test.c strtosign.c && ./a s = ? -5 s = "-5"(0x22c710) strtosign(s, &endp) = -1 endp = "5"(0x22c711)
例題: 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"(0x22c710) strtosign(s, &endp) = -1 endp = "5"(0x22c711)
演習: strtobase.c 文字列sの先頭2文字を確認し、0b,0,0xなら2,8,16進数、それ以外なら10進数と判別する関数 strtobase を作成せよ 関数のプロトタイプ宣言はmyfunc_week14.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 ヒント: 前出の「N進法ヘッダの処理」のフローチャートを見てみよう mintty + bash + GNU C $ gcc strtobase_test.c strtobase.c && ./a s = 0x123 s = "0x123"(0x22c710) strtobase(s, &endp) = 16 endp = "123"(0x22c712)
演習: base36toint.c 36進法で用いられる'0'~'9','A'~'Z','a'~'z' (文字コード: 0x30~0x39, 0x41~0x5a, 0x61~0x7a) までの文字をint型の数値0~35に変換する関数 base36toint を作成せよ 関数のプロトタイプ宣言は myfunc_week14.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の数値に変換出来る 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
演習: base36toint.c ヒント: デバッグ出力は以下のようにすれば良い mintty + bash + GNU C #ifdef DEBUG fprintf(stderr, "Warning: in file %s line %d: invalid value: c = '%c'(=%#04x)\n", __FILE__, __LINE__, c, c); #endif mintty + bash + GNU C $ gcc base36toint_test.c base36toint.c -DDEBUG && ./a c = ? @ Warning: in file base36toint.c line 15: invalid value: c = '@'(=0x40) -1
演習: basetoint.c 「0~9,A~Z,a~z」の文字をN進数表現の1桁としてint型の数値に変換する関数 basetointを 作成せよ 関数のプロトタイプ宣言はmyfunc_week14.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 $ gcc basetoint_test.c basetoint.c base36toint.c && ./a c = ? z base = ? 10 -1
演習: basetoint.c ヒント base36toint.c を用いると簡単に作成出来る base36toint の結果が -1 なら 36 進法で使えない数字 N 以上なら N 進法で使えない数字 訂正2015-07-17 誤: N 以下 正: N 以上
演習: strtoi.c N進整数を表現した文字列をint型の値に変換する関数 strtoi を作成せよ 関数のプロトタイプ宣言はmyfunc_week14.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に返す 変換出来た最後の文字の次の文字へのポインタ
講義資料 第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)
演習: strtoi.c ヒント basetoint(*s, N) の結果が -1 なら N 進文字以外 -1 なら36進文字以外 N 以上なら N 進文字以外 N進整数数値化処理本体 符号の処理 *s は N進文字か? N base36toint(*s) の結果を そのまま使える N進法ヘッダの処理 Y *s を数値化 変換済み数値に符号を付加 N進整数数値化処理本体 N倍した変換済み数値に加算 全体が strtoi s++ 変換済み数値を返して終了
C言語の開発支援ツール
分割コンパイル ファイルが多くなるとコンパイルが大変 mintty + bash + GNU C cmd + Borland C++ $ gcc strtoi_test.c strtoi.c strtosign.c strtobase.c basetoint.c base36toint.c $ cmd + Borland C++ >bcc32 strtoi_test.c strtoi.c strtosign.c strtobase.c basetoint.c base36toint.c Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland strtoi_test.c: strtoi.c: strtosign.c: strtobase.c: basetoint.c: base36toint.c: Turbo Incremental Link 5.00 Copyright (c) 1997, 2000 Borland 一度実行すれば カーソルキーの上下で コマンドの実行履歴から 選べるが、最初が面倒 1ファイルしか変更してないのに 全ファイルコンパイルし直すのは非効率
make コマンド 依存関係を記述し必要な処理だけ行う Makefile に依存関係と作成方法を記述する Makefile.cygwin.strtoi_test,1 all: strtoi_test.exe strtoi_test.exe: strtoi_test.c strtoi.c strtosign.c strtobase.c basetoint.c base36toint.c $(CC) -o $@ $^ clean: -rm strtoi_test.exe 作成するファイル: 材料のファイル ... タブ...→作成方法 Makefile.bcc32.strtoi_test all: strtoi_test.exe strtoi_test.exe: strtoi_test.c strtoi.c strtosign.c strtobase.c basetoint.c base36toint.c $(CC) strtoi_test.c strtoi.c strtosign.c strtobase.c basetoint.c base36toint.c clean: -DEL strtoi_test.exe *.obj *.tds
make コマンド make と打つだけで自動的にコンパイル mintty + bash + GNU C cmd + Borland C++ $ make -f Makefile.cygwin.strtoi_test,1 cc -o strtoi_test.exe strtoi_test.c strtoi.c strtosign.c strtobase.c basetoint.c base36toint.c cmd + Borland C++ >make -f Makefile.bcc32.strtoi_test MAKE Version 5.2 Copyright (c) 1987, 2000 Borland bcc32 strtoi_test.c strtoi.c strtosign.c strtobase.c basetoint.c base36t oint.c Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland strtoi_test.c: strtoi.c: strtosign.c: strtobase.c: basetoint.c: base36toint.c: Turbo Incremental Link 5.00 Copyright (c) 1997, 2000 Borland
make コマンド 依存関係の記述 作成するファイル: 材料のファイル ... タブ...→作成方法 Makefile.cygwin.strtoi_test,2 all: strtoi_test.exe strtoi_test.exe: strtoi_test.o strtoi.o strtosign.o strtobase.o basetoint.o base36toint.o $(CC) -o $@ $^ strtoi_test.o: strtoi_test.c myfunc_week14.h strtoi.o: strtoi.c myfunc_week14.h strtosign.o: strtosign.c myfunc_week14.h strtobase.o: strtobase.c myfunc_week14.h basetoint.o: basetoint.c myfunc_week14.h base36toint.o: base36toint.c myfunc_week14.h clean: -rm strtoi_test.exe strtoi_test.o strtoi.o strtosign.o strtobase.o basetoint.o base36toint.o 標準的な作成方法で良い場合は 作成方法は省略出来る 訂正2015-07-25 誤: myfunc_week10.h, myfunc_week10.h, myfunc_week12.h 正: myfunc_week14.h
make コマンド 依存関係の解決 コンパイルが完了しているので 改めてコンパイルする 必要がなかった mintty + bash + GNU C 直前に削除したため改めて コンパイルが必要になった ファイルだけ処理し直して てくれている $ make -f Makefile.cygwin.strtoi_test,2 cc -c -o strtoi_test.o strtoi_test.c cc -c -o strtoi.o strtoi.c cc -c -o strtosign.o strtosign.c cc -c -o strtobase.o strtobase.c cc -c -o basetoint.o basetoint.c cc -c -o base36toint.o base36toint.c cc -o strtoi_test.exe strtoi_test.o strtoi.o strtosign.o strtobase.o basetoint.o base36toint.o $ make make: Nothing to be done for 'all'. $ rm strtosign.o
make コマンド 依存関係の解決 例: 必要に応じて適宜作成方法を実行する 材料のファイルが不足している場合 作成するファイル(旧)、材料のファイル(新)の場合
make コマンド 同じmakeという名前が付いていて基本は同じだが方言があり、細かい違いがある Wikipedia / make GNU Make Embarcadero / MAKE JM / make (1) FreeBSD 9.0-RELEASE-K / make (1) MSDN / NMAKE Reference
その他のビルドツール Autotools autoconf, automake, libtool の総称 UNIX 系のソフトウェアでは標準的なビルドツール 以下の標準的な手順でビルド出来るようになる 書籍 GNU AUTOCONF, AUTOMAKE, AND LIBTOOL https://sourceware.org/autobook/ (無料オンライン版) Wikipedia / Autotools mintty + bash + GNU C $ ./configure $ make $ make install
その他のビルドツール CMake - http://www.cmake.org/ Wikipedia / CMake マルチプラットフォームな Makefile 作成ツール Wikipedia / CMake
ライブラリの自作 標準ライブラリ関数はアーカイブやライブラリと呼ばれる複数のオブジェクトファイルを1つにまとめたファイルとして提供されている 教科書pp.203-206. ライブラリの自作 標準ライブラリ関数はアーカイブやライブラリと呼ばれる複数のオブジェクトファイルを1つにまとめたファイルとして提供されている cygwinでは/usr/lib/libc.a等 Borland C++ではC:\boland\bcc32\Lib\cw32.lib等 標準ライブラリはコンパイル時に自動的にリンクされる 但し <math.h> 等は -lm 等として明示的に /usr/lib/libm.aをリンクする必要がある場合もある
ライブラリの自作 複数のオブジェクトファイルを1つのファイルにまとめるにはアーカイバやライブラリアンと呼ばれるツールを用いる JM / ar (1) 作成方法: ar q ライブラリ名 オブジェクトファイル名 ... embarcadero / ライブラリマネージャ TLIB.EXE 作成方法: tlib ライブラリ名 -+オブジェクトファイル名 ...
ライブラリの自作(Cygwin) 複数のオブジェクトファイル(.oファイル)を1つの.aファイルにまとめる まとめたファイルをアーカイブ(archive)またはスタティックライブラリ(static library)と呼ぶ mintty + bash + GNU C $ gcc -c strtoi.c strtosign.c strtobase.c basetoint.c base36toint.c $ ar q myfunc.a strtoi.o strtosign.o strtobase.o basetoint.o base36toint.o ar: myfunc.a を作成しています $ gcc strtoi_test.c myfunc.a 必要な .o ファイルを myfunc.a から探してリンクし 実行ファイルを作成 複数の .o ファイルをまとめた .a ファイルの作成
ライブラリの自作(Borland C++) 複数のオブジェクトファイル(.objファイル)を1つの.libファイルにまとめる まとめたファイルをライブラリと呼ぶ cmd + Borland C++ >bcc32 /c strtoi.c strtosign.c strtobase.cbasetoint.c base36toint.c Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland strtoi.c: strtosign.c: strtobase.c: basetoint.c: base36toint.c: >tlib myfunc.lib -+strtoi.obj -+strtosign.obj -+strtobase.obj -+basetoint.obj -+base36toint.obj TLIB 4.5 Copyright (c) 1987, 1999 Inprise Corporation >bcc32 strtoi_test.c myfunc.lib strtoi_test.c: Turbo Incremental Link 5.00 Copyright (c) 1997, 2000 Borland 複数の .o ファイルをまとめた .lib ファイルの作成 必要な .obj ファイルを myfunc.lib から探してリンクし 実行ファイルを作成
ライブラリの自作 make によるアーカイブの作成 Makefile.cygwin.myfunc.a all: myfunc.a myfunc.a: strtoi.o strtosign.o strtobase.o basetoint.o base36toint.o $(AR) q $@ $^ strtoi_test.o: strtoi_test.c myfunc_week14.h strtoi.o: strtoi.c myfunc_week14.h strtosign.o: strtosign.c myfunc_week14.h strtobase.o: strtobase.c myfunc_week14.h basetoint.o: basetoint.c myfunc_week14.h base36toint.o: base36toint.c myfunc_week14.h clean: -rm myfunc.a strtoi.o strtosign.o strtobase.o basetoint.o base36toint.o 訂正2015-07-25 誤: myfunc_week10.h, myfunc_week10.h, myfunc_week12.h 正: myfunc_week14.h
ライブラリの自作 make によるアーカイブの作成 mintty + bash + GNU C $ make -f Makefile.cygwin.myfunc.a cc -c -o strtoi.o strtoi.c cc -c -o strtosign.o strtosign.c cc -c -o strtobase.o strtobase.c cc -c -o basetoint.o basetoint.c cc -c -o base36toint.o base36toint.c ar q myfunc.a strtoi.o strtosign.o strtobase.o basetoint.o base36toint.o ar: myfunc.a を作成しています
参考文献 [1] B.W.カーニハン/D.M.リッチー著 石田晴久 訳、プログラミング言語C 第2版 ANSI 規格準拠、共立出版(1989)