文字列を扱う関数を作る
以下に紹介する関数は、Cの標準ライブラリに収録されているものと同じ名前ですが、内部の動作や使用結果はまったく同じではありません。また、対象とする文字列は1バイト(半角)文字の英数記号の集合という前提です。日本語などの2バイト文字は対象としていません(日本語の扱いについては、回を追って取り上げる予定です)。
strlen~文字列の長さ(文字数、バイト数)を調べる(リスト1)
・宣言 unsigned long int strlen(const char *);
・機能 引数に文字列へのポインタを受け取り、そのバイト数を返します。
戻り値はunsigned long int(符号なし長整数)型としています。単純に"int"型とすると、処理系やプラットフォームによってshortまたはlongとして扱われ、ソースコードとしては曖昧な表現となります。文字の長さ(文字数)のように正の整数であることが明白な値は符号なし(unsigned)と宣言しておく方が確実です。
こういった『何らかの数』を扱う処理のために、ANSI-C ※1 では以下のようにして"size_t"型を定義しています。
typedef unsigned long int size_t;
size_t型は型の占有するバイト数を調べるsizeof演算子の戻り値などに用いられ、stdio.h、stdlib.hなどのヘッダファイルで宣言されています。
処理の内容は、リストのコメント(/*~*/)を読んでいただければお分かりでしょう。whileループで引数の示す文字列の終端('\0')まで1文字ずつポインタを進め、その回数をカウンタ変数iで数えています。
whileループ内でポインタsをインクリメントしている部分は、以下のようにwhileの条件式にまとめることもできます。
while (*s++ != '\0') { i++; }
ANSI(American National Standards Institute:米国規格協会)の定めたCの標準規格
リスト1:strlen関数
unsigned long int strlen(const char * s) { unsigned long int i; /* カウンタ */
i = 0; /* カウンタを初期化 */ while (*s != '\0') { /* 文字列の終端まで繰り返す */ s++; /* ポインタを進める */ i++; /* カウンタを加算 */ } return (i); /* カウンタを返す */ }
strcpy~文字列のコピー(リスト2~4)
・宣言 char * strcpy(const char *, char *);
・機能 引数に文字列を示す2つのポインタを採り、第1引数の示す文字列を第2引数のポインタが示す場所にコピーします。
戻り値はコピー後の文字列を示すポインタ(第2引数)とします。
コピー元をs、コピー先をdとしています。sはsource(元、出所)、dはdestination(宛先、目的地)の略です。sの示す文字列を1文字ずつdの示す場所にコピーし、dにコピーされた最後の文字の次(文字列の終端)にNULLを代入します。
リスト2のwhileループでは、処理の流れが分かるよう冗長な書き方をしています。
while (*s != '\0') { *d = *s; d++; s++; }
リスト3では、sからdへの値の代入とポインタのインクリメントとを1行にまとめています。
while (*s != '\0') { *d++ = *s++; ↑ 1文字コピーしてポインタを進める }
さらにリスト4では、それらすべての処理をwhileの条件式の中に記述し、{ }内の記述をなくしています。
while ((*d++ = *s++) != '\0') { }
さすがに、ここまで簡略化するとソースが読みにくくなります。C独特の書き方に慣れてしまえばどうということはありませんが、できればリスト3程度の簡略化で済ませておいた方が分かりやすいソースとなるでしょう。
リスト2:strcpy関数~冗長な書き方
char * strcpy(const char *s, char *d) { char *r; r = d; while (*s != '\0') { /* コピー元の終端まで繰り返す */ *d = *s; /* 1文字コピー */ d++; /* コピー先のポインタを進める */ s++; /* コピー元のポインタを進める */ } *d = '\0'; /* 終端のNULLをセット */ /* このときdは最後の文字の次を示している */ return (r); /* 本来なら「失敗時にNULLを返す」ようにするべきだが省略 */ }
リスト3:strcpy関数~やや冗長な書き方
char * strcpy(const char *s, char *d) { char *r; r = d; while (*s != '\0') { *d++ = *s++; /* 1文字コピーしてポインタを進める */ } *d = '\0'; return (r); /* 本来なら「失敗時にNULLを返す」ようにするべきだが省略 */ }
リスト4:strcpy関数~シンプルな書き方
char * strcpy(const char *s, char *d) { char *r; r = d; while ((*d++ = *s++) != '\0') { /* 条件式の中でコピーとポインタのインクリメントを行う */ } *d = '\0'; return (r); /* 本来なら「失敗時にNULLを返す」ようにするべきだが省略 */ }
strcat~文字列の連結(リスト5)
・宣言 char * strcat(char *, const char *)
・機能 引数に文字列を示す2つのポインタを採り、第1引数の示す文字列の末尾に第2引数の示す文字列を連結します。
戻り値は連結先のポインタ(第1引数)とします。
仮引数名はs1とs2とし、s1の示す文字列の最後にs2の示す文字列を連結します。処理の最後にs1を戻り値として返すため、処理の中でs1の値を書き換えてはいけません。そのため、char *型の作業用ポインタpを用意し、それにs1の値を代入して処理を行います。
もしs1をそのまま使って処理を行うと、whileループでs1を進めていった結果、戻り値であるs1の値は処理前と異なり、文字列の最後尾(NULL)を指した状態となってしまいます。
リスト5:strcat関数
char * strcat(char *s1, const char *s2) { char *p; /* 作業用のポインタ */ p = s1; /* s1の値を受け取る */
/* p(s1)の末尾までポインタを進める */ while (*p != '\0') { p++; } /* p(s1)の末尾にs2をコピー */ while (*s2 != '\0') { *p++ = *s2++; } *p = '\0'; return (s1); }
|