■この記事の概要
この記事では、C言語のstrncpy
とmemcpy
の違いを解説しています。strncpy
は終端文字\0
を含めた文字列操作を行い、不足部分を埋めますが、memcpy
は終端文字を考慮せず指定サイズを転送します。誤用によるバグや安全な使い方について、具体例を交えて説明しています。
■strNcmpとmemcmpが同じ例
#include <stdio.h>
#include <string.h>
char s1[] = "abc" ;
char s2[] = "abc" ;
int main(void){
if(strncmp(s1,s2,sizeof(s1)) == 0){
puts("同じ:ここに来る");
} else {
puts("違う:来ない");
}
if(memcmp(s1,s2,sizeof(s1)) == 0){
puts("同じ:ここに来る");
} else {
puts("違う:来ない");
}
}
同じ動きをする時もあります。
■strNcmpとmemcmpが違う例
#include <stdio.h>
#include <string.h>
char s1[] = "abc\0DEF" ;//abc+DEF
char s2[] = "abc\0XYZ" ;//abc+XYZ
int main(void){
if(strncmp(s1,s2,sizeof(s1)) == 0){
puts("同じ:ここに来る");
} else {
puts("違う:来ない");
}
if(memcmp(s1,s2,sizeof(s1)) == 0){
puts("同じ:来ない");
} else {
puts("違う:ここに来る");
}
}
●違いその1
strncmp()は終端文字¥0までしか比較しない
memcmp()は終端文字¥0を超えて比較する。
●違いその2
strncmp()の第1,第2引数には char * 型しか渡せません。
(キャストしてコンパイラを騙し警告を無視するのは論外とします)
memcmp()の第1,第2引数はvoid *型なのでデータのアドレスならば
何でも渡せます。
●実行結果
./a.out
同じ:ここに来る
違う:ここに来る
■strNcpyとmemcpyが違う例
//gcc -g f1.c -fsanitize=address
#include <stdio.h>
#include <string.h>
static char コピー元小[8] = "abc\0xyz";
static char コピー先大[16];
int main(void){
//'\0'を終端とするstr系
strncpy(コピー先大,コピー元小,sizeof(コピー先大));
printf("%s:%s\n",&コピー先大[0],&コピー先大[4]);
//'\0'を終端としないmem系(バグあり)
memcpy(コピー先大,コピー元小,sizeof(コピー先大));//★
printf("%s:%s\n",&コピー先大[0],&コピー先大[4]);
}
●違いその1
strncpy()は終端文字¥0までしか転送しません。
memcpy()は終端文字¥0を超えて指定のサイズまで転送します。
●違いその2
第2引数が第3引数より短い場合、
strncpy()は残りを¥0で埋めます。
memcpy()は第2引数で領域外参照が発生して動作不明となります。
※このプログラムの例では転送元は8byteしかないのに16byte読み込むので領域外参照のバグとなります。
gcc -g -fsanitize=address でコンパイルすると異常終了しました。
■文字列比較にmemcmpを使ってバグった例
#include <stdio.h>
#include <string.h>
int main(void){
char str1[1024]="ABCD";
char str2[1024];
strcpy(str2,"ABCD");
puts("memcmp");
if(memcmp(str1,str2,sizeof(str1)) == 0){
printf("%sと%sは同じ\n",str1,str2);
}else{
printf("%sと%sは違う\n",str1,str2);
}
puts("strncmp");
if(strncmp(str1,str2,sizeof(str1)) == 0){
printf("%sと%sは同じ\n",str1,str2);
}else{
printf("%sと%sは違う\n",str1,str2);
}
}
(1)str1[1024]は先頭の”ABCD”と残り全部が0保証されます。
(2)str2[1024]は先頭の”ABCD”と終端文字の\0が保証されますが、
残りは全部ゴミの値です。
(3)memcmpは1024byte全部比較するので
ゴミの内容で結果が変わります。
(4)strncmpは先頭5byte(ABCDと終端文字\0)を比較して終了するのでstr1とstr2は同じ文字列となります。
➡str1,str2のサイズ1024を小さくしてみましょう。
結果がコロコロ変わる
非常に恐ろしいバグと分るでしょう。
参考:
C-FAQ 13.2:任意のバイトをコピーするときには、普通は memcpy()のほうがstrncpy()よりも適切なルーチンである。
http://www.kouno.jp/home/c_faq/c13.html#2