考虑一个简单的程序,该程序将所有指定的参数连接起来并在标准输出中打印它们。我使用了2个for循环来附加字符串,其中一个用于计算字符串的长度,另一个用于连接字符串。有没有办法只做一次循环?为每个要连接的字符串重新分配内存不是更有效,是吗? Java的StringBuilder如何用C实现?它会像我一样循环两次吗?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char** argv)
{
size_t len = 0;
// start for loop at i = 1 to skip the program name specified in argv
for(int i = 1; i < argc; i++)
len += strlen(argv[i]) + 1; // +1 for the space
char* toAppend = (char*)malloc(len * sizeof(char) + 1);
toAppend[0] = '\0'; // first string is empty and null terminated
for(int i = 1; i < argc; i++)
{
strcat(toAppend, argv[i]);
strcat(toAppend, " ");
}
printf(toAppend);
free(toAppend);
}
答案 0 :(得分:2)
您的分配方法非常有效,可以测量总长度并仅分配一次。但是,串联循环从一开始就反复测量输出缓冲区的长度,直到连接到它为止,从而导致二次运行时间。
要修复该问题,请跟踪您的位置:
size_t pos = 0;
for(int i = 1; i < argc; i++) {
size_t len = strlen(argv[i]);
memcpy(toAppend+pos, argv[i], len);
pos += len;
toAppend[pos] = ' ';
pos++;
}
toAppend[pos] = 0;
这是实际在内存中进行连接的最有效方法,但是最有效的方法是不进行连接。相反:
for(int i = 1; i < argc; i++)
printf("%s ", argv[i]);
stdio被缓冲的全部原因是,您不必构建任意长度的内存中缓冲区即可进行有效输出;相反,它会自动缓冲到固定大小,并在缓冲区已满时刷新。
请注意,如果您的输入在任何地方都包含printf
字符,那么您对%
的使用是错误和危险的;应该是printf("%s", toAppend);
。
如果您要写的是POSIX(或POSIX-ish)系统,而不是纯C,则另一个选择是fmemopen
,它使您可以像这样编写循环:
for(int i = 1; i < argc; i++)
fprintf(my_memfile, "%s ", argv[i]);
答案 1 :(得分:2)
在c中连接字符串的有效方法
一种有效的方法是计算字符串长度-并记住它们。
size_t sum = 1; // for \0
if (argc > 2) sum += argc - 2. // spaces
size_t length[argc]; // This is a VLA, available C99 and optionally in C11
for(int i = 1; i < argc; i++)
length[i] = strlen(argv[i]);
sum += length[i];
}
然后分配,然后检查错误。
char *dest = malloc(sum);
if (dest == NULL) Handle_OutOfMemory();
依次复制每个字符串
char *p = dest;
for(int i = 1; i < argc; i++)
// Use either memcpy() or strcpy().
// memcpy() tends to be faster for long strings than strcpy().
memcpy(p, argv[i], length[i]);
p += length[i]; // advance insertion point
if (i > 1) {
*p++ = ' '; // space separators
}
}
*p = '\0';
现在使用dest[]
。
printf("<%s>\n", dest);
完成后释放资源。
free(dest);
为每个要串联的字符串重新分配内存不是更有效吗?
通常最好避免重复的重新分配,但是对于小的短字符串,确实没有什么区别。专注于big O。我的答案是O(n)
。重定位为O(n*n)
。
如果性能至关重要,请针对目标系统尝试各种方法和配置文件。关键在于在一台机器上最快的速度在另一台机器上可能有所不同。通常,最好先编写一种合理清晰的方法。
答案 2 :(得分:0)
最有效的方法可能是不使用任何str
函数并“手动”复制字符:
char* toAppend = malloc(len + 1);
size_t j = 0;
for(size_t i = 1; i < argc; i++)
{
for(size_t k = 0; argv[i][k]; k++)
toAppend[j++] = argv[i][k];
toAppend[j++] = ' ';
}
toAppend[j - 1] = '\0'; // Remove the last space and NULL-terminate the string