🌊

【C言語】strncpy の使い方

2023/07/16に公開

「strncpy」とは何か

シンボル 意味
str 「string(文字列)」の略称
n コピーするサイズの上限
cpy 「copy」の略称

すなわち、「strncpy」とは、

「コピーするサイズの上限を指定して文字列をコピーする」

というC言語の標準関数である。

「strncpy」の使い方

char filepath[8];
strncpy(filepath, __FILE__, sizeof(filepath));
filepath[sizeof(filepath)-1] = '\0';

これは、char型の配列に「__FILE__」を設定するコードである。
__FILE__」は文字列の長さが特定できない。かと言って、char型の配列をむやみに大きく確保するわけにもいかない。 「__FILE__」が長すぎる場合は先頭の特定数を取り出せればよい。
2行目の「strncpy」は「filepath」の長さまでをコピーしてくれるため、ちょうどいい。

ポイントは最後の行である。

filepath[sizeof(filepath)-1] = '\0';

strncpy」は、コピーするサイズの上限内に終端(\0)が見つからない場合、コピー先に終端(\0)を含まない
コピーされた文字列に終端(\0)を含まないことがあるというのは致命的である。「文字列」を定義すると終端されているという前提で設計実装することが多い。このため、ここの最終行にあるように必ず終端しておく。

clang の strncpy

clangstrncpyを試してみた。
すると、少々意外な結果が出た。

0x7ffed1cc58 30 31 32 33 34 35 36 37 38 39 00

0x7ffed1cc64 30 31 32 33 34 35 36 37 38 55 55 55
0x7ffed1cc70 30 31 32 33 34 35 36 37 38 39 55 55
0x7ffed1cc7c 30 31 32 33 34 35 36 37 38 39 00 55
0x7ffed1cc88 30 31 32 33 34 35 36 37 38 39 00 00
0x7ffed1cc94 30 31 32 33 34 35 36 37 38 39 00 00
0x7ffed1cca0 00 55 55 55 55 55 55 55 55 55 55 55

先頭行は、コピー元文字列である。
"0123456789"の10文字と、終端(\0)のデータが並んでいる。

空行をはさんで順に
9文字、10文字、11文字、12文字、13文字
をコピーした結果である。

コピー先文字列はコピー前に「0x55」で埋めつくしておいた。
ようするに、次のような結果になったわけである。

文字数 コピー結果
9文字 "012345678"
10文字 "0123456789"
11文字 "0123456789"終端(\0)
12文字 "0123456789"終端(\0)終端(\0)
13文字 "0123456789"終端(\0)終端(\0)終端(\0)

11文字までは問題ない。
問題は、12文字と13文字である。
指定した文字数に達するまで終端(\0)で埋めているようなのである。
コピーは終端(\0)までではないのか?

strncpy の歴史

昔、この標準関数はなかった。
文字列をコピーする標準関数と言えば「strcpy」だけだったのである。長年、このような文字列コピーの脆弱性が問題視されてきた。「strcpy」の場合、上記のサンプルの「__FILE__」のように長さを特定し難い文字列をコピーするのは不安である。更に、オーバーランさせてしまう不具合も少なくなかった。
いっそのことC言語の仕様から「strcpy」をなくしてしまいたかったかもしれないが、C言語の歴史は長い。古いコードを使い続けているソフトウェアも少なくない。「strcpy」はまだ残っているかもしれないが、可能な限り「strncpy」を使うのが望ましい。

GitHubで編集を提案

Discussion