MW211 EXIT

devlog
C言語/文字列のコピー総集編
2013年01月13日
文字列の場合は、代入式は使えない。ということで以下はダメ。
┌──────────────────────────────────────┐
│出力先 = 入力元;                                                            │
└──────────────────────────────────────┘
「&出力先 = 入力元;」とか「&出力先 = &入力元;」とか足掻いてみてもダメ。
構造体の場合は代入式が使えるので、よく勘違いしがちだ。

では、どうするか?

まず、最凶なのは、「memcpy()」。これならなんでもできる。
┌──────────────────────────────────────┐
│memcpy(出力先, 入力元, sizeof(出力先));                                     │
└──────────────────────────────────────┘
ただし、末尾の「\0」を保証してくれないので、入力元の文字数が多いとアウト。
んな訳で、以下のような小細工を施したりする。
┌──────────────────────────────────────┐
│memset(出力先, 0 sizeof(出力先));                                           │
│memcpy(出力先, 入力元, sizeof(出力先) - 1);                                 │
└──────────────────────────────────────┘
まあ、なんつーか、文字をメモリとして機械的扱う感じだ。

じゃ次は、せっかくだから文字として扱ってやろうという話。
代表的なのはこれ、「strcpy()」。
┌──────────────────────────────────────┐
│strcpy(出力先, 入力元);                                                     │
└──────────────────────────────────────┘
こいつは、入力元の「\0」までを強制的にコピーしてしまうので、
扱いにはかなり慎重にならなければなるまい(メモリ侵犯しやすい)。
あらかじめ、出力先と入力元のサイズの関係が保証される時に使う感じか。
ま、予防策を講じるなら以下のような感じか。オールオアナッシングだけど。
┌──────────────────────────────────────┐
│if (strlen(入力元) < sizeof(出力先)) {                                      │
│    strcpy(出力先, 入力元);                                                 │
│}                                                                           │
└──────────────────────────────────────┘

続いて、進化形(?)の「strncpy()」。
┌──────────────────────────────────────┐
│strncpy(出力先, 入力元, sizeof(出力先));                                    │
└──────────────────────────────────────┘
サイズを指定できて、しかも入力元が短い場合、残りを「\0」で埋めてくれる。
ってことで、「strcpy()」よりも遅いらしい。
そしてなによりもこいつも末尾の「\0」を保証してくれない。
前出の「memcpy()」とほとんど同じやん。
やっぱり以下となってしまうのか?(これじゃあんまり使い道ないね)
┌──────────────────────────────────────┐
│memset(出力先, 0 sizeof(出力先));                                           │
│strncpy(出力先, 入力元, sizeof(出力先) - 1);                                │
└──────────────────────────────────────┘

そして、別分野から参入の「snprintf()」。これは使える。
┌──────────────────────────────────────┐
│snprintf(出力先, sizeof(出力先), "%s", 入力元);                             │
└──────────────────────────────────────┘
これだと「出力先≦入力元」でも、最後の末尾に「\0」を押し込めてくれる。
ま、その分最後の一文字は犠牲になるのだが、
「\0」がなくて例外になるよりはかなりまし。
多少速度が犠牲になるらしいが、「memset()」とか使って実装するなら、
一行で済むこいつにしてしまった方がいいのかも。

ま、これらをコングロマリット(?)してできた案が以下。
┌──────────────────────────────────────┐
│if (strlen(入力元) < sizeof(出力先)) {                                      │
│    strcpy(出力先, 入力元);                                                 │
│} else {                                                                    │
│    snprintf(出力先, sizeof(出力先), "%s", 入力元);                         │
│}                                                                           │
└──────────────────────────────────────┘
ま、速度が気になるなら一考かも。
分類:C/C++