[C言語のはじめ方] Part23: 文字列操作(strlen, strcpy, strcat)

C言語

Step 5: 文字列操作(strlen, strcpy, strcat)マスターへの道 🚀

こんにちは!C言語学習者の皆さん。今回は、プログラミングで非常によく使う「文字列操作」の中でも、特に重要な3つの関数、strlenstrcpystrcatについて学んでいきましょう。

ファイル操作やユーザーからの入力処理など、多くの場面で文字列を扱う必要があります。これらの関数を使いこなせるようになると、できることの幅がぐっと広がりますよ!💪

前提知識: この記事を読む前に、C言語の基本的な文法(変数、データ型、配列、ポインタの基礎)を理解していると、よりスムーズに学習を進められます。特に、C言語における文字列が「文字(char)の配列」であり、末尾にヌル文字(\0)が付いているというルールを思い出しておきましょう。[3, 19]

文字列操作関数の準備:string.h

これらの関数を使うには、まずプログラムの冒頭でstring.hというヘッダーファイルをインクルードする必要があります。これは、C言語の標準ライブラリに含まれるヘッダーファイルで、文字列操作に関する様々な便利な関数が定義されています。[3, 7, 20, 21, 22, 23, 24]

#include <stdio.h>
#include <string.h> // ← これを追加!

int main() {
  // ここにプログラムを書く
  return 0;
}
  

これで準備はOKです!早速、各関数を見ていきましょう。

1. 文字列の長さを知る:strlen()

strlen() (エス・ティー・アール・レン) 関数は、指定された文字列の長さ(文字数)を返します。ただし、重要な注意点として、この長さには文字列の終わりを示すヌル文字(\0)は含まれません。[1, 14, 19, 23]

使い方

#include <string.h>

size_t strlen(const char *str);
  
  • 引数 str: 長さを知りたい文字列(へのポインタ)。
  • 戻り値 size_t: 文字列の長さ(ヌル文字を含まない文字数)。size_t は符号なし整数型です。[21]

コード例

#include <stdio.h>
#include <string.h>

int main() {
  char my_string[] = "Hello C!"; // ヌル文字含めて9文字分の配列
  size_t length = strlen(my_string);

  printf("文字列: %s\n", my_string);
  printf("長さ (strlen): %zu\n", length); // %zu は size_t 型の出力に使う書式指定子

  // 配列全体のサイズ (ヌル文字含む) と比較
  printf("配列サイズ (sizeof): %zu\n", sizeof(my_string));

  return 0;
}
  

実行結果

文字列: Hello C!
長さ (strlen): 8
配列サイズ (sizeof): 9
  

実行結果を見ると、strlenが返した長さは8で、これはヌル文字を除いた文字数と一致していますね。一方、sizeof演算子は配列全体のメモリサイズ(ヌル文字\0を含む)である9を返しています。

⚠️ strlen() の注意点

strlen() は、文字列のヌル文字 \0 を探して長さを数えます。もし、渡された文字配列の途中にヌル文字がなかったり、そもそもヌル文字で終わっていなかったりすると、strlen() はメモリの範囲を超えてヌル文字を探し続け、予期せぬ動作(最悪の場合、プログラムのクラッシュ)を引き起こす可能性があります。 これは非常に危険な状態です。文字列を扱う際は、必ずヌル文字で終わっていることを確認しましょう。

2. 文字列をコピーする:strcpy()

strcpy() (エス・ティー・アール・コピー) 関数は、ある文字列を別の場所にコピーするために使います。例えば、ユーザーが入力した文字列を変数に保存したり、既存の文字列を加工するために別の場所に複製したりする場合に役立ちます。[4, 7, 12, 13, 15, 16, 23]

使い方

#include <string.h>

char *strcpy(char *dest, const char *src);
  
  • 引数 dest: コピーの文字列(を格納するchar配列へのポインタ)。
  • 引数 src: コピーの文字列(へのポインタ)。
  • 戻り値 char *: コピー先の文字列へのポインタ (dest と同じ値)。[12]

strcpy() は、src が指す文字列を、ヌル文字 \0 を含めて dest が指す場所にコピーします。[12, 15]

コード例

#include <stdio.h>
#include <string.h>

int main() {
  char source_string[] = "Learning C";
  char destination_string[20]; // コピー先は十分なサイズを確保!

  // source_string の内容を destination_string にコピー
  strcpy(destination_string, source_string);

  printf("コピー元: %s\n", source_string);
  printf("コピー先: %s\n", destination_string);

  return 0;
}
  

実行結果

コピー元: Learning C
コピー先: Learning C
  

コピー元の文字列が、コピー先の配列に正しくコピーされましたね。

🚨 strcpy() の重大な注意点:バッファオーバーフロー

strcpy() は非常に便利な関数ですが、使い方を誤るとバッファオーバーフローという深刻な問題を引き起こす可能性があります。[4, 8, 12, 13, 15] これは、コピー先の配列 (dest) のサイズが、コピー元の文字列 (src) の長さ(ヌル文字含む)よりも小さい場合に発生します。

strcpy() はコピー先のサイズをチェックしません。[12] そのため、用意された配列の範囲を超えてメモリに書き込みを行ってしまい、プログラムの他の部分のデータを破壊したり、セキュリティ上の脆弱性を生み出したりする原因となります。これは絶対に避けなければなりません!

対策:
  • コピー先の配列は、コピーする可能性のある最大の文字列長 + ヌル文字1文字分よりも十分に大きなサイズを確保する。
  • 可能であれば、コピーする文字数を制限できる、より安全な関数 strncpy() や、さらに安全性が高いとされる strcpy_s() (ただし、環境によっては使えない場合がある[13, 10]) などの代替関数の使用を検討する。[7, 8, 10, 11, 12] (これらの関数の詳細は、より進んだ学習ステップで扱います)
安全なプログラミングのため、strcpy() を使う際は常にバッファオーバーフローのリスクを意識し、コピー先のサイズを十分に確保するようにしてください。

3. 文字列を連結する:strcat()

strcat() (エス・ティー・アール・キャット) 関数は、ある文字列の末尾に別の文字列を連結(追加)します。「cat」は連結を意味する “concatenate” から来ています。[2, 3, 6, 7, 9, 23]

使い方

#include <string.h>

char *strcat(char *dest, const char *src);
  
  • 引数 dest: 連結の文字列(末尾に追加される)。この文字列はヌル文字で終わっている必要があります。
  • 引数 src: 連結する文字列(dest の末尾に追加される)。
  • 戻り値 char *: 連結後の文字列へのポインタ (dest と同じ値)。

strcat() は、dest 文字列の末尾にあるヌル文字 \0 を探し、その位置から src 文字列の内容(ヌル文字含む)を書き込みます。

コード例

#include <stdio.h>
#include <string.h>

int main() {
  char first_part[50] = "C Language is "; // 連結後も収まる十分なサイズを確保!
  char second_part[] = "powerful!";

  printf("連結前: %s\n", first_part);

  // first_part の末尾に second_part を連結
  strcat(first_part, second_part);

  printf("連結後: %s\n", first_part);

  return 0;
}
  

実行結果

連結前: C Language is
連結後: C Language is powerful!
  

first_part の末尾に second_part がうまく連結されましたね!

🚨 strcat() の重大な注意点:バッファオーバーフロー

strcat()strcpy() と同様に、バッファオーバーフローのリスクがあります。[2, 6, 9] これは、連結先の配列 (dest) のサイズが、元の文字列の長さ + 連結する文字列 (src) の長さ + ヌル文字1文字分よりも小さい場合に発生します。

strcat() も連結先のサイズをチェックしません。 用意された配列の範囲を超えてメモリに書き込んでしまう可能性があり、非常に危険です。

対策:
  • 連結先の配列は、連結後の文字列が確実に収まるように、十分に大きなサイズを確保する。事前に strlen() で各文字列の長さを確認し、合計サイズを計算してから連結するのが安全です。[6, 3]
  • 可能であれば、連結する文字数を制限できる、より安全な関数 strncat() や、さらに安全性が高いとされる strcat_s() (ただし、環境によっては使えない場合がある[9, 10]) などの代替関数の使用を検討する。[3, 6, 7, 10, 11] (これらの関数の詳細は、より進んだ学習ステップで扱います)
strcat() を使用する際も、常にバッファオーバーフローのリスクを念頭に置き、連結先の配列サイズに細心の注意を払いましょう。

まとめと次のステップ

今回は、C言語の基本的な文字列操作関数である strlen, strcpy, strcat について学びました。

  • strlen(): 文字列の長さ(ヌル文字含まず)を取得する。
  • strcpy(): 文字列をコピーする(⚠️バッファオーバーフローに注意!)。
  • strcat(): 文字列を連結する(⚠️バッファオーバーフローに注意!)。

特に strcpystrcat は、便利ですがバッファオーバーフローという重大なリスクを伴います。安全なプログラミングのためには、コピー先・連結先の配列サイズを常に意識し、十分な大きさを確保することが非常に重要です。💡

これらの関数は文字列処理の基本となります。使い方と注意点をしっかり理解し、安全に活用できるようになりましょう!

次は、「Step 5: 数値変換・標準関数(atoi, atof, rand)」に進み、文字列を数値に変換する方法や乱数の使い方などを学びます。お楽しみに!😊

コメント

タイトルとURLをコピーしました