使用尽可能少的内存来预占C字符串的最有效方法是什么?
我正在尝试重建大目录树中文件的路径。
以下是我以前做过的事情的想法:
char temp[LENGTH], file[LENGTH];
file = some_file_name;
while (some_condition) {
parent_dir = some_calculation_that_yields_name_of_parent_dir;
sprintf(temp, "%s/%s", parent_dir, file);
strcpy(file, temp);
}
这看起来有点笨拙。
任何帮助将不胜感激。谢谢!
答案 0 :(得分:14)
如果您希望在同一个内存块中进行复制,则很难避免复制。如果分配的块足够大,你可以使用memmove
将原始字符串移动你想要的长度,然后将其复制到开头,但我怀疑这是不那么“笨重”。然而,它会为你节省额外的内存(再次,授予原始块有足够的可用空间)。
这样的事情:
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
/* Prepends t into s. Assumes s has enough space allocated
** for the combined string.
*/
void prepend(char* s, const char* t)
{
size_t len = strlen(t);
size_t i;
memmove(s + len, s, strlen(s) + 1);
for (i = 0; i < len; ++i)
{
s[i] = t[i];
}
}
int main()
{
char* s = malloc(100);
strcpy(s, "file");
prepend(s, "dir/");
printf("%s\n", s);
return 0;
}
答案 1 :(得分:7)
如果您不需要按顺序存储字符串,但只有出现才能按顺序存储,那么请使用称为“绳索”的东西。 (它由许多“字符串”组成,请参阅。)
我认为它基本上是struct { char *begin; char *end };
在C ++中,它实现了所有std :: string函数。在C中,您需要为所有strxxx()函数编写(或获取库)替换函数。
“绳索”将字符串添加到另一个字符串所做的只是插入一个新的开始,结束对指向新的字符串。它可能还必须复制新的字符串,如果它是一个临时指针。或者如果它是一个已分配的字符串,它可以获取字符串的所有权。
绳子非常适合大弦。但是,使用memmove和memcpy处理大约8 KB以下的任何内容都会更快。
答案 2 :(得分:3)
sprintf()通常不是'快'。因为你知道它的预先存在的memmove()两次可能比速度更好。
如果您最初使用malloc()分配字符串,则可以考虑使用realloc()来调整字符数组的大小,以便它们可以包含新字符串。
char* p = malloc( size_of_first_string );
...
p = realloc( p, size_of_first_string + size_of_prepended_string + 1 );
memmove( p + size_of_prepended_string, p, size_of_first_string );
memmove( p, prepended_string, size_of_prepended_string );
答案 3 :(得分:1)
您可以从结尾开始维护字符串。因为你似乎已经知道了maxSize ......
所以基本上如果文件最初是(foo.txt)
[] [] [] [] [] [f] [o] [o] [.] [t] [x] [t] [\0]
^
|
lastEmpty
现在,如果您添加父目录/它将显示为
[] [] [] [a] [/] [f] [o] [o] [.] [t] [x] [t] [\0]
^
|
lastEmpty
所以代码看起来像(可能有错误,但你明白了)。
char temp[LENGTH], file[LENGTH];
int lastEmpty = put_at_end(some_file_name, file);
// lastEmpty points to right most empty slot
while (some_condition) {
parent_dir = some_calculation_that_yields_name_of_parent_dir;
int len = strlen(parent_dir);
char *tmp = parent_dir + len -1;
while (lastEmpty > 0) {
file[lastEmpty] = *tmp;
lastEmpty --;
tmp--;
}
}
因为我认为我们可以期望parent_dir很小,所以重复两遍应该没问题。如果要传递文件字符串,可以使用file+lastEmpty+1
。
答案 4 :(得分:1)
您可以爬到目录树的顶部,随时保留名称,然后将名称一次粘贴到一起。至少那时你不会通过推到前面来做不必要的副本。
int i = 0;
int j;
char temp*[MAX_DIR_DEPTH], file[LENGTH];
while (some_condition) {
temp[i++] = some_calculation_that_yields_name_of_parent_dir;
}
char *pCurrent = file;
for( j = i-1; j > 0; j-- )
{
strcpy(pCurrent, temp[j]);
pCurrent += strlen(temp[j]);
*pCurrent++ = '\';
}
strcpy(pCurrent, filename);
*pCurrent = 0;
答案 5 :(得分:1)
也许我很困惑,但我相信前缀与换掉字符串相同。因此,字符串“World”可以追加到“Hello”,而不是将
const char world[] = "World";
const char hello[] = "Hello";
// Prepend hello to world:
const unsigned int RESULT_SIZE = sizeof(world) + sizeof(hello) + 2 * sizeof('\0');
char * result = malloc(RESULT_SIZE);
if (result)
{
strcpy(result, hello);
strcat(result, world);
puts("Result of prepending hello to world: ");
puts(result);
puts("\n");
}
此外,执行时间的主要浪费是找到字符串的结尾。如果字符串以长度存储,则可以更快地计算结束。
答案 6 :(得分:0)
此解决方案不再需要复制。它确实需要一个strlen,因此如果目录名称检索可以返回复制的字节数,或者如果可以预先计算父目录字符串长度,则可以对其进行优化。
void GetFilename(char *pFile)
{
strcpy(pFile, "someFile");
}
void GetParentDir(char *pDir)
{
strcpy(pDir, "/parentdir");
}
int _tmain(int argc, _TCHAR* argv[])
{
char path[1024];
GetParentDir(path);
int dirSize = strlen(path);
path[dirSize] = '/';
GetFilename(path + dirSize + 1);
printf(path);
return 0;
}
答案 7 :(得分:0)
我在数组的左侧和右侧留下一个缓冲区。你必须持有两个索引,但是如果你必须做很多次(其他方面没有效率问题)它会对它进行排序。我建议的两个索引是s; e],一个包括,一个不包括:
#define BUFSIZE 256
#define LEFTBUF 20
struct mstring
{
char * string;
unsigned s;
unsigned e;
}
void checkbuf(struct mstring *value, int newstringlen, char leftorright)
{
//have fun here
}
char * concat (struct mstring * value, char * str)
{
checkbuf(value, strlen(value,str), 'r');
int i=0;
while (str[i])
value->string[value->e++]=str[i++];
}
char * set(struct mstring * value, char * str)
{
value->e=LEFTBUF;
value->s=LEFTBUF;
concat( value,str);
}
char * prepend (struct mstring * value, char * str)
{
checkbuf(value, strlen(value,str), 'l');
int i=strlen(value,str)-1;
while (i>=0)
value->string[--value->s]=str[i--];
}
int main()
{
struct mstring * mystring= (struct mstring *) malloc(sizeof(struct mstring) );
mystring->string=(char*)malloc(sizeof(char)*BUFSIZE);
set( mystring,"World");
prepend(mystring,"Hallo")
}
然后你必须为填充子串准备一个函数......