【C言語】
strcpy()をstrNcpy()に置き換えても安全とは限らない

strcpyより strncpyが 安全?

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()の使い方が混乱し
メモリ破壊が発生したり
暴走が発生したりします。