C 言語入門 第 12 週 プログラミング言語Ⅰ ( 実習を含 む。 ), 計算機言語Ⅰ・計算機言語 演習Ⅰ, 情報処理言語Ⅰ ( 実習を含 む。 ) 1
文字列の基本 2
2 進数 8 進数 10 進数 16 進数 a b c d e f の桁へ 桁上がり 4 の桁へ 桁上がり 8 の桁へ 桁上がり 16 の桁へ 桁上がり 8 の桁へ 桁上がり 10 の桁へ 桁上がり 16 の桁へ 桁上がり
2 進数 8 進数 10 進数 16 進数 f f fa fb fc fd fe ff の桁へ 桁上がり 256 の桁へ 桁上がり
8bit 整数の 10 進表現 2進2進 進 ABCDEF A B C D E F 下位 4 ビット 5 2 進数と 10 進数は桁数の対応で収まりが悪い
8bit 整数の 2 進表現 2進2進 進 ABCDEF A B C D E F 下位 4 ビット 6 2 進数は桁が多過ぎて直感的に分かり難い
8bit 整数 16 進表現 2進2進 進 ABCDEF a0b0c0d0e0f a1b1c1d1e1f a2b2c2d2e2f a3b3c3d3e3f a4b4c4d4e4f a5b5c5d5e5f a6b6c6d6e6f a7b7c7d7e7f a8b8c8d8e8f a9b9c9d9e9f 1010 A a0a1a2a3a4a5a6a7a8a9aaabacadaeaf 1011 B b0b1b2b3b4b5b6b7b8b9babbbcbdbebf 1100 C c0c1c2c3c4c5c6c7c8c9cacbcccdcecf 1101 D d0d1d2d3d4d5d6d7d8d9dadbdcdddedf 1110 E e0e1e2e3e4e5e6e7e8e9eaebecedeeef 1111 F f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff 下位 4 ビット 7 2 進数と 16 進数は相性が良い。 2 進数 4 桁が 16 進数 1 桁に対応するので直感的に分かり易い
ASCII 文字コード表 16 進 ABCDEF 0NULSOHSTXETXEOTENQACKBELBSHTLFVTFFCRSOSI 1DLEDC1DC2DC3DC4NAKSYNETBCANEMSUBESCFSGSRSUS 2SP!"#$%&'()*+,-./ :;<=>? 5PQRSTUVWXYZ[\]^_ 6`abcdefghijklmno 7pqrstuvwxyz{|}~DEL 8 9 A B C D E F 下位 4 ビット 赤字は制御コード 教科書 p.51., 第 2 週資料 p
標準ヘッダ // 文字コードの調査 (c が該当する文字なら真を返す ) int islower(int c); // 小文字 int isupper(int c); // 大文字 int isdigit(int c); //10 進数 ( 数字 ) int isxdigit(int c);//16 進数 int isalpha(int c); // 英字 (isupper(c)||islower(c)) int isalnum(int c); // 英数字 (isalpha(c)||isdigit(c)) int iscntrl(int c); // 制御文字 (0x00 ~ 0x1f, 0x7f) int isspace(int c); // 空白文字 (' ','\f','\n','\r','\t','\v') int isprint(int c); // 印字可能文字 (0x20 ~ 0x7e) int isgraph(int c); // 印字可能文字 ( スペースを除く ) isprint(c)&&!isspace(c) int ispunct(int c); // 印字可能文字 ( スペース、英数字を除 く )isgraph(c)&&!isalnum(c) // 文字コードの変換 int tolower(int c); //c を小文字に変換 int toupper(int c); //c を大文字に変換 9 [1] pp.303. JM: isalpha (3)isalpha touppertoupper (3) 講義資料の ctype_test.c, ctype_test.xlsx も参照
char 型変数と文字コード char, unsigned char 型 半角文字の文字コードを1つ つまり 1 バイト (=8 ビット ) を格納出来るサイ ズ char: -128 ~ 127 unsigned char: 0 ~ 教科書 pp.44, , 第 2 週資料 pp c 変数の実体は N バイト (8N ビット ) のメモリ つまり内部的には 2 進数が入っている 16 進数リテラルによる初期化 char c = 0x68; // =104='h' 文字コードに対応する文字を表示 printf("%c\n", 104); // "%c" に文字コードを与えると対応する文字が表示される
char 型変数と文字コード 以下の変数宣言と初期化は全く同じ結果 11 教科書 pp.44, , 第 2 週資料 pp c どの方法で初期化しようと結局 char 型 (1 バイト ) の変数 c を 0b で初期化している 文字コードを代入したい場合 文字定数リテラルで書くと 数値リテラルで書くよりも 意味が分かり易くなる 結果は同じでも読んだ時 分かり易いのはどれだろう? 必要であれば 読んだ人が分かり易いよう コメントで情報を補うと良い 文字定数リテラルによる初期化 char c = 'h'; // 0x68 10 進数リテラルによる初期化 char c = 104; // 'h' 16 進数リテラルによる初期化 char c = 0x68; // 'h' c 104 c 0x68 c 'h'
char 型配列と文字列 以下の変数宣言と初期化は全く同じ結果 12 文字定数リテラルによる初期化 char s[] = {'h', 'e', 'l', 'l', 'o', '\0'}; 文字列定数リテラルによる初期化 char s[] = "hello"; 教科書 pp.44, , 第 2 週資料 pp 進数リテラルによる初期化 char s[] = {104, 101, 108, 108, 111, 0}; 16 進数リテラルによる初期化 char s[] = {0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00}; s[0] 'h' s[1] 'e' s[2] 'l' s[3] 'l' s[4] 'o' s[5] '\0' 文字列は配列に格納した 一連の文字コード 終端には 0 を格納する どれで書いても良いが 普通は面倒だから 文字列の初期化は 文字列定数リテラルを 用いる
char 型配列と文字列 文字列の表示 13 文字列定数リテラルによる初期化 char s[] = "hello"; 教科書 pp.44, , 第 2 週資料 pp s[0] 'h' s[1] 'e' s[2] 'l' s[3] 'l' s[4] 'o' s[5] '\0' 文字列の表示 printf("%s\n", s); // "%s" に与えたアドレス以降 '\0' が現れるまで // メモリに格納された文字コードに対応する文字が表示される
関数の引数とよく使われる名前 p, ptr, pointer : ポインタ c, ch, chr, character : 文字 s, str, string : 文字列 s, src, source : 発信元 d, dst, destination : 送信先 t, txt, text : テキスト l, len, length : 長さ w, wid, width : 幅 h, hei, height : 高さ n, num, number : ( 個 ) 数 s, size : サイズ b, buf, buff, buffer : バッファ i, idx, index : 添え字 s, stat, status : 状態 14
標準ヘッダ 15
strlen 関数 size_t strlen(const char *s); 文字列 s の長さを返す 引数 : s: 長さを調べる文字列 戻り値 文字列 s の長さを返す 16 教科書 p.312., [1] pp JM: strlen (3)strlen
strcpy 関数 char *strcpy(char *dst, const char *src); '\0' を含めて src を dst へコピーする 引数 : dst: コピー先のアドレス src: コピー元の文字列 戻り値 dst を返す dst が十分な大きさでないとバッファオーバーフローを引き起 こすので注意 17 教科書 p.308., [1] pp JM: strcpy (3)strcpy
strncpy 関数 char *strncpy(char *dst, const char *src, size_t n); src から最大 n 文字 dst へコピーする 引数 : dst: コピー先のアドレス src: コピー元の文字列 n: コピーする最大文字数 戻り値 dst を返す src が n 文字より短い場合は、残りの dst の末尾に '\0' を詰めて、合計 n バイト書き込む 18 教科書 p.308., [1] pp JM: strcpy (3)strcpy
strcat 関数 char *strcat(char *dst, const char *src); 文字列 dst の末尾に文字列 src を連結する 引数 : dst: 連結先の文字列 src: 連結元の文字列 戻り値 dst を返す dst が十分な大きさでないとバッファオーバーフローを引き起 こすので注意 19 教科書 p.309., [1] pp JM: strcat (3)strcat
strncat 関数 char *strncat(char *dst, const char *src, size_t n); 文字列 dst の末尾に文字列 src を最大 n 文字連結する 引数 : dst: 連結先の文字列 src: 連結元の文字列 n: 連結する最大文字数 戻り値 dst を返す dst は strlen(dst)+min(strlen(stc),n)+1 の大きさがな ければバッファオーバーフローが発生する 20 教科書 p.309., [1] pp JM: strcat (3)strcat
strcmp 関数 char *strcmp(const char *s1, const char *s2); 文字列 s1 と s2 を比較する 引数 : s1: 比較する文字列 1 s2: 比較する文字列 2 戻り値 s1 が s2 に比べて小さければマイナスの値を返す s1 と s2 が等しければ 0 を返す s1 が s2 に比べて大きければプラスの値を返す 21 教科書 p.309., [1] pp JM: strcmp (3)strcmp
strncmp 関数 char *strncmp(const char *s1, const char *s2, size_t n); 文字列 s1 と s2 の先頭 n 文字を比較する 引数 : s1: 比較する文字列 1 s2: 比較する文字列 2 n: 比較する最大文字数 戻り値 strcmp に同じ 22 教科書 p.310., [1] pp JM: strcmp (3)strcmp
strchr 関数 char *strchr(const char *s, char c); 文字列 s のなかで最初に現れる c を見つける 引数 : s: 検索する文字列 c: 検索する文字 戻り値 最初に見つかった c へのポインタを返す 見つからなかった場合は NULL を返す 23 教科書 p.312., [1] pp JM: strchr (3)strchr
strrchr 関数 char *strrchr(const char *s, char c); 文字列 s のなかで最後に現れる c を見つける 引数 : s: 検索する文字列 c: 検索する文字 戻り値 最後に見つかった c へのポインタを返す 見つからなかった場合は NULL を返す 24 教科書 p.313., [1] pp JM: strchr (3)strchr
strspn 関数 int strspn(const char *s, const char *t); 文字列 s の先頭から、 t に含まれる文字だけからなる文字列の長 さを返す 引数 : s: 検索する文字列 t: 検索する文字の集合 戻り値 t に含まれる文字からなる接頭子 (prefix) の長さを返す 25 [1] pp JM: strspn (3)strspn
strcspn 関数 int strcspn(const char *s, const char *t); 文字列 s の先頭から、 t に含まれない文字だけからなる文字列の 長さを返す 引数 : s: 検索する文字列 t: 検索する文字の補集合 戻り値 t に含まれない文字からなる接頭子 (prefix) の長さを返す 26 [1] pp JM: strspn (3)strspn
strpbrk 関数 char *strpbrk(const char *s, const char *t); 文字列 s のなかで、 t に含まれる文字が最初に出てくる位置を見 つける 引数 : s: 検索する文字列 t: 検索する文字の集合 戻り値 t に含まれる文字が最初に見つかった位置へのポインタを返す 見つからなかった場合は NULL を返す 27 [1] pp JM: strpbrk (3)strpbrk
strstr 関数 char *strstr(const char *s, const char *t); 文字列 s のなかで、文字列 t が最初に現れる位置を見つける 引数 : s: 検索する文字列 t: 検索する文字列 戻り値 文字列 t が最初に見つかった位置へのポインタを返す 見つからなかった場合は NULL を返す 28 教科書 p.314., [1] pp JM: strstr (3)strstr
strtok 関数 char *strtok(char *s, const char *t); 文字列 s のなかから、 t に含まれる文字で区切られるトークンを 見つける 引数 : s: 検索する文字列 t: トークンを区切る文字の集合 戻り値 トークンへのポインタを返す 2 つ目以降のトークンは s を NULL にして呼ぶことで順次切り 出される トークンがなくなったらは NULL を返す s は区切り文字の位置が終端文字 '\0' で破壊される 29 [1] pp JM: strtok (3)strtok
memset 関数 void *memset(void *s, int c, size_t n); s の先頭から n バイトを c で埋め s を返す 引数 : s: データで埋める先頭アドレス c: メモリを埋める 1 バイトのデータ n: データ c を埋めるバイト数 戻り値 s を返す 要 string.h 30 [1] pp JM: memset (3)memset
memcpy 関数 void *memcpy(void *dst, const void *src, size_t n); dst に src の内容を n バイトコピーする dst と src は領域が重なっていてはならない。重なっている場合 は memmove を用いる 引数 : dst: データのコピー先 src: データのコピー元 n: コピーするバイト数 戻り値 dst を返す 要 string.h 31 [1] pp JM: memcpy (3)memcpy
memmove 関数 void *memmove(void *dst, const void *src, size_t n); dst と src は領域が重なっていても良い点を除けば、 memcpy と同 じ 引数 : dst: データのコピー先 src: データのコピー元 n: コピーするバイト数 戻り値 dst を返す 要 string.h 32 JM: memmove (3)memmove [1] pp
memcmp 関数 int memcmp(const void *s1, const void *s2, size_t n); s1 と s2 の最初の n バイトを比較する 引数 : s1: 比較するメモリ 1 s2: 比較するメモリ 2 n: 比較するバイト数 戻り値 strcmp に同じ 要 string.h 33 JM: memcmp (3)memcmp [1] pp
memchr 関数 void *memchr(const void *s, int c, size_t n); s の中で c が最初に現れる位置を見つける 引数 : s: 検索するメモリ c: 検索する値 n: 検索する範囲のバイト数 戻り値 c が最初に見つかった位置へのポインタを返す 見つからなかった場合は NULL を返す要 string.h 34 JM: memchr (3)memchr [1] pp
標準ヘッダ // 文字列を数値に変換 double atof(const char *s); // 文字列を double 型の数値に変換 int atoi(const char *s); // 文字列を int 型の数値に変換 long atol(const char *s); // 文字列を long 型の数値に変換 double strtod(const char *s, char **endp); long strtol(const char *s, char **endp, int base); unsigned long strtoul(const char *s, char **endp, int base); // 文字列を double, long, unsigned long 型の数値に変換する // endp が NULL でない場合、 // *endp は変換に用いた最後の文字の次の文字へのポインタとなる // base は変換元の文字列の基数 // base が 0 の場合、文字列先頭の 0 や 0x を解釈し // 8 進数や 16 進数として扱う 35 [1] pp JM: atof (3)atof atoiatoi (3) strtodstrtod (3) strtolstrtol (3)
シーザー暗号 文字列の演習 36
シーザー暗号 アルファベットを N 文字シフトする暗号 例 : IBM→1 文字左へ →HAL 37
演習 : caesar.c 与えられた文字列をシーザー暗号で暗号化する関数 caesar(n,s) を作成せ よ。 文字コードが 0x20 ~ 0x7e の文字ついてのみ処理し、それ以外の文字につい ては処理せずそのまま残すこと。 0x20 ~ 0x7e からはみ出す値は値域の反対側に繋がるよう循環するよう変換 せよ。例えば n=1 なら 0x61('a') は 0x62('b') に、 0x7e は 0x20 に変換され る変換される。 n=-1 の場合も同様で、 0x20 は 0x7e に変換される。 caesar_main.c と共にコンパイルして動作を確認せよ 引数 : int n : 暗号鍵 ( シフトする文字数 ) char s[] : 暗号化する文字列 ( 終端が '\0') 戻り値 なし (void) 与えた配列 s を上書きしてシーザー暗号で 暗号化した文字列で上書きせよ 38 mintty + bash + GNU C $ gcc caesar_main.c caesar.c &&./a n = 1 s = hal ibm
ヒント プロトタイプ宣言の末尾の ; を {} に変え れば関数の定義になる。 文字の末尾は \0 で終端されている。 i を増やしながら s[i] != '\0' の間ループを 続ければ良い。 s 全体を暗号文で上書きするには各 s[i] を暗号化して s[i] に戻してやれば良い。 39 caerar_main.c void caesar(int n, char s[]);4
ヒント 0x20 未満、 0x7e 超になった場合の処理は 幾つかやり方がある if 文等で場合分けする方法 剰余算を使う方法 等々 40
参考文献 [1] B.W. カーニハン /D.M. リッチー著 石田晴 久 訳、プログラミング言語 C 第 2 版 ANSI 規 格準拠、共立出版 (1989) 41