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

发布于 2024-10-02 10:42:11 字数 1240 浏览 0 评论 0原文

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

目前我正在使用这种简单的方法: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 函数慢(可能使用一些程序集优化或其他技巧而不是简单的 char按字符循环)。或者也许我不知道某些标准内置函数已经做到了这一点?


我做了一些穷人基准测试,迭代 0x1FFFFFFF 乘以三个算法(strcpy+strlen、我的 strcpylen 版本和 版本 user434507)。结果是:

1) strcpy+strlen 以 967 毫秒获胜;

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

3)编辑后的版本需要53秒。

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

I need to get the pointer to the terminating null char of a string.

Currently I'm using this simple way: MyString + strlen(MyString) which is probably quite good out of context.

However I'm uncomfortable with this solution, as I have to do that after a string copy:

char MyString[32];
char* EndOfString;
strcpy(MyString, "Foo");
EndOfString = MyString + strlen(MyString);

So I'm looping twice around the string, the first time in strcpy and the second time in strlen.

I would like to avoid this overhead with a custom function that returns the number of copied characters:

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

EndOfString = MyString + strcpylen(MyString, "Foobar");

However, I fear that my implementation may be slower than the compiler provided CRT function (that may use some assembly optimization or other trick instead of a simple char-by-char loop). Or maybe I'm not aware of some standard builtin function that already does that?


I've done some poor's man benchmarking, iterating 0x1FFFFFFF times three algorithms (strcpy+strlen, my version of strcpylen, and the version of user434507). The result are:

1) strcpy+strlen is the winner with just 967 milliseconds;

2) my version takes much more: 57 seconds!

3) the edited version takes 53 seconds.

So using two CRT functions instead of a custom "optimized" version in my environment is more than 50 times faster!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

毁梦 2024-10-09 10:42:11
size_t strcpylen(char *strDestination, const char *strSource)
{
    char* dest = strDestination;
    while( *dest++ = *strSource++ );
    return dest - strDestination;
}

这几乎与 strcpy 的 CRT 版本所做的完全一样,只是 CRT 版本还会进行一些检查,例如确保两个参数都不为空。

编辑:我正在查看 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 );
}
size_t strcpylen(char *strDestination, const char *strSource)
{
    char* dest = strDestination;
    while( *dest++ = *strSource++ );
    return dest - strDestination;
}

This is almost exactly what the CRT version of strcpy does, except that the CRT version will also do some checking e.g. to make sure that both arguments are non-null.

Edit: I'm looking at the CRT source for VC++ 2005. pmg is correct, there's no checking. There are two versions of strcpy. One is written in assembly, the other in C. Here's the C version:

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

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

        return( dst );
}
ι不睡觉的鱼゛ 2024-10-09 10:42:11

Hacker's Delight 有一个很好的章节介绍了如何在 C 字符串中查找第一个空字节(请参阅第 6 章第 1 节)。我在 Google 图书,代码似乎是 这里。我总是会回到这本书。希望它有帮助。

Hacker's Delight has a nice section on finding the first null byte in a C string (see chapter 6 section 1). I found (parts of) it in Google Books, and the code seems to be here. I always go back to this book. Hope it's helpful.

惟欲睡 2024-10-09 10:42:11

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

Use strlcpy(), which will return the length of what it copied (assuming your size parameter is large enough).

春风十里 2024-10-09 10:42:11

您可以尝试这样做:

int len = strlen(new_str);
memcpy(MyString, new_str, len + 1);
EndOfString = MyString + len;

仅当 new_str 很大时才有意义,因为 memcpy 比标准 while( *dest++ = *strSource++ ); 快得多code> 方法,但有额外的初始化成本。

You can try this:

int len = strlen(new_str);
memcpy(MyString, new_str, len + 1);
EndOfString = MyString + len;

It makes sense only if the new_str is large, because memcpy is much faster that standard while( *dest++ = *strSource++ ); approach, but have extra initialization costs.

温柔戏命师 2024-10-09 10:42:11

只是几点说明:如果您的函数不经常被调用,那么它在您的代码中运行可能比在 C 库中运行得更快,因为您的代码已经在 CPU 缓存中。

您的基准测试正在做的是确保库调用位于缓存中,而在实际应用程序中不一定是这种情况。

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

这取决于您的代码风格、应用程序以及需要节省周期的位置。

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

Just a couple of remarks: if your function is not called very often then it may run faster from your code than from the C library because your code is already in the CPU caches.

What your benchmark is doing, is to make sure that the library call is in the cache, and this is not necessarily the case in a real-world application.

Further, Being inline could even save more cycles: compilers and CPUs prefer leaf function calls (one level encapsulation rather than several call levels) for branch prediction and data pre-fetching.

It al depends on your code-style, your application, and where you need to save cycles.

As you see, the picture is a bit more complex than what was previously exposed.

时光病人 2024-10-09 10:42:11

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

简而言之:不要这样做。

I think you may be worrying unnecessarily here. It's likely that any possible gain you can make here would be more than offset by better improvements you can make elsewhere. My advice would be not to worry about this, get your code finished and see whether you are so short of processing cycles that the benefit of this optimisation outweighs the additional work and future maintenance effort to speed it up.

In short: don't do it.

时光无声 2024-10-09 10:42:11

尝试 memccpy()(或在 VC 2005+ 中使用 _memccpy())。我针对 strcpy + strlen 和您的自定义算法对其进行了一些测试,在我的环境中它击败了两者。不过,我不知道它在你的算法中效果如何,因为对我来说,你的算法运行得比你看到的要快得多,而 strcpy + strlen 则慢得多(前者为 14.4 秒,而后者为 7.3 秒)对于后者,使用您的迭代次数)。我将下面的代码计时为大约 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;
}

Try memccpy() (or _memccpy() in VC 2005+). I ran some tests of it against strcpy + strlen and your custom algorithm, and in my environment it beat both. I don't know how well it will work in yours, though, since for me your algorithm runs much faster than you saw, and strcpy + strlen much slower (14.4s for the former vs. 7.3s for the latter, using your number of iterations). I clocked the code below at about 5s.

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文