strcat()、malloc() 和 realloc() 的开销经常被忽略。
我在这个案子上玩了一会儿,得到了一些数字:
% ./catter "Start: " "test " 1000
realloc_catter: 379
prealloc_catter: 154
realloc_mycatter: 152
prealloc_mycatter: 8
% ./catter "Start: " "test " 100000
realloc_catter: 1453494
prealloc_catter: 741639
realloc_mycatter: 733160
prealloc_mycatter: 365
% ./catter "Start: " "test " 1000000
realloc_catter: 265374117
prealloc_catter: 128139699
realloc_mycatter: 127484834
prealloc_mycatter: 3397
在这里,我们清楚地看到了 O(n^2) 成本。适当优化的连接方法只需 O(n)。
测试代码是:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
void measure(char *name, void (*func)(char**,char *,char*,int), char **target, char *initial, char *repeat, int times)
{
clock_t start, finish;
start = clock();
func(target, initial, repeat, times);
free(*target);
finish = clock();
printf("%20s: %10lld\n", name, (long long)(finish - start));
}
void realloc_catter(char **target, char *initial, char *repeat, int times)
{
*target = malloc(strlen(initial)+1);
strcpy(*target, initial);
for (int i = 0; i < times; i++) {
*target = realloc(*target, strlen(*target) + strlen(repeat) + 1);
strcat(*target, repeat);
}
}
void prealloc_catter(char **target, char *initial, char *repeat, int times)
{
*target = malloc(strlen(initial) + strlen(repeat) * times + 1);
strcpy(*target, initial);
for (int i = 0; i < times; i++) {
strcat(*target, repeat);
}
}
char *mystrcat(char *target, char *repeat)
{
for(;;) {
*target = *repeat;
if (!*repeat) break;
target++;
repeat++;
}
return target;
}
void realloc_mycatter(char **target, char *initial, char *repeat, int times)
{
char *catptr = *target = malloc(strlen(initial)+1);
strcpy(*target, initial);
for (int i = 0; i < times; i++) {
*target = realloc(*target, strlen(*target) + strlen(repeat) + 1);
catptr = mystrcat(catptr, repeat);
}
}
void prealloc_mycatter(char **target, char *initial, char *repeat, int times)
{
char *catptr = *target = malloc(strlen(initial) + strlen(repeat) * times + 1);
strcpy(*target, initial);
for (int i = 0; i < times; i++) {
catptr = mystrcat(catptr, repeat);
}
}
int main(int argc, char **argv)
{
if (argc < 4) exit(1);
char *initial = argv[1];
char *repeat = argv[2];
int times = atoi(argv[3]);
char *target;
measure("realloc_catter", realloc_catter, &target, initial, repeat, times);
measure("prealloc_catter", prealloc_catter, &target, initial, repeat, times);
measure("realloc_mycatter", realloc_mycatter, &target, initial, repeat, times);
measure("prealloc_mycatter", prealloc_mycatter, &target, initial, repeat, times);
return 0;
}
mystrcat() 函数返回一个指向最后一个字符串位置的指针,保存下一次调用必须再次遍历字符串。此测试代码也可通过gist 获得。