【问题标题】:Converting an int to a string in C, securely安全地将 int 转换为 C 中的字符串
【发布时间】:2014-09-05 17:40:18
【问题描述】:

根据top answer on SO,这是我在 C 中将整数转换为字符串时应该做的:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <math.h>

#define digits(x) ((int)((ceil(log10(abs(x)))+1)*sizeof(char)))

int main () {
    int a = 345908220;
    long b = 23094809284358;
    unsigned int c = 3456789234;
    uint8_t d = 242;
    int64_t e = -840958202029834;

    int dstr_len = digits(a) + digits(b) + digits(c) +
                   digits(d) + digits(e) + 5;

    char dstr[dstr_len];
    sprintf(dstr, "%d %ld %u %u %" PRIu64, a, b, c, d, e);

    printf("%s\n", dstr);

    return 0;
}

但是,这似乎效率低得离谱。我必须将我的程序链接到 libmath,并对我想要打印的每个整数进行 三个 数学调用。另请注意,通过计算格式字符串中的空格数,我必须将5 添加到我的缓冲区中,而不仅仅是1 用于NUL 终止符。这似乎也容易出错,并可能导致缓冲区溢出。

那么,有没有什么好的标准函数可以为我计算缓冲区的大小?

我正在尝试编写安全的 C。

【问题讨论】:

  • 您始终可以自己编写。这并不难(并且可能非常有效),但需要考虑处理“边缘”情况,例如大型 64 位值。

标签: c string string-formatting unsigned


【解决方案1】:

如果您的编译器有snprintf() 可用,您可以请求格式化的缓冲区长度,然后进行相应的分配:

int dstr_len = snprintf(NULL, 0, "%d %ld %u %u %" PRIu64, a, b, c, d, e) + 1;

char dstr[dstr_len];
//
// NOTE: variable-length arrays are NOT supported in all compilers!
// A more portable solution is:
//
// char *dstr = malloc(sizeof(char) * dstr_len);

snprintf(dstr, dstr_len, "%d %ld %u %u %" PRIu64, a, b, c, d, e);

// free(dstr);

【讨论】:

  • 优秀。我找到了asprintf,但它不是标准的,总是需要动态分配。这是标准的,并不严格要求动态分配。喜欢它。
  • 是的,返回值是+1。
  • sizeof(char) 的定义是“1”,所以在 malloc 中不需要它,只需 char * dstr = malloc(dstr_len); 就足够了
  • @JohnHascall: sizeof(char) 确实是 1,但这并不能保证 char 是 1 8 位字节。考虑CHAR_BIT &gt; 8 的系统,这意味着char 的大小超过8 位。 malloc() 处理 8 位八位字节,但 snprintf() 处理 char,因此使用 malloc((CHAR_BIT / 8) * dstr_len); 会更准确。大多数开发人员假设CHAR_BIT == 8,这在大多数系统上都是正确的,但不是所有系统。
  • @RemyLebeau,这完全不正确。 malloc 分配“字节”,其中一个字节需要很多才能保存一个字符。
【解决方案2】:

您可以为此使用 snprintf。从手册页:

函数 snprintf() 和 vsnprintf() 不会写入超过 size 字节(包括终止空字节 ('\0'))。如果由于此限制而截断了输出,则返回 value 是字符数(不包括终止的空字节) 如果有足够的空间可用,则写入最终字符串。因此,大小的返回值 或更多意味着输出被截断。

因此,您可以使用 0 作为大小调用它并捕获返回值,然后根据该值进行分配。

【讨论】:

    【解决方案3】:

    您可以使用asprintf,它会为您分配足够大的输出字符串。

    不要忘记释放输出字符串,因为它是动态分配的。

    asprintf 在 Mac OSX、Linux 和 BSD 上可用。如果您希望在其他平台上使用source code,可以从 Apple 获得。

    例子:

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdint.h>
    #include <inttypes.h>
    
    int main () {
        int a = 345908220;
        long b = 23094809284358;
        unsigned int c = 3456789234;
        uint8_t d = 242;
        int64_t e = -840958202029834;
    
        char *dstr;
        asprintf(&dstr, "%d %ld %u %u %" PRIu64, a, b, c, d, e);
        if (dstr == NULL) {perror(NULL), exit(1);}
    
        printf("%s\n", dstr);
        free(dstr);
    
        return 0;
    }
    

    【讨论】:

      【解决方案4】:

      大多数 C“运行时环境”最多为 64 位。您可以测试 int 最多为 64 位(使用 &lt;stdint.h&gt; 和 &lt;limits.h&gt;)然后在足够大的缓冲区(32 字节足够264(十进制)。

      请参阅 limits.h 上的 Posix 规范,它定义了 WORD_BIT 所以

      #if WORD_BIT > 64
      #error cannot compile on this machine
      #endif
      
      char buf[32];
      snprintf(buf, sizeof(buf), "%d", a);
      

      顺便说一句,&lt;stdint.h&gt; 定义了several types。你可能想要intmax_t

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-10
        • 2021-10-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多