我遇到了这两种连接字符串的方法:
共同部分:
char* first= "First";
char* second = "Second";
char* both = malloc(strlen(first) + strlen(second) + 2);
方法1:
strcpy(both, first);
strcat(both, " "); // or space could have been part of one of the strings
strcat(both, second);
方法2:
sprintf(both, "%s %s", first, second);
在这两种情况下,both
的内容都是"First Second"
。
我想知道哪一个更有效(我必须执行多个连接操作),或者如果你知道更好的方法。
答案 0 :(得分:72)
为了便于阅读,我会选择
char * s = malloc(snprintf(NULL, 0, "%s %s", first, second) + 1);
sprintf(s, "%s %s", first, second);
如果您的平台支持GNU扩展,您还可以使用asprintf()
:
char * s = NULL;
asprintf(&s, "%s %s", first, second);
如果您遇到MS C Runtime,则必须使用_scprintf()
来确定结果字符串的长度:
char * s = malloc(_scprintf("%s %s", first, second) + 1);
sprintf(s, "%s %s", first, second);
以下很可能是最快的解决方案:
size_t len1 = strlen(first);
size_t len2 = strlen(second);
char * s = malloc(len1 + len2 + 2);
memcpy(s, first, len1);
s[len1] = ' ';
memcpy(s + len1 + 1, second, len2 + 1); // includes terminating null
答案 1 :(得分:24)
不要担心效率:使代码可读和可维护。我怀疑这些方法之间的差异在你的程序中是否重要。
答案 2 :(得分:18)
这对你来说有些疯狂,我实际上是去测量它。血淋淋的地狱,想象一下。我想我得到了一些有意义的结果。
我使用双核P4,运行Windows,使用mingw gcc 4.4,使用“gcc foo.c -o foo.exe -std = c99 -Wall -O2”构建。
我测试了原帖中的方法1和方法2。最初将malloc保留在基准循环之外。方法1比方法2快48倍。奇怪的是,从构建命令中删除-O2使得生成的exe快30%(尚未调查原因)。
然后我在循环中添加了一个malloc并且自由了。这使方法1减慢了4.4倍。方法2减慢了1.1倍。
所以,malloc + strlen + free不会在配置文件中占主导地位,足以让sprintf值得一试。
这是我使用的代码(除了使用<而不是!=实现循环,但这打破了这篇文章的HTML呈现):
void a(char *first, char *second, char *both)
{
for (int i = 0; i != 1000000 * 48; i++)
{
strcpy(both, first);
strcat(both, " ");
strcat(both, second);
}
}
void b(char *first, char *second, char *both)
{
for (int i = 0; i != 1000000 * 1; i++)
sprintf(both, "%s %s", first, second);
}
int main(void)
{
char* first= "First";
char* second = "Second";
char* both = (char*) malloc((strlen(first) + strlen(second) + 2) * sizeof(char));
// Takes 3.7 sec with optimisations, 2.7 sec WITHOUT optimisations!
a(first, second, both);
// Takes 3.7 sec with or without optimisations
//b(first, second, both);
return 0;
}
答案 3 :(得分:6)
size_t lf = strlen(first);
size_t ls = strlen(second);
char *both = (char*) malloc((lf + ls + 2) * sizeof(char));
strcpy(both, first);
both[lf] = ' ';
strcpy(&both[lf+1], second);
答案 4 :(得分:2)
他们应该差不多了。差别并不重要。我会选择sprintf
,因为它需要更少的代码。
答案 5 :(得分:2)
差异不太重要:
正如其他海报所提到的,这是一个不成熟的优化。专注于算法设计,只有在分析表明它是性能问题时才会回到这一点。
那说......我怀疑方法1会更快。解析 sprintf 格式字符串有一些---无可否认的小---开销。并且 strcat 更可能是“内联”。
答案 6 :(得分:1)
sprintf()旨在处理的不仅仅是字符串,strcat()是专家。但我怀疑你是在冒汗。 C字符串在根本上是低效的,使得这两种方法之间的差异无关紧要。请阅读Joel Spolsky的"Back to Basics"了解详情。
这是一个C ++通常比C表现更好的实例。对于使用std :: string进行重量级的字符串处理可能更有效,更安全。
[编辑]
[第二次编辑]更正的代码(C字符串实现中的迭代次数过多),时间和结论会相应更改
我很惊讶Andrew Bainbridge的评论说std :: string比较慢,但他没有发布这个测试用例的完整代码。我修改了他的(自动化时间)并添加了一个std :: string测试。该测试采用VC ++ 2008(本机代码),默认为“Release”选项(即优化),Athlon双核,2.6GHz。结果:
C string handling = 0.023000 seconds
sprintf = 0.313000 seconds
std::string = 0.500000 seconds
所以这里strcat()到目前为止更快(你的milage可能因编译器和选项而异),尽管C字符串约定固有的低效率,并且支持我的原始建议sprintf()带有很多不需要的包袱以此目的。然而,它仍然是最不可读和安全的,所以当性能不是很关键时,IMO没什么好处。
我还测试了一个std :: stringstream实现,它再次慢得多,但对于复杂的字符串格式仍然有优点。
更正后的代码如下:
#include <ctime>
#include <cstdio>
#include <cstring>
#include <string>
void a(char *first, char *second, char *both)
{
for (int i = 0; i != 1000000; i++)
{
strcpy(both, first);
strcat(both, " ");
strcat(both, second);
}
}
void b(char *first, char *second, char *both)
{
for (int i = 0; i != 1000000; i++)
sprintf(both, "%s %s", first, second);
}
void c(char *first, char *second, char *both)
{
std::string first_s(first) ;
std::string second_s(second) ;
std::string both_s(second) ;
for (int i = 0; i != 1000000; i++)
both_s = first_s + " " + second_s ;
}
int main(void)
{
char* first= "First";
char* second = "Second";
char* both = (char*) malloc((strlen(first) + strlen(second) + 2) * sizeof(char));
clock_t start ;
start = clock() ;
a(first, second, both);
printf( "C string handling = %f seconds\n", (float)(clock() - start)/CLOCKS_PER_SEC) ;
start = clock() ;
b(first, second, both);
printf( "sprintf = %f seconds\n", (float)(clock() - start)/CLOCKS_PER_SEC) ;
start = clock() ;
c(first, second, both);
printf( "std::string = %f seconds\n", (float)(clock() - start)/CLOCKS_PER_SEC) ;
return 0;
}
答案 7 :(得分:0)
我不知道在第二种情况下有任何真正的连接。将它们背靠背打印不构成连接。
告诉我,这会更快:
1)a)将字符串A复制到新缓冲区 b)将字符串B复制到缓冲区 c)将缓冲区复制到输出缓冲区
或
1)将字符串A复制到输出缓冲区 b)将字符串b复制到输出缓冲区
答案 8 :(得分:0)
答案 9 :(得分:-1)
由于两种方法都必须计算字符串长度或每次扫描它,因此两者都不是非常有效。相反,既然你计算了各个字符串的strlen()s,那么把它们放在变量中然后只是strncpy()两次。