【问题标题】:Will memcpy copy a string correctly?memcpy 会正确复制字符串吗?
【发布时间】:2013-02-03 11:58:15
【问题描述】:

我正在努力实现一些低级文件写入,其中文件格式特定于每一位。我需要将一个字符串从NSString 复制到长度为 16 的以空字符结尾的字符串中(根据 Xcode,这是不可分配的)。对于c,我完全是n00b,我想确保我理解正确。这就是我目前正在做的事情:

//I have a non-null NSString called _friendly_name.
const char *string = [_friendly_name UTF8String];
//profile.friendly is a utf-8 null-terminated string
memcpy(&profile.friendly_name, &string, 16);

这还没有经过测试,但我想确定它会起作用。这会提供我期望的行为吗?或者我应该以不同的方式复制字符串(例如strcpy)?

【问题讨论】:

    标签: objective-c c nsstring memcpy strcpy


    【解决方案1】:

    您可以使用memcpy()strcpy(),但您必须自己检查边界,并且您的代码中还有其他错误。

    所以,我认为您想将 NSString 复制到 char[16] 数组中。您可以使用NSString 内置方法来执行此操作,您不必自己使用memcpy()

    NSString *src;
    char dest[16];
    NSUinteger destlen;
    [src getBytes:dest
        maxLength:sizeof(dest) - 1
        usedLength:&destlen
        encoding:NSUTF8StringEncoding
        options:0
        range:NSMakeRange(0, [src length])
        remainingRange:NULL];
    dest[destlen] = '\0';
    

    如果你想使用memcpy(),那么你必须这样做:

    NSString *src;
    char dest[16], *srcUtf8;
    size_t len;
    
    srcUtf8 = [src UTF8String];
    len = strlen(srcUtf8);
    if (len >= sizeof(dest))
        len = sizeof(dest) - 1;
    memcpy(dest, srcUtf8, len);
    dest[len] = '\0';
    

    代码中的错误

    这段代码有两个错误!

    memcpy(&profile.friendly_name, &string, 16); // Wrong!
    

    首先,&string 是错误的。它应该是string,因为string 是指向要复制的字符串数据的指针。如果你复制&string,你会得到一个指针和一些随机的堆栈数据位。换句话说,你会得到垃圾。

    其次,16 是错误的。如果您知道string 指向至少 16 个字节的数据,则只能复制 16 个字节。如果string 小于 16 字节,内存中的以下数据不可读,这将导致分段错误(使您的程序崩溃)。它今天可能不会崩溃,但可能下周会崩溃,而你会忘记这一切吗?

    错了,除非你知道源至少有 16 个字节长,否则不要传递 16。

    【讨论】:

    • 好的。但是 char friendly_name[16] 最多是长度为 15 的 c 字符串(意味着 strlen(friendly_name) 通常会
    • @qPCR4vir:我看不出strlen(friendly_name) 与任何事情有什么关系。我们正在初始化friendly_name,所以没有必要在上面调用strlen()
    • @qPCR4vir:关于 NUL 终止:自己去阅读文档。我不会为你复制粘贴。
    • OK:我现在假设 srcUtf8 = [src UTF8String];在末尾添加一个 0。 (比我在整个 Q 中看不到重要的一点)。无论如何,如果 len >= sizeof(dest)) 您将需要手动在末尾添加 0 (len+1)
    • @qPCR4vir:我更改了memcpy() 版本以添加'\0',但进一步说明,我们并不真正知道截断是否是所需的行为。如果您找不到 API 文档,为什么还要回答有关 Objective C 的问题,我也有点困惑。
    【解决方案2】:

    memcpy() 应该可以正常工作,但 strcpy() 也可以正常工作。您只需要确保 profile.friendly_name 足够大,可以容纳您要复制的内容。

    【讨论】:

      【解决方案3】:

      你读过文档吗? memcpy 将起作用,但您传递要复制的字节数。它将复制您指定的字节数,与 NULL 无关。 strcpy 你不传递长度,它会复制字节直到并包括第一个 NULL。

      http://www.cplusplus.com/reference/cstring/memcpy/

      http://www.cplusplus.com/reference/cstring/strcpy/

      【讨论】:

      • 好的,知道了。感谢您的帮助!
      【解决方案4】:
      //I have a non-null NSString called _friendly_name.
      const char *string = [_friendly_name UTF8String];
      //profile.friendly is a utf-8 null-terminated string  : char [17] ??
      memcpy(profile.friendly_name, string, 16);
      profile.friendly_name[16]='\0';
      

      编辑: 对,就是这样。在 ende 添加NULL。实际上,在复制之前初始化所有0 可能会更好。如果_friendly_name 在位置 16 之前没有 '\0'strcpy 将不起作用。strncpy 会很好。

      EDIT2:

      出现了一些问题,为什么我们没有所有需要的信息:

      1- 您需要一个长度为 16 的以 NULL 结尾的字符串(如char var[17]; 中的 16 个字符+0),或者您需要一个以 NULL 结尾的 16 字节字段,如 char var[16]; 中的?

      2- 什么是预期的“错误”管理:如果原始字符串大于“16”,我们只是截断它,或者我们抛出一个错误?

      假设您需要一个 16 字节的字段和一个可以使用的截断字符串:

      strncpy(profile.friendly_name, string, 15);
      profile.friendly_name[15]='\0';
      

      EDIT3:

      另外...截断必须小心:我们不想截断多字节字符...

      【讨论】:

      • @Phil,null 终止字符串。 C 中的“字符串”应以 NULL 结尾。
      • &string 错误,friendly_name[16] 越界。
      • @Dietrich Epp 感谢字符串!但是我们怎么知道长度为 16 的字符串到底是什么意思呢?在 c 中是 16 个字符加上一个 0、17 个字节。
      • 我认为该字段是char friendly_name[16]
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-18
      相关资源
      最近更新 更多