【问题标题】:Safely printing all attributes of struct in C在 C 中安全地打印结构的所有属性
【发布时间】:2014-03-14 20:19:28
【问题描述】:

我正在编写一个 toString 方法,该方法打印出指向结构的输入指针的所有属性。在阅读处理字符串的安全方法时,我最终创建了以下解决方案: 请注意,Person 结构具有属性名称、体重、身高和年龄(除 name 之外的所有整数,它是一个 char 数组)。

char* toString(struct Person* inputPerson)
{
    // Allocate memory to return string
    char* ret = malloc(sizeof (char) * 100);
    // copy "Name: " into string
    strcpy(ret, "Name: ");
    // safely copy name, at most leaving enough room for the other params (leaving 50 bytes) 
    strncat(ret, inputPerson->name, 100-50);
    // copy "Age: " into string
    strcat(ret, "\n\tAge: ");
    // create tmp char to allow us to convert ints age, weight, and height into strings
    char tmp[4];
    // safely convert string to int (max number of digits being 3)
    snprintf(tmp, sizeof(tmp), "%d", inputPerson->age);
    // safely copy at most 3 characters, plus a null terminating char
    strcat(ret, tmp); // the previous snprintf makes sure that tmp is not too large.
    // repeat previous 2 steps for both weight and height attributes
    strcat(ret, "\n\tWeight: ");
    snprintf(tmp, sizeof(tmp),  "%d", inputPerson->weight);
    strcat(ret, tmp);
    strcat(ret, "\n\tHeight: ");
    snprintf(tmp, sizeof(tmp), "%d", inputPerson->height);
    strcat(ret, tmp);

    // Return a pointer to the string
    return ret;
}

我的问题是:这是否矫枉过正?我要做的就是安全可靠地打印出每个属性。对于每个字符串,我必须在附加之前确保它是最大大小。对于每个整数,我必须将它打印成一个字符串(确保最大允许长度),然后将该字符串附加到我的返回字符串中。有没有更简单的方法?每次我查看“100-50”代码部分时,我的 heebie jeebies 都在增长:如何指定“分配给 ret 的大小”而不是 100?

【问题讨论】:

  • 为什么不使用单个snprintf 呼叫?如果缓冲区太小,您可以随时重新分配它并重试。 (snprintf manual page 有一个很好的例子来说明如何做到这一点。)
  • 如果没有一个小实用程序可以自动生成代码以根据源代码为您漂亮地打印或序列化struct,我会感到非常惊讶。对于不需要完整解析器的情况(例如,structs 中没有 structs),在 shell/sed/awk 中编写一个可能比仅编写 C 单个案例的代码。

标签: c security struct


【解决方案1】:

既然您无论如何都在mallocing 返回缓冲区,为什么不通过分配正确大小的缓冲区来让自己的生活更轻松呢?正如 Joachim 建议的那样,您甚至可以使用 snprintf 来做到这一点:

char* toString(struct Person* inputPerson)
{
    size_t space_required =
        snprintf(0, 0,
                 "Name: %s\n"
                 "\tAge: %d\n"
                 "\tWeight: %d\n"
                 "\tHeight: %d\n",
                 inputPerson->name,
                 inputPerson->age,
                 inputPerson->weight,
                 inputPerson->height);

    // space_required excludes the terminating NUL
    // sizeof(char) == 1 *by definition*
    char *ret = malloc(space_required+1);
    if (!ret)
        return 0;

    snprintf(ret, space_required+1,
             "Name: %s\n"
             "\tAge: %d\n"
             "\tWeight: %d\n"
             "\tHeight: %d\n",
             inputPerson->name,
             inputPerson->age,
             inputPerson->weight,
             inputPerson->height);
    return ret;
}

这可能看起来很重复。如果你的 C 库有asprintf,你可以避免重复:

char* toString(struct Person* inputPerson)
{
    char *ret;
    if (asprintf(&ret,
                 "Name: %s\n"
                 "\tAge: %d\n"
                 "\tWeight: %d\n"
                 "\tHeight: %d\n",
                 inputPerson->name,
                 inputPerson->age,
                 inputPerson->weight,
                 inputPerson->height) == -1)
        return 0;
    return ret;
}

如果您没有asprintf,您可以使用mallocvsnprintf 来实现它。我会把它作为练习;-)

【讨论】:

    猜你喜欢
    • 2022-06-13
    • 2021-11-22
    • 1970-01-01
    • 1970-01-01
    • 2010-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多