如何strcpy并返回复制的字符数?

Posted

技术标签:

【中文标题】如何strcpy并返回复制的字符数?【英文标题】:How to strcpy and return number of copied characters? 【发布时间】:2016-03-27 22:37:34 【问题描述】:

我想将一个以 null 结尾的字符串复制到另一个位置,并想知道复制的字符串有多长。效率是最重要的。有strcpy函数可以实现,但它不返回实际复制了多少个字符。

当然,我可以通过简单地调用strlen 来检测复制字符串的长度来发现这一点,但这意味着再次遍历字符串中的字符,尽管strcpy 必须跟踪如何无论如何它复制了许多字符。出于性能原因,我不想要这样的第二次遍历。

我知道用简单的逐字符复制编写自己的strcpy 很容易,但我认为标准库可能会应用魔法,这使得strcpy 比简单的逐字符实现更快。

那么,strcpy 并接收复制的字符数而不再次遍历字符串的最佳方法是什么?

【问题讨论】:

strcpy 并不复杂,你可以自己写... 不明白如何使用 strcpy。如果您知道要复制的字符串的长度,请使用它,因为 strcpy 将始终从源字符串中复制所有字符(包括 null,因此它确实复制了 strlen(src)+1 个字符)。因为它是 C++,你可以使用 std::string。 你冷使用std::strings,不用担心这个。 你为什么这么担心遍历字符串两次?这真的会导致您的代码出现性能问题,还是您过早地进行优化>? @Barmar:我在主存数据库管理系统实现的核心中使用了这段代码。它很可能处于要执行的大量查询的热循环中。 【参考方案1】:

许多系统支持stpcpy,它返回指向目标末尾的指针。您可以减去原始目标指针,这样您就可以得到长度。

【讨论】:

Here 表示返回的 char* 与 dest ... @Ben 您链接到strcpy 文档,但@Zbynek 指的是stpcpy 这确实很有用,但我想独立于平台。标准库中是否存在这样的函数? @gexicide:不幸的是,没有扩展的基本 C 库非常有限。我相信我在 Windows 上也看到过类似的东西,但忘记了名字。【参考方案2】:

我会使用sprintf 来完成这项工作:

size_t len_strcpy(char *dest, char const *src)  
    return sprintf("%s", dest, src);

很难猜测这是否真的会比使用strcpy 后跟strlen 更慢或更快。以微软编译器为例,他们最近在sprintf 的实现上做了一些工作,所以(在英特尔上)它使用向量指令并且运行得非常快。使用旧版本的编译器,strcpy/strlen 获胜的机会会更大。

【讨论】:

我想解析格式字符串可能不是最快的解决方案。 @gexicide:但是,正如答案中所指出的,实际上,至少使用一种广泛使用的实现,使用sprintf(等等)进行一些字符串操作实际上比使用看起来应该更快的功能(在某些情况下有相当大的差距)。我目前找不到链接,但 STL 最近在一篇博客文章中提到了它。在任何情况下,它都会解析两个字符的格式字符串,因此除非解析器真的很慢(或者被复制的字符串非常短),否则它并不难获胜。【参考方案3】:

如果你真的需要这个,你自己写会很容易:

unsigned int lenstrcpy(char dest[], const char source[]) 
    unsigned int i = 0;
    while ((dest[i] = source[i]) != '\0') 
       i++;
    
    return i; 

Here is a live example.

【讨论】:

目前尚不清楚这是否会比使用strcpy() 然后strlen() 更快,因为这些功能针对目标平台进行了优化。 @Slava OP担心遍历字符串两次?这样就解决了这个问题? @Ben 他担心速度,两次遍历可能会减慢速度,而不是不惜一切代价避免双重遍历 Slava 是对的,速度是我的目标。我知道编写自己的 strcpy 很容易,但我认为标准库可能会应用魔法,这使得 strcpy 比简单的逐字符实现更快。 @Slava 好的,看起来肯定会发生一些神奇的事情:ideone.com/PZTQu5【参考方案4】:

我会尝试这个 - 使用 strlen() 然后 memcpy() 但像往常一样,当您的目标是速度时,您最好在目标平台上测试它,因为理论上可能有很多因素难以计算。

已将 memcpy() 添加到 Ben 的 speed test

在 gcc 5.1.0 上,-O1 结果是:

第 1 部分:62181 - 手动复制 第 2 部分:195093 - strcpy/strlen 第 3 部分:45568 - strlen/memcpy

第 1 部分和第 3 部分的 -O2 结果几乎没有变化,但第 2 部分发生了巨大变化:

第 1 部分:62234 第 2 部分:12129 第 3 部分:45565

看起来 -O2 优化编译器消除了对 strlen() 的额外调用并使用了缓存值。

【讨论】:

【参考方案5】:

标准 C 库不提供满足您需求的函数,因此您需要编写自己的函数。幸运的是,它并不太复杂:

size_t StrCpyLen(char *dest, const char *src) 
    const char *s = src;
    while ((*dest++ = *s++))
        ;
    return s - src - 1;

Demo.

上面的实现是从K&R中提炼出来的,做了一点小改动:不是直接使用src,而是在运行循环之前进行复制,这样循环一次就可以通过减去原来的src来确定长度完成。

【讨论】:

这种幼稚的逐字符实现是否与strcpy 一样有效,它可能会应用一些依赖于平台的魔法? @gexicide 好吧,实现不应该与strcpy 本身进行比较,而是与strcpy+strlen 背靠背进行比较。尽管它与strcpy 一样慢或更慢,但它胜过strcpy+strlen 的可能性相当高,尤其是对于长字符串,当缓存开始发挥主要作用时。 迟到了,但这只是因为该示例中的字符串是编译时常量。只要您将str1 更改为动态字符串,strcpy + strlen 版本就会变得几乎慢一倍。 ideone.com/arie8e

以上是关于如何strcpy并返回复制的字符数?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 strcpy 在 C 中不安全? [复制]

c语言中如何像用函数strcpy复制字符串一样复制数字,如复制30。

C语言strcpy()函数,字符数组复制

strcpy函数如何使用

C语言--strcpy()函数

strcpy 与 strdup