【问题标题】:Concatenate with memcpy与 memcpy 连接
【发布时间】:2011-10-19 08:38:12
【问题描述】:

我正在尝试使用 memcpy 将两个字符串相加。我需要,第一个 memcpy 确实包含数据。然而,第二个没有添加。知道为什么吗?

if (strlen(g->db_cmd) < MAX_DB_CMDS )
{
      memcpy(&g->db_cmd[strlen(g->db_cmd)],l->db.param_value.val,strlen(l->db.param_value.val));
      memcpy(&g->db_cmd[strlen(g->db_cmd)],l->del_const,strlen(l->del_const));
      g->cmd_ctr++;
}

【问题讨论】:

  • 仅供参考:在 C 中允许将长语句分成多行,甚至可以分成带有额外变量的多个语句以保存中间结果。
  • 我注意到一些嵌入式数据库(我认为是 berkeley DB)明确使用数据和长度。数据不需要以空值结尾,并且可能包含空值。在这种情况下,如果您的程序已经从 DB API 获得了长度,请保留它并避免使用 strlen。

标签: c string memcpy


【解决方案1】:

您不是在复制空终止符,您只是在处理原始字符串数据。这会使您的字符串非空终止,这可能会导致各种问题。您也没有检查以确保缓冲区中有足够的空间,这可能会导致buffer overflow vulnerabilities

为确保复制空终止符,只需将要复制的字节数加 1 -- 复制 strlen(l-&gt;db.param_value.val) + 1 字节。

【讨论】:

    【解决方案2】:

    一个可能的问题是,您的第一个 memcpy() 调用不一定会导致以空字符结尾的字符串,因为您没有从 l-&gt;db.param_value.val 复制 '\0' 终止符:

    因此,当在第二次调用 memcpy() 时调用 strlen(g-&gt;db_cmd) 时,它可能会返回完全虚假的内容。这是否是一个问题取决于g-&gt;db_cmd 缓冲区是否事先初始化为零。

    为什么不使用strcat(),它的作用正是你想要用memcpy() 做的事情?

    if (strlen(g->db_cmd) < MAX_DB_CMDS )
         {
          strcat( g->db_cmd, l->db.param_value.val);
          strcat( g->db_cmd, l->del_const);
          g->cmd_ctr++;
         }
    

    这样做的好处是更容易让人阅读。您可能会认为它的性能会降低 - 但我不这么认为,因为您明确地进行了一堆 strlen() 调用。无论如何,我会先集中精力把它做好,然后再担心性能。不正确的代码是尽可能未经优化的——在快速获得它之前先把它做好。事实上,我的下一步不会是提高代码性能,而是改进代码以减少缓冲区溢出的可能性(我可能会改用 strlcat() 之类的东西而不是 @ 987654331@).

    例如,如果g-&gt;db_cmd 是一个字符数组(而不是指针),则结果可能如下所示:

    size_t orig_len = strlen(g->db_cmd);
    
    size_t result = strlcat( g->db_cmd, l->db.param_value.val, sizeof(g->db_cmd));
    result = strlcat( g->db_cmd, l->del_const, sizeof(g->db_cmd));
    g->cmd_ctr++;
    
    if (result >= sizeof(g->db_cmd)) {
        // the new stuff didn't fit, 'roll back' to what we started with
        g->db_cmd[orig_len] = '\0';
        g->cmd_ctr--;
    }
    

    如果strlcat() 不是您平台的一部分,则可以很容易地在网上找到它。如果您使用的是 MSVC,则可以使用 strcat_s() 函数来代替(但请注意,它不等同于 strlcat() - 您必须更改检查和处理调用 strcat_s() 的结果的方式)。

    【讨论】:

      【解决方案3】:
      size_t len = strlen(l->db.param_value.val);
      
      memcpy(g->db_cmd, l->db.param_value.val, len);
      memcpy(g->db_cmd + len, l->del_const, strlen(l->del_cost)+1);
      

      这将为您带来以下好处:

      • 减少对strlen 的冗余调用。每个都必须遍历字符串,因此最好尽量减少这些调用。
      • 第二个memcpy 需要实际附加,而不是替换。所以第一个参数必须与之前的调用不同。
      • 注意第二个memcpy 的第三个参数中的+1。这是针对 NUL 终止符的。

      我也不确定您的 if 声明是否有意义。也许更明智的做法是确保g-&gt;db_cmd 有足够的空间来存放您要复制的内容。您可以通过sizeof(如果db_cmd 是一个字符数组)或跟踪您的堆分配有多大(如果db_cmd 是通过malloc 获得的)来做到这一点。所以也许最有意义的是:

      size_t param_value_len = strlen(l->db.param_value.val),
             del_const_len = strlen(l->del_const);
      
      // Assumption is that db_cmd is a char array and hence sizeof(db_cmd) makes sense.
      // If db_cmd is a heap allocation, replace the sizeof() with how many bytes you
      // asked malloc for.
      //
      if (param_value_len + del_const_len < sizeof(g->db_cmd))
      {
         memcpy(g->db_cmd, l->db.param_value.val, param_value_len);
         memcpy(g->db_cmd + param_value_len, l->del_const, del_const_len + 1);
      }
      else
      {
         // TODO: your buffer is not big enough.  handle that.
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-05-23
        • 2013-02-04
        • 2011-02-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-01-13
        相关资源
        最近更新 更多