代码之家  ›  专栏  ›  技术社区  ›  LukeN

C变量参数重构

  •  6
  • LukeN  · 技术社区  · 15 年前

    我有一个功能 irc_sendline 可以这样叫 printf 可以

    irc_sendline(s, "A strange game.\nThe only %s is not to play.", "winning move");
    

    它工作得很好,但我对它的实现不满意:

    int irc_sendline(irc *iobj, char *msg, ...)
    {
       char tmp_msg[BUFSIZE], fmsg[BUFSIZE];
       va_list args;
       int len;
    
       va_start(args, msg);
    
       strncpy(tmp_msg, msg, BUFSIZE);
       strncat(tmp_msg, "\r\n", BUFSIZE);
    
       len = vsnprintf(fmsg, BUFSIZE, tmp_msg, args);
       len = send(iobj->fd, fmsg, len, 0);
    
       return len;
    }
    

    您知道,我在这里使用了2个“临时”缓冲区,因为我首先必须将原始消息从函数参数复制到临时缓冲区,以便将“\r\n”附加到它,然后将该临时缓冲区复制到 另一个 用函数调用提供的参数进行实际格式化的临时缓冲区,并且仅 然后 我可以把东西送到路上。

    我怎么能让这个更干净,更好?


    谢谢你给我的所有意见,我以为我唯一的问题是那里一团糟,但实际上它是一个定时炸弹!我的新功能如下:

    int irc_sendline(irc *iobj, char *msg, ...)
    {
       char buffer[BUFSIZE];
       va_list args;
       int res_str_len;
       int sent;
    
       va_start(args, msg);
    
       res_str_len = vsnprintf(buffer, BUFSIZE, msg, args);
    
       sent =  send(iobj->fd, buffer, res_str_len, 0);
       sent += send(iobj->fd, "\r\n", 2, 0);
    
       return sent;
    }
    

    如果可以的话,我会在这里接受多个答案,但是我。

    5 回复  |  直到 15 年前
        1
  •  5
  •   Jerry Coffin    15 年前

    首次使用 vsnprintf 若要格式化数据,请将“\r\n”附加到该结果中。或者,只需使用第二个电话 send 发送“\r\n”。

        2
  •  3
  •   AnT stands with Russia    15 年前

    首先,如果你对“清洁剂”感兴趣,停止使用 strncpy . 尽管名称有误导性(与流行的观点相反),但这不是一个有限长度的字符串复制函数。可以肯定地说 斯特朗普 是一个在今天几乎没有实际用途的函数。当你看到 斯特朗普 在代码中使用,“清洁剂”立即变得毫无疑问(除了逐渐缩小的箱子之外) 斯特朗普 实际上是打算用于)。

    事实上,您的代码被破坏是因为您使用了 斯特朗普 :如果格式字符串的长度大于缓冲区的长度, 斯特朗普 不附加终止的空字符 结果,意味着 strncat 呼叫将崩溃(最多)。

    标准库中不存在长度有限的复制功能,但通常由实现以名称提供 strlcpy . 如果您正在使用的实现没有提供一个-自己编写一个并使用它。

    您的代码也因使用错误而损坏 斯特兰卡 : 斯特兰卡 不将完整缓冲区的长度作为最后一个参数。相反, 斯特兰卡 预期可用的 余数 缓冲区的。所以,如果你想使用 斯特兰卡 ,您必须首先计算上一次复制后缓冲区末尾剩余的空间。再说一遍,即使 斯特兰卡 斯特朗普 ,您最好使用非标准(但通常由实现提供)函数 strlcat 这实际上是你想的那样 斯特兰卡 工作。

    第二,而不是添加 \r\n 提前一部分,为什么不事后再做呢?利用这个事实 vsnprintf 返回写入输出缓冲区的字符数,只需将 \r , the \n \0 后面的字符 VSNPrTNF 完成工作。你不必使用 斯特兰卡 为此目的。只需将字符直接写入缓冲区,当然要确保您不会越过边界。

        3
  •  1
  •   R Samuel Klatchko    15 年前

    自从 \r\n 将在格式化字符串的末尾结束,为什么不在后面复制:

    va_start(args, msg);
    len = vsnprintf(fmsg, BUFSIZE, msg, args);
    strncat(fmsg, "\r\n", BUFSIZE - strlen(fmsg) - 1);
    

    注意,我也修正了strncat的参数。

        4
  •  0
  •   Michael Dorgan    15 年前

    除非你想把msg用于strcat(不安全和邪恶,因为你不知道字符串的大小),否则我认为你必须使用2个缓冲区。

    顺便说一句,我将考虑strncpy(…,bufsize-2),以便\r\n始终将其放到您的消息上,因此字符串始终换行。

        5
  •  0
  •   Chris Dodd    15 年前

    代码的一个主要问题是:vsnprintf返回如果缓冲区无限大,将放置在缓冲区中的字符数,如果缓冲区不够大,则可能大于bufsize。因此,如果有一条消息溢出,那么您将在缓冲区结束后从发送随机垃圾。您需要添加一行

    if (res_str_len >= BUFSIZE) res_str_len = BUFSIZE-1
    

    在vprintf之后,如果要实际截断消息