在C中获取复制字符串中空字符的最快方法

在C中获取复制字符串中空字符的最快方法,c,performance,string,crt,C,Performance,String,Crt,我需要获取指向字符串的终止空字符的指针 目前我使用的是这种简单的方式:MyString+strlen(MyString),这在上下文之外可能非常好 但是,我对这种解决方案感到不舒服,因为我必须在复制字符串后执行此操作: char MyString[32]; char* EndOfString; strcpy(MyString, "Foo"); EndOfString = MyString + strlen(MyString); 所以我在字符串周围循环了两次,第一次在strcpy中循环,第二次在

我需要获取指向字符串的终止空字符的指针

目前我使用的是这种简单的方式:
MyString+strlen(MyString)
,这在上下文之外可能非常好

但是,我对这种解决方案感到不舒服,因为我必须在复制字符串后执行此操作:

char MyString[32];
char* EndOfString;
strcpy(MyString, "Foo");
EndOfString = MyString + strlen(MyString);
所以我在字符串周围循环了两次,第一次在
strcpy
中循环,第二次在
strlen
中循环

我希望通过一个返回复制字符数的自定义函数来避免这种开销:

size_t strcpylen(char *strDestination, const char *strSource)
{
    size_t len = 0;
    while( *strDestination++ = *strSource++ )
        len++;
    return len;
}

EndOfString = MyString + strcpylen(MyString, "Foobar");
但是,我担心我的实现可能比编译器提供的CRT函数慢(这可能会使用一些汇编优化或其他技巧,而不是简单的逐字符循环)。或者也许我不知道一些标准的内置函数已经做到了这一点


我做过一些穷人的基准测试,将0x1FFFFFF迭代三次算法(
strcpy
+
strlen
,我的
strcpylen
版本,以及的)。结果是:

1)
strcpy
+
strlen
以967毫秒的成绩获胜

2) 我的版本需要更多的时间:57秒

3) 编辑的版本需要53秒


因此,在我的环境中使用两个CRT函数而不是自定义的“优化”版本,速度要快50倍以上

查看
sprintf

使用
,它将返回复制内容的长度(假设您的尺寸参数足够大)

这几乎正是strcpy的CRT版本所做的,只是CRT版本还将进行一些检查,例如确保两个参数都不为null

编辑:我正在查看VC++2005的CRT源代码。pmg是正确的,没有检查。strcpy有两个版本。一个用汇编语言编写,另一个用C语言编写。以下是C版本:

char * __cdecl strcpy(char * dst, const char * src)
{
        char * cp = dst;

        while( *cp++ = *src++ )
                ;               /* Copy src over dst */

        return( dst );
}

我想你在这里可能担心得不必要。你在这里可能获得的任何收益都可能会被你在其他地方所做的更好的改进所抵消。我的建议是不要担心这一点,完成您的代码,看看您是否缺少处理周期,以至于这种优化的好处超过了额外的工作和未来的维护工作来加速它

简而言之:不要这样做。

有一个很好的章节介绍如何在C字符串中查找第一个空字节(参见第6章第1节)。我在中找到了(部分),代码似乎是。我总是回头看这本书。希望对您有所帮助。

您可以尝试以下方法:

int len = strlen(new_str);
memcpy(MyString, new_str, len + 1);
EndOfString = MyString + len;
只有当
new_str
很大时才有意义,因为
memcpy
比标准
快得多,而(*dest++=*strSource++)方法,但有额外的初始化成本。

尝试
memccpy()
(或
\u memccpy()
在VC 2005+中)。我用strcpy+strlen和您的自定义算法对它进行了一些测试,在我的环境中,它打败了这两种算法。不过,我不知道它在你的算法中的效果如何,因为对我来说,你的算法运行得比你看到的快得多,而且
strcpy+strlen
慢得多(根据你的迭代次数,前者为14.4s,后者为7.3s)。我把下面的代码记在5秒左右

int main(int argc, char *argv[])
{
    char test_string[] = "Foo";
    char new_string[64];
    char *null_character = NULL;
    int i;
    int iterations = 0x1FFFFFFF;

    for(i = 0; i < iterations; i++)
    {
        null_character = memccpy(new_string, test_string, 0, 64);
        --null_character;
    }

    return 0;
}
intmain(intargc,char*argv[])
{
字符测试_字符串[]=“Foo”;
char new_字符串[64];
char*null_character=null;
int i;
int迭代次数=0x1ffffff;
对于(i=0;i
请注意:如果函数不经常调用,那么它从代码中运行的速度可能比从C库中运行的速度快,因为您的代码已经在CPU缓存中

您的基准测试所做的是确保库调用位于缓存中,而在实际应用程序中不一定如此

此外,内联甚至可以节省更多的周期:编译器和CPU更喜欢叶函数调用(一级封装而不是多个调用级别),用于分支预测和数据预取

这取决于您的代码样式、应用程序以及需要保存周期的位置


正如您所看到的,这张图片比之前曝光的要复杂一些。

是的,但这涉及到对格式字符串的解析,我怀疑它是否能更快。@pmg:sprintf返回复制的字符数。@Lorenzo:我知道。。。我只是怀疑
EndOfString=MyString+sprintf(MyString,“%s”,“foo”)
比将
sprintf
排除在我的机器上的pictureQuick检查之外要好:我环境中的
sprintf
版本比我的自定义函数慢50倍…@Lorenzo,解析单个“%s”需要恒定的时间,而strlen O(len)需要恒定的时间。尝试这个实验:使用大约100K个字符串(每个大小可能在10个字符到5K个字符之间变化)并存储其大小。然后,使用此已知大小将函数与strcpy和已知字符串大小进行比较。在此处显示结果。如果字符串不大(如兆字节长)这真的没关系。@pmg是对的。这个操作是否消耗了整个程序时间的10%或更多?(几个堆栈快照会告诉你。)如果不是,你可能会在其他地方遇到更大的问题。@Mike:我不在乎在实践中性能的提高是否可以忽略不计。这只是一个寻找最佳解决方案的练习。我在这里反复说过,“不要这样做”,“它可以忽略不计”,“过早优化是邪恶的”之类的答案(顺便说一句,这不是答案,而是建议),不应该提供。他们是赞助人,大多数时候甚至不是真的,其他时候也不有趣。这是否真的值得减少便携性值得商榷。从您引用的页面:“这些不是C标准的库函数,但在几个Unix操作系统中都可以使用,包括BSD、Mac OS X和Solaris,其中包括著名的exc
int main(int argc, char *argv[])
{
    char test_string[] = "Foo";
    char new_string[64];
    char *null_character = NULL;
    int i;
    int iterations = 0x1FFFFFFF;

    for(i = 0; i < iterations; i++)
    {
        null_character = memccpy(new_string, test_string, 0, 64);
        --null_character;
    }

    return 0;
}