warning: argument to ‘sizeof’ in ‘strncpy’ call is the same expression as the source;
警告:転送元のサイズではなく転送先のサイズでは?
[-Wsizeof-pointer-memaccess]
■一文字コピー関数使う意味がない
static void 一文字コピー関数使う意味がない(void){
char dst[] = "1234567890123456" ;
strncpy(dst,"#",1); //dst[0] = '#'と何が違う?
PUTS(dst);
}
1文字しか転送しないのにstrncpyを使う人がいますが、
このコードの場合dst[0]=’#’と何が違うでしょうか?
■一文字代入=単純早い事故らない
static void 一文字コピー単純早い事故らない(void){
char dst[] = "1234567890123456" ;
dst[0] = '#';
PUTS(dst);
}
strncpy(dst,”#”,1);ではなくて
dst[0] = ‘#’;と書く事を推奨します。
コーディング量も少なく、
処理速度も高速で、
メモリ破壊の心配もありません。
■終端文字を入れないのは意図的か
static void 終端文字を入れないのは意図的か(void){
char dst[] = "1234567890123456" ;
strncpy(dst,"src",3);//終端文字\0を忘れたのか意図的なのか分からない
PUTS(dst);
}
コピー元の長さが第三引数と同じなので
コピー先には終端文字\0が設定されません。
プログラマが意図的に終端文字を抜いたのか
うっかり終端文字の考慮を忘れたのか
読み手にはわかりにくいです。
■コピー先のサイズが小さくても第三引数byte書き込むのでメモリ破壊
static void コピー先のサイズが小さくても第三引数byte書き込みメモリ破壊(void){
char dst[] = "1234567890123456" ;
strncpy(dst,"src",4096);//コピー先サイズを無視して4096byteとにかく埋める
PUTS(dst);
}
コピー先のサイズ16+1byte
コピー元のサイズ3+1byte
なので
問題なさそうですが、
コピー先を4096byteになるまで終端文字¥0で埋めるのでメモリ破壊が発生します。
■CとC++で動作が違う文字定数のサイズ
static void CとCPPで動作が違う文字定数のサイズ(void){
char dst[] = "1234567890123456" ;
strncpy(dst,"#",sizeof('#'));
PUTS(dst);
}
sizeof(‘#’)の結果は
C言語では4
C++では1なので動作が変わります。
■文字定数は1byteじゃない
static void 文字定数は1byteじゃない(void){
char dst[2] ={'\0'};
strncpy(dst,"#",sizeof('#'));//メモリ破壊する第3引数は1ではなく4
PUTS(dst);
}
sizeof(‘#’)の結果はC言語では4byteです。
このコードでは転送先は2byteしかないので
メモリ破壊が発生します。
■終端文字を入れたいか入れたくないかsizeof
static void 終端文字を入れたいか入れたくないかsizeof(void){
char dst[] = "1234567890123456" ;
strncpy(dst,"#",sizeof("#")); //sizeof("#")は終端文字’\0’を含む
PUTS(dst);
}
sizeof(‘#’)と
sizeof(”#”)とは似ていますが
sizeof(”#”)は’#’+’\0’の2byteとなります。
このコードでは
プログラマが終端文字¥0を
コピー先に入れたいのか?入れたくないのか?
読み手にはわかりにくいです。
■コピーサイズがコピー元でメモリ破壊
static void コピーサイズがコピー元でメモリ破壊(void){
char dst[4];
char src[] = "1234567890123456";
strncpy(dst,src,sizeof(src));//コピー元が大きいのでコピー先を破壊する
PUTS(dst);
}
このコードでは
コピー先のサイズ < コピー元のサイズ
なので
メモリ破壊が発生します。
■コピーサイズがコピー先で尻切れトンボ
static void コピーサイズがコピー先で尻切れトンボ(void){
char dst[4];
char src[] = "1234567890123456";
strncpy(dst,src,sizeof(dst)); //コピー元を尻切れでコピー
PUTS(dst); //終端文字がみつからないので暴走
}
このコードでは
コピー先に終端文字が設定されません。
このためコピー先を文字列として参照すると
終端文字が見つからず暴走します。
■コピーサイズがポインターのサイズ
static void コピーサイズがポインターのサイズ(void){
char dst[1024];
char *src = "1234567890123456";
strncpy(dst,src,sizeof(src));//ポインタのサイズ(ILP32=4,LP64=8)しかコピーしない
PUTS(dst);
}
sizeof(src)と
strlen(src)は似ていますが違います。
このコードの場合
strlen(src)は文字列の長さですが
sizeof(src)はポインタのサイズです
ポインタのサイズは
ILP32=4,LP64=8固定です。
■終端文字の考慮はあるがstrcpyより遅いだけ
static void 終端文字の考慮はあるがstrcpyより遅いだけ(void){
char dst[] = "1234567890123456" ;
char src[] = "src";
strncpy(dst,src,strlen(src)+1);
PUTS(dst);
}
このコードは終端文字を考慮して
strlen(src)+1としているので
プログラマの意図通り動きます。
しかし
strncpy(dst,src,strlen(src)+1);は
strcpy(dst,src);と比較して
処理速度が遅くなるだけで
安全でも何でもありません。
コピー元が長すぎると
strncpy(dst,src,strlen(src)+1);はstrcpy(dst,src);と
同じようにメモリ破壊します。
■終端文字を入れたいか入れたくないかstrlen
static void 終端文字を入れたいか入れたくないかstrlen(void){
char dst[] = "1234567890123456" ;
char src[] = "src";
strncpy(dst,src,strlen(src));
PUTS(dst);
}
strncpy(dst,src,strlen(src));は
終端文字の分転送されません。
プログラマが
意図的に終端文字を抜いたのか、
うっかり忘れたのか
読み手にはわかりにくいです。
■まとめ
strcpy()はメモリ破壊の危険性が高いので使用禁止とし
変わりにstrncpy()を推奨するプロジェクトがあります。
しかしstrncpy()にすれば安全というわけではありません。
一番重要なのは
コピー元より
コピー先の容量が
必要十分大きい事を保証することです。
strncpyの第三引数に何を記述するか
決めているプロジェクトはほぼ皆無、
というか一律に決められません。
単純にstrcpy()を禁止しただけでは
strncpy()の使い方が混乱し
メモリ破壊が発生したり
暴走が発生したりします。