文字列を連結するときのパフォーマンス低下の原因は、メモリの再割り当てです。Joel Spolskyは、彼の記事Backtobasicsでこれについて説明しています。彼は、文字列を連結する素朴な方法について次のように説明しています。
シュレミエルは通りの画家としての仕事に就き、道路の真ん中に点線を描きます。初日、彼はペンキの缶を道路に持ち出し、300ヤードの道路を終えました。「それはかなりいいです!」上司は「あなたは速い労働者だ!」と言います。そして彼にコペイカを支払います。
翌日、シュレミエルは150ヤードしか完成しません。「まあ、それは昨日ほど良くはありませんが、あなたはまだ速い労働者です。150ヤードは立派です」と彼にコペイカを支払います。
翌日、シュレミエルは道路の30ヤードをペイントします。「たった30!」上司が叫ぶ。「それは受け入れられない!初日にあなたはその10倍の仕事をした!何が起こっているのか?」
「私はそれを助けることはできません」とシュレミエルは言います。「毎日、ペンキ缶からどんどん遠ざかっていきます!」
可能であれば、宛先バッファーを割り当てる前に、宛先バッファーのサイズを知る必要があります。strlen
これを行う唯一の現実的な方法は、連結するすべての文字列を呼び出すことです。次に、適切な量のメモリを割り当て、わずかに変更されたバージョンを使用してstrncpy
、宛先バッファの最後へのポインタを返します。
// Copies src to dest and returns a pointer to the next available
// character in the dest buffer.
// Ensures that a null terminator is at the end of dest. If
// src is larger than size then size - 1 bytes are copied
char* StringCopyEnd( char* dest, char* src, size_t size )
{
size_t pos = 0;
if ( size == 0 ) return dest;
while ( pos < size - 1 && *src )
{
*dest = *src;
++dest;
++src;
++pos;
}
*dest = '\0';
return dest;
}
size
宛先バッファーの終わりまでの残りのバイト数になるようにパラメーターを設定する方法に注意してください。
テスト関数の例を次に示します。
void testStringCopyEnd( char* str1, char* str2, size_t size )
{
// Create an oversized buffer and fill it with A's so that
// if a string is not null terminated it will be obvious.
char* dest = (char*) malloc( size + 10 );
memset( dest, 'A', size + 10 );
char* end = StringCopyEnd( dest, str1, size );
end = StringCopyEnd( end, str2, size - ( end - dest ) );
printf( "length: %d - '%s'\n", strlen( dest ), dest );
}
int main(int argc, _TCHAR* argv[])
{
// Test with a large enough buffer size to concatenate 'Hello World'.
// and then reduce the buffer size from there
for ( int i = 12; i > 0; --i )
{
testStringCopyEnd( "Hello", " World", i );
}
return 0;
}
生成するもの:
length: 11 - 'Hello World'
length: 10 - 'Hello Worl'
length: 9 - 'Hello Wor'
length: 8 - 'Hello Wo'
length: 7 - 'Hello W'
length: 6 - 'Hello '
length: 5 - 'Hello'
length: 4 - 'Hell'
length: 3 - 'Hel'
length: 2 - 'He'
length: 1 - 'H'
length: 0 - ''