source 文字列から最大 n 文字コピーするが、 コピー文字列はそこで terminate する話

命題

source 文字列から n 文字コピーする。
source 文字列が n 文字以下ならば全ての文字をコピーし、 そこで文字列は terminate する。
source 文字列が n 文字より多いならば n 文字コピーし、 コピー文字列はそこで terminate する。
そのような文字列コピー関数を作れ。

これは X11 の Source にあったプログラムが用意していた関数の一つです。

 やることは strncpy() 関数と同じです。 唯一違うのは strncpy() が source 文字列が n 文字以上の場合、 n 文字コピーした後、コピー文字列をそこで terminate しない、 という点だけです。

X11R6 のサンプルコードにあったのは次のようなコードでした。

code 1:
    int    length = strlen( source );
    strncpy( dest, source, n );
    if ( length > n ) {
	dest[n] = '\0';
    }
  

 私はこういうコードの方が良いと思う。

code 2:
   dest[n]	= '\0';
   strncpy( dest, source, n );
  

 length < = n の場合、 code 1 の方は dest に source の純粋なコピーが発生する。 一方 code 2 は source の純粋なコピーの他に、 dest[n] の文字が '\0' つまり terminate 文字に置き換わる。 しかし、どちらの場合も、dest[n] よりも前に、 もう一つ「純粋コピーの結果存在しなくてはいけない」 terminate 文字が存在し、 それが dest 文字列の終端記号として有効になるので、 dest を文字列へのポインターとして参照した場合、 文字列としては同じものが参照され、 結果として code1 と code2 は等価である。

 length > n の場合、 dest[n-1] まで source のコピーが入るのは、 code 1 も code 2 も同じである。 dest[n] = '\0' でなくてはいけないが、 code 2 はデフォルトでその処理を行っているので、 この条件は成立している。 code 1 は source の文字数と n を比較して、 必要な場合に dest[n] に '\0' を代入している。 よって結果として code 1 と code 2 は等価である。

 では、 code 1 と code 2、どちらが早いか?

 code 2 の無駄は、dest[n] に '\0' を必ず代入する分である。 一方、 code 1 の無駄は strlen() で source 文字列を全てスキャンする分と、 文字列の長さと n の比較に要する時間である。

 大抵の場合、文字列を read スキャンするのと、 ある特定の1バイトに write するのとで、 read の方が早いということはあり得ない。 ましてやさらに条件分岐が必要となると、 よほどおかしなシステムでない限り code 2 の方が早い。 この傾向は 多重パイプラインを駆使した RISC Chip であるほど 顕著に出るはずだ。