C++でC風ライブラリを作る(文字列コピー編)
高木です。こんばんは。
なんか久しぶりですが、「C++でC風ライブラリを作る」もまだまだ続きますよ。
今回は文字列のコピーを行う関数を作成します。
Cで文字列をコピーする関数といえばstrcpy
関数ですね。
ほかにはstrncpy
関数もありますし、BSD系の処理系にはstrlcpy
関数もあります。
sprintf
関数やsnprinf
関数を文字列のコピーに使うこともできます。
また、ワイド文字列のコピーのためにwcscpy
関数やwcsncpy
関数もあります。
swprintf
関数もありますね。
それぞれの関数には特徴があって、よく似ているようで微妙に仕様が異なっています。
strcpy
関数は結果を格納する配列のサイズがわからないので、簡単に不正なメモリアクセスを起こしてしまいます。
strncpy
関数は結果を格納するサイズを指定するのですが、コピー元の文字列が短い場合は残りの領域にナル文字を書き込みます。
逆に、コピー元の文字列の長さが指定したサイズと同じ場合には末尾にナル文字が書き込まれません。
ナル文字で終端しない文字列ができてしまうのは問題につながることも多いため、BSD系の処理系には末尾にナル文字を書き込むことを保証するstrlcpy
関数があります。
これは便利ですが、特定の処理系でしか使えないので移植性に問題があります。
sprintf
関数も結果を格納する配列のサイズを指定できません。
しかし、書式指定子で”%.8s”のようにすることで、書き込む文字列の最大長を指定することができます。
“%.*s”のようにすれば、実引数で最大長を指定することもできます。
snprintf
関数はすべての問題を解決してくれそうで、結果の格納先の配列のサイズを指定できますし、sprintf
関数と同様、書式指定子で文字列の最大長を指定することも可能です。
ただし、決して軽量とはいえないのが難です。
これらを踏まえ、今回はサイズ指定可能なstrcpy
関数とstrncpy
関数をC++で再実装することにします。
snprintf
関数はもともと文字列をコピーするためのものではありませんので、別の機会に譲ることにしましょう。
まずはstrcpy
関数からです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | namespace cloverfield { template <typename charT> inline charT* strcpy(charT* s1, charT const* s2, std::size_t n) { if (s1 == nullptr || n == 0) return s1; std::size_t length = 0; if (s2 != nullptr) length = strlen(s2); if (length < n - 1) n = length + 1; std::copy_n(s2, n - 1, s1); s1[n - 1] = charT(0); return s1; } template <typename charT, std::size_t N> inline charT* strcpy(charT (&s1)[N], charT const* s2) { return strcpy(&s1[0], s2, N); } } |
次にstrncpy
関数です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | namespace cloverfield { template <typename charT> inline charT* strncpy(charT* s1, charT const* s2, std::size_t n) { if (s1 == nullptr || n == 0) return s1; std::size_t length = 0; if (s2 != nullptr) length = strlen(s2); if (n < length) length = n; std::copy_n(s2, length, s1); std::fill_n(s1 + length, n - length, charT(0)); return s1; } } |
とりあえず今回は、単純な文字配列を使ったstrcpy
関数とstrncpy
関数だけを作ってみました。
正直、何の面白みもないシンプルな実装です。
もう少し頑張ることもできるのですが、今回はシンプルなものを目指していますのでこんな感じにしておきます。
次回はstd::basic_string
クラステンプレートを絡めた形で、これらの関数を拡張することにします。