はじめに
先日に書いた「【C】初めてのC言語(25. atoi関数の罠)」では、atoi
関数が(初心者にとっては)予想外の挙動になるという話を書きました。
今回はstrcpy
関数でも同様に初心者にとっては予想外の挙動が見つかったので、その点についてまとめてみました。
strcpy関数の概要
#include
char *strcpy(char *s1, const char *s2);
- こちらのページを見ると、「文字型配列 *s1 に文字列 *s2 を '\0' までコピーします。'\0' もコピーするので s1 はその分も考えて大きさを宣言しておかなければなりません。」と書かれています。
- 平たく言えば、「文字列型配列を終端文字の'\0'までコピーする」ということになります。
strcpy関数の挙動
- 以下のコードを実行すると、コピー先の
buff
の長さは3バイトであるにもかかわらず、長さが5バイトのtext
の中身が全てコピーされてしまっています。 - このように「コピー元の配列のサイズ > コピー先の配列のサイズ」という条件の時に
strcpy
関数を使うと、「コピー先の配列のバッファサイズを超えた書き込み」が実行されてしまいます。 - これは以前にも少し勉強したバッファオーバーフロー(バッファオーバーラン)という現象です。
strcpy関数を使った例
#include <stdio.h>
int main(void){
char text[5] = "abcd";
char buff[3];
strcpy(buff, text);
printf("buffの値:%s\n", buff);
}
実行結果
buffの値:abcd
改善策
コピー前に配列の長さをチェックする
- 以下のコードでは、
strcpy
関数を呼び出す前にコピー先の配列の長さが十分であるかをチェックしています。 - 以下のコードではコピー先の配列
buff
の長さが不十分なので、strcpy
関数が呼び出されずに処理が終了しています。-
char buff[5];
とすると、正常にコピー処理が行われます。
-
- JPCERTのページを見ると、コピー元のNULLチェックも入れているので、以下のコードにもNULLチェックをいれておきました。
コピー前に配列の長さをチェックする例
#include <stdio.h>
int main(void){
char text[5] = "abcd";
char buff[3];
if (text != NULL && strlen(text) < sizeof(buff)) {
strcpy(buff, text);
}
printf("buffの値:%s\n", buff);
}
実行結果
buffの値:
strncpy関数を使う
-
strncpy
関数は長さを指定して文字列をコピーできる関数なので、以下のコードでは「コピー先の配列の長さで収まるように末尾を切り落としてコピー」しています。 - 長さを
sizeof(buff)
としてしまうと、コピー先に終端文字\0
が入らないので、sizeof(buff)-1
としています。 - 末尾が削られてしまってもOKであれば、strncpy関数を使うのも良さそうです。
strncpy関数を使う例
#include <stdio.h>
int main(void){
char text[5] = "abcd";
char buff[3];
if (text != NULL) {
strncpy(buff, text, sizeof(buff)-1);
}
printf("buffの値:%s\n", buff);
}
実行結果
buffの値:ab