在C中连接字符串,哪种方法更有效?

Xan*_*ndy 56 c string performance concatenation

我遇到了这两种连接字符串的方法:

共同部分:

char* first= "First";
char* second = "Second";
char* both = malloc(strlen(first) + strlen(second) + 2);
Run Code Online (Sandbox Code Playgroud)

方法1:

strcpy(both, first);
strcat(both, " ");       // or space could have been part of one of the strings
strcat(both, second);
Run Code Online (Sandbox Code Playgroud)

方法2:

sprintf(both, "%s %s", first, second);
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,内容both都是"First Second".

我想知道哪一个更有效(我必须执行几个连接操作),或者如果你知道更好的方法来做到这一点.

Chr*_*oph 72

为了便于阅读,我会选择

char * s = malloc(snprintf(NULL, 0, "%s %s", first, second) + 1);
sprintf(s, "%s %s", first, second);
Run Code Online (Sandbox Code Playgroud)

如果您的平台支持GNU扩展,您还可以使用asprintf():

char * s = NULL;
asprintf(&s, "%s %s", first, second);
Run Code Online (Sandbox Code Playgroud)

如果您遇到MS C Runtime,则必须使用_scprintf()确定结果字符串的长度:

char * s = malloc(_scprintf("%s %s", first, second) + 1);
sprintf(s, "%s %s", first, second);
Run Code Online (Sandbox Code Playgroud)

以下很可能是最快的解决方案:

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
Run Code Online (Sandbox Code Playgroud)

  • 我只想对您的第一个解决方案可读性进行投票表决.它更紧凑,但更具可读性吗?我不这么认为.不过,我并没有投票. (17认同)
  • 或许值得一提的是`asprintf()`为你做内存分配:`char*s; int len = asprintf(&s,"%s%s",first,second);`没有任何大惊小怪或muss. (2认同)

Ned*_*der 24

不要担心效率:使代码可读和可维护.我怀疑这些方法之间的区别在你的程序中是否重要.

  • @Ned:那不回答这个问题!他问哪种方式更有效率,而不是他是否应该担心效率. (16认同)
  • 我和内德在一起.您似乎正在进行过早优化.像女孩一样,它也是万恶之源(它有多重根源).让您的程序运行,然后对其进行分析,然后进行优化.在那之前你只是在等待时间恕我直言. (6认同)
  • -1不回答问题; 另外,从这个问题来看,你无法确定优化是否为时过早.为@Arun提供+1实际建议将其分解为函数以获得更大的灵活性(这实际上可以帮助OP) (3认同)
  • 使用这种编程语言实际上意味着你关心效率.如果不这样做,为什么要使用不安全的功能受限语言和手动内存管理?此外,分析被高估了.您可以了解自己的目标并预测可能的性能瓶颈,或者即使在分析器的帮助下您也没有线索. (2认同)
  • 我同意它可能是一个过早优化的情况,但重要的是要认识到(如OP所做的)它最终可能会成为优化的一个案例.如果,事实证明它是瓶颈并且在整个程序中完成了这样的字符串连接,那么这将是一个问题.为了降低风险并且当然为了更好的可读性,我会将其纳入一个函数,比如strConstructConcat(),并将方法1或方法2放入其中并完成它直到分析显示它是一个瓶颈. (2认同)

And*_*dge 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;
}
Run Code Online (Sandbox Code Playgroud)

  • 尝试使用`-march = generic`.mingw默认为i586,它真的很老,过时并且做出适合的假设 (3认同)

Mic*_*dis 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);
Run Code Online (Sandbox Code Playgroud)

  • @onebyone:`memcpy()`的优化版本将在每次迭代步骤中复制多个字节; `strcpy()`也可以这样做,但它仍然必须检查每个字节以检查终止0; 因此我希望`memcpy()`更快 (3认同)
  • 事实上,人们可以使用memcpy,因为已经计算了长度:) (2认同)