strlen() / strcpy() / strncpy()
| 対応: | C89(1989) |
|---|
文字列の長さを取得したり、別のバッファへコピーしたりする基本関数です。バッファサイズを超えた書き込みはセキュリティ上の重大な脆弱性になるため、境界の扱いに細心の注意が必要です。
構文
// 文字列の長さをヌル文字を除いたバイト数で返します size_t strlen(const char *s); // src の内容を dst にヌル文字ごとコピーします // dst と src のメモリが重なってはいけません char *strcpy(char *dst, const char *src); // src の最大 n バイトを dst にコピーします // src が n バイト未満の場合は残りをヌル文字で埋めます // src が n バイト以上の場合はヌル文字が付加されません(注意が必要) char *strncpy(char *dst, const char *src, size_t n);
関数比較
| 関数 | バッファ保護 | ヌル終端保証 | 用途 |
|---|---|---|---|
| strcpy() | なし | あり | dst が十分大きいことが保証されている場合のみ使えます。 |
| strncpy() | あり(サイズ指定) | なし(n以内でも保証なし) | バッファサイズを制限できますが、ヌル終端を手動で付ける必要があります。 |
| strlcpy()* | あり(サイズ指定) | あり | BSD拡張。常にヌル終端を保証する安全な代替です(標準Cではありません)。 |
| snprintf() | あり(サイズ指定) | あり | 常にヌル終端を保証します。コピーにも使えます。 |
サンプルコード
strlen_strcpy_strncpy.c
#include <stdio.h>
#include <string.h>
int main(void) {
const char *src = "Hello, C!";
printf("長さ: %zu\n", strlen(src));
char dst1[32];
strcpy(dst1, src);
printf("strcpy: %s\n", dst1);
char dst2[6];
strncpy(dst2, src, sizeof(dst2) - 1); /* n に sizeof-1 を渡します */
dst2[sizeof(dst2) - 1] = '\0'; /* 手動でヌル終端を保証します */
printf("strncpy(5): %s\n", dst2);
/* snprintf はヌル終端を自動で保証します。 */
char dst3[8];
snprintf(dst3, sizeof(dst3), "%s", src);
printf("snprintf: %s\n", dst3);
return 0;
}
コンパイルして実行すると次のようになります。
gcc strlen_strcpy_strncpy.c -o strlen_strcpy_strncpy ./strlen_strcpy_strncpy 長さ: 9 strcpy: Hello, C! strncpy(5): Hello snprintf: Hello,
文字列の末尾の改行を取り除く
fgets() で読み込んだ文字列の末尾には改行文字が残ることがあります。strlen() と strcpy() を組み合わせて取り除く実用的なパターンです。
strlen_trim_newline.c
#include <stdio.h>
#include <string.h>
/* 末尾の改行文字を除去します */
void trim_newline(char *s) {
size_t len = strlen(s);
if (len > 0 && s[len - 1] == '\n') {
s[len - 1] = '\0';
}
}
int main(void) {
char buf[64];
/* fgets で読み込んだ文字列には末尾に \n が含まれます */
const char *input = "Kiryu Kazuma\n";
strncpy(buf, input, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\0';
printf("trim前: '%s'\n", buf);
trim_newline(buf);
printf("trim後: '%s'\n", buf);
return 0;
}
コンパイルして実行すると次のようになります。
gcc strlen_trim_newline.c -o strlen_trim_newline ./strlen_trim_newline trim前: 'Kiryu Kazuma ' trim後: 'Kiryu Kazuma'
よくあるミス
よくあるミス: strcpy のバッファオーバーフロー
strcpy() はコピー先バッファのサイズを確認しません。src が dst より長い場合はバッファオーバーフローが発生します。
#include <stdio.h>
#include <string.h>
int main(void) {
char dst[5];
const char *src = "Hello, World!"; /* 13文字 + ヌル文字 */
/* NG: dst が小さすぎてバッファオーバーフロー */
strcpy(dst, src);
return 0;
}
strcpy_overflow_ok.c
#include <stdio.h>
#include <string.h>
int main(void) {
char dst[5];
const char *src = "Hello, World!"; /* 13文字 + ヌル文字 */
/* OK: snprintf でサイズを制限する */
snprintf(dst, sizeof(dst), "%s", src);
printf("OK: %s\n", dst);
return 0;
}
修正後は次の通りです。
gcc strcpy_overflow_ok.c -o strcpy_overflow_ok ./strcpy_overflow_ok OK: Hell
概要
strcpy() はコピー先バッファのサイズを一切チェックしません。src が dst より長い場合はバッファオーバーフローが発生し、隣接メモリを破壊してセキュリティ脆弱性になります。現代的なコードでは snprintf() を使うことが推奨されます。
strncpy() は n 文字以内に src が収まらない場合、ヌル終端を付加しません。必ず末尾を手動でヌル文字にする(dst[n-1] = '\0')か、snprintf() を代替として使ってください。
文字列の連結には『strcat() / strncat()』、比較には『strcmp()』を参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。