文字列を扱う関数を作る
以下に紹介する関数は、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);
}
|