【问题标题】:Concatenating a struct containing variable sized data in one char*在一个 char* 中连接包含可变大小数据的结构
【发布时间】:2014-05-28 17:07:47
【问题描述】:

我有一个结构:

typedef struct message
{
    char* dchatver;
    int contype;
    int conlen;
    char* context;
} message_t;

我需要一个函数来获取指向已填充message_t 的指针,它将message_t 写入文件描述符。 message_t 可以包含 dchatver = "DCHAT:1.0"; contype = 3; conlen = 6; context = "hello"; 我想将它连接成一个 char[totallength] 看起来像“DCHAT:1.0\n3\n6\nhello\n

我的问题是如何将dchatvercontypeconlencontext 连接到一个char*,以便将其用于写入?我考虑过使用snprintf,但我不知道如何确定给定message_t 的长度。

【问题讨论】:

  • 您能否展示一些示例,说明在给定示例 message_t 的情况下输出的样子?您想如何表示dchatver 的结束,以便您知道contype 的开始位置?
  • 对不起,如果我不够清楚hacks.. message_t 可以包含例如dchatver = "DCHAT:1.0"; contype = 3; conlen = 6; context = "hello"; 我想将它连接成一个字符[totallength].. 看起来像@ 987654337@

标签: c struct printf


【解决方案1】:
void WriteMessage(int fd, message_t *message)
   {
   char *writeBuf=NULL;
   size_t writeBufSize;

   /* Determine the size of the output string. */
   writeBufSize = 1 + snprintf(NULL, 0, "%s\n%d\n%d\n%s\n",
         message->dchatver,
         message->contype,
         message->conlen,
         message->context
         );

   /* Allocate memory for the string */
   writeBuf = malloc(writeBufSize);

   /* Initialize the string. */
   snprintf(writeBuf, writeBufSize, "%s\n%d\n%d\n%s\n",
         message->dchatver,
         message->contype,
         message->conlen,
         message->context
         );

   /* write the record */
   write(fd, writeBuf, writeBufSize - 1);

   free(writeBuf);

   return;
   }

【讨论】:

  • 就是这样!我不会想到使用 snprintf 的返回值来确定长度!但是如果我在这个函数中写 writeBuf,我就不需要 malloc 缓冲区了,对吗?
  • @apoc, write() 不允许 formatString,只有缓冲区。您可能仍然需要使用 malloc() 来为 write() 生成格式化字符串。
【解决方案2】:

我将此解释为您希望将变量值打印到屏幕上。

如果您确定每个 char* 都正确地以 null 结尾,则可以只使用 sprintf() 而不是 snprintf()。

例如,这将在一个新行上打印出每个变量,并将字符串封装在引号中:

char buf[256]; // make sure this buffer is large enough
message_t msg;

//
// assuming the variable 'msg' gets filled in here
//

sprintf(buf, "dchatver = \"%s\"\ncontype = %d\nconlen = %d\ncontext = \"%s\"\n", 
   msg.dchatver, 
   msg.contype, 
   msg.conlen, 
   msg.context);

// print the string to console, or do other stuff
printf("%s\n", buf);

【讨论】:

  • 您的实现有一个巨大的漏洞,使其受到缓冲区溢出攻击。此外,即使在正常操作中msg.dchatver 字段的长度超过 256 字节,也会破坏程序。
  • 这正是我的想法,但我仍然遇到的问题是如何确定 buf 长度,因为我希望它只是 message_t 的给定数据的长度..跨度>
  • 正如@randomusername 所说,如果您完全采用上述方法,您必须确信缓冲区大小是正确的。在某些情况下,已知某些字符串会包含 N 个字符或更少,因此您可以选择适当的界限。
  • 我明白了。对于字符串,它很容易确定长度,然后我可以添加 sprintf 中使用的 "\n" 的数量。但是我怎么知道,两个int的contype和conlen会占用多少空间呢?
  • 类似于字符串,如果你知道整数是有界的,你可以估计它需要打印的字符数。例如,如果 'conlen' 始终为 [0, 99],则最多需要 2 个字符来打印。 @randomusername 的真正担忧是您做出了这样的假设,但随后为“conlen”输入了一个类似 999 的值。
【解决方案3】:

这是一个函数,可以将message_t 转换为您想要使用的格式的字符串。

char* messageToString(message_t m)
{
   char buffer1[50]; // If your numbers can occupy more than 50 spaces, 
   char buffer2[50]; // these will have to be updated.
   char* ret = NULL;
   size_t len = 0;

   sprintf(buffer1, "\n%d", m.contype);
   sprintf(buffer2, "\n%d\n", m.conlen);
   len = strlen(m.dchatver) + strlen(buffer1) + strlen(buffer2) + strlen(m.context);
   ret = malloc(len+2); // One extra for the newline. One extra for the null

   strcpy(ret, m.dchatver);
   strcat(ret, buffer1);
   strcat(ret, buffer2);
   strcat(ret, m.context);
   strcat(ret, "\n");

   return ret;
}

确保在返回值上调用free 以防止内存泄漏。

【讨论】:

  • 这会很好用!但是如何确定两个 int 值的确切缓冲区大小?我不想只使用“足够大”的缓冲区..
  • @apoc:如果你有一个 32 位整数,那么 11 个字节(对于 NULL 加一个)对于一个以 10 为底的整数就足够大了。如果它总是正数,则为 10 个字节。这里的 R Sahu 提供 50,这有点过分,但无害。
【解决方案4】:

您可以使用strlen 找到dchatvercontext 的长度。要找到contypeconlen 的长度比较困难,但您可以走捷径。您可以使用以下方法计算它们的最大长度,而不是查找这些特定字段的长度:

#include <limits.h>

#define INT_STR_LEN_UPPER_BOUND (sizeof (int) * CHAR_BIT * 3.4)

因此您可以为每个int 字段分配这么多空间,将其传递给snprintf,然后在其上使用strlen 以获得整个字符串的实际长度。像这样:

size_t s = strlen (msg->dchatver) + strlen (msg->context) + 
    sizeof (int) * 54.4 + 4;
char *out = calloc (s, sizeof *out);
snprintf (out, s, "%s %d %d %s", msg->dchatver, msg->contype, 
          msg->conlen, msg->context);

【讨论】:

  • 我正在分析INT_STR_LEN_UPPER_BOUND,并认为它在某些情况下会低估。然而,我意识到它实际上过度分配了 insane 边距。我向你保证,一个 32 位整数不会占用 102 个字节。我认为你的意思是 / 3.3 + 1 而不是 * 3.4(我发现 CHAR_BITsizeof(int) 的任何模糊合理的数字都是安全的,尽管我没有证明任何东西)你是可能已经意识到了,但使用 log(10) ~ 3.32192809489 有时会稍微减少开销。
  • 另外,我认为sizeof(int)*54.4 应该是1+sizeof(int)*2.40823996531。 (那个幻数是log(256,10)
【解决方案5】:

诀窍是您不一定需要知道格式化消息字符串的长度,您只需要一个上限即可。而且您知道格式化的消息永远不会超过strlen(dchatver) + 10(用于contype)+ 10(用于conlen)+ conlen(用于上下文)+ 3(用于'\n's)。 (conlen 显然包含用于尾随 null 的空间,因此我们不一定要为此添加一个空间,但以防万一。)

神奇的数字 10 假设您的 int 类型是 32 位,并且始终是正数,如果这些假设不安全,您必须做更多的数学运算。

我喜欢这样做是为了灵活。 (我的 C 生锈了,所以这里可能有语法错误)

//this allows you to format to preallocated buffers, saving you
//allocation overhead, and possibly copy overhead
int printf_message(char* buffer, int len, message_t* msg) 
{
    assert(msg->conlen == strlen(msg->context)+1); //just in case
    int r = snprintf(buffer, len, "%s\n%d\n%d\n%s\n",
            msg->dchatver, msg->contype, msg->conlen,  msg->context);
    return r;
}

//this is slightly easier to use, with a small potential overhead
//free the return pointer with "free" when you're done
char* get_formatted_message(message_t* msg) 
{ 
    //contype is a 32bit positive integer, and therefore a maximum of 10 digits 
    //conlen is a 32bit positive integer, and therefore a maximum of 10 digits
    //we need 3 '\n's, and one '\0'.
    int len = strlen(msg->dchatver) + conlen + 24;
    char* buffer = malloc(len);
    if (buffer) 
    {
        printf_message(buffer, len, msg);
    }
    return buffer;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-02
    • 1970-01-01
    • 2018-12-28
    • 2016-11-21
    • 1970-01-01
    • 1970-01-01
    • 2011-08-19
    相关资源
    最近更新 更多