在标准C中将整型转换为字符串

4

我刚开始学习 C 语言。

我正在寻找一个将 int 转换成字符串的函数示例。我发现 itoa,但这不是标准 C 的一部分。

我还找到了 sprintf(str, "%d", aInt);,但问题是我不知道所需 str 的大小。那么,我应该如何传递正确的输出字符串大小呢?


2
你在使用gcc吗?如果是的话,asprintf会是你的好帮手。 - Dan
1
如果aint是一个32位(有符号或无符号)的量,那么12个字符(10个数字、符号、尾随空值)就足够了。如果它是一个64位有符号量,则需要21个字符(19个数字、符号、尾随空值);对于没有符号的量,21个字符就足够了,但是如果有符号,你需要22个字符。通常的技巧是分配比必要的稍微多一点的空间。你可以使用空字符串和零长度检查snprintf()的返回值,以找到需要多少空间。你可以看看建议使用的asprintf(),但它还比较新,可移植性有限。 - Jonathan Leffler
5个回答

5

有最佳的方法来适当地调整数组大小,以考虑 sizeof(int) 的差异,但是乘以 4 对于十进制足够了。对于 sizeof(int)==1 的边缘情况需要加上 +1。

int x; // assign a value to x
char buffer[sizeof(int) * 4 + 1];
sprintf(buffer, "%d", x);

如果您需要从函数返回字符串指针,应该分配缓冲区而不是使用堆栈内存:

char* integer_to_string(int x)
{
    char* buffer = malloc(sizeof(char) * sizeof(int) * 4 + 1);
    if (buffer)
    {
         sprintf(buffer, "%d", x);
    }
    return buffer; // caller is expected to invoke free() on this buffer to release memory
}

谢谢...如果我把上面的代码放在一个函数里,这个函数应该返回char *还是char[]? - user836026
2
@user836026:你不能仅仅从一个函数中返回一个数组。你需要将一个适当大小的数组传递到函数中(最简单的方法),或者你需要进行动态内存分配(使用malloc()等,这更难)。如果你传递了数组,请同时传递大小,并在函数内部使用snprintf()来确保安全。如果你这样做,应该检查溢出错误,但是大多数函数都应该进行错误检查。 - Jonathan Leffler
1
@user836026 - 就是Jonathan刚才说的。我更新了我的答案,展示了如何使用malloc分配一个数组。 - selbie
@selbie 谢谢。 - user836026
我应该指出,在理论上,如果 sizeof(int) == 1,那么您需要将分配的大小增加1,以便负数可以与空终止符一起适合。实际上,sizeof(int) 总是 > 1,因此不需要这样做。 - selbie
1
@selbie 周围有 sizeof(int)==1 的系统(它们具有最小可寻址单元为16位或32位)。我猜代码应该是 sizeof(int) * CHAR_BIT / 2 - M.M

2

在便携式C中,最简单的方法是使用snprintf来计算所需数组的大小,然后使用sprintf进行实际转换。例如:

char buffer[snprintf(NULL, 0, "%d", x) + 1];
sprintf(buffer, "%d", x);

值得注意的是,在C99之前,这种方法是不可行的。而且有一种更简洁的替代方案,适用于C99之前的所有整数类型。这在另一个回答中使用乘法技巧描述,但我注意到那里提出的技巧也不是完全可移植的。在CHAR_BIT不为8的环境(例如,一些DSP使用16位或32位字节)中,您需要更改乘数。
我在回答另一个问题时提供了类似的技巧。该代码使用CHAR_BIT确保可移植性,即使CHAR_BIT发生变化。它被呈现为宏,因此它具有内部文档功能;它告诉您高级别描述是什么,而仅仅使用乘法无法做到这一点。
#include <limits.h>
#include <stddef.h>
#include <stdio.h>

#define digit_count(num) (1                                /* sign            */ \
                        + sizeof (num) * CHAR_BIT / 3      /* digits          */ \
                        + (sizeof (num) * CHAR_BIT % 3 > 0)/* remaining digit */ \
                        + 1)                               /* NUL terminator  */

int main(void) {
    short short_number = -32767;
    int int_number = 32767;
    char short_buffer[digit_count(short_number)] = { 0 };
    char int_buffer[digit_count(int_number)];
    sprintf(short_buffer, "%d", short_number);
    sprintf(int_buffer, "%d", int_number);
}

对于建议使用snprintf来计算数组大小,我给予+1的支持。然而,值得一提的是,在C99之前似乎没有定义这种行为。从man snprintfCONFORMING TO中可以看到:“SUSv2和C99相互矛盾:当size=0时,SUSv2规定返回一个小于1的未指定返回值,而C99允许在这种情况下str为空,并且像往常一样返回(总是)输出字符串中将要写入的字符数。” - Will
@Will 你说得没错,不过我有一点小意见。我认为既然SUS现在使用了更加现代的标准来定义C语言,那么除非使用了[SUSv2]或[C89]标签,否则C语言应该是C99或C11。此外,VLA在C89中是无法工作的...这可能会成为接下来部分的一个很好的过渡。我现在会进行编辑 :) - autistic
@Will,你现在觉得这个答案怎么样? - autistic

1
使用C99 snprintf()函数。它可以计算需要多少空间。
int needed = snprintf(NULL, 0, "%s", value);
if (needed < 1) /* error */;
char *representation = malloc(needed + 1); // add 1 for '\0'
if (!representation) /* error */;
sprintf(representation, "%d", value);
// ... use representation ...
free(representation);

0

有一种方法可以不使用任何函数来实现,例如这个(可能有点原始,但仍然有效):

char dec_rev[255];
dec_rev[0] = '\0';
int i = 0;

while (val != 0) {
    int temp = val % 10;
    dec_rev[i] = temp + '0';
    //printf("%c\n", dec_rev[i]);
    val /= 10;
    if (val == 0) {
        dec_rev[i + 1] = '\0';
        break;
    }
    i++;
}

char dec[255];
i = 0;
for (int j = strlen(dec_rev) - 1; j != -1; j--) {
    dec[i] = dec_rev[j];
    i++;
}

最终,我们将我们的整数存储在dec [255]中。


0

很奇怪这没有被提到,但是一个十进制整数的表示大小是ceil(log10(value)); (或者如果你想要写出它的整数版本,就是log10)

因此 ceil(log10(5)) => 1

并且 ceil(log10(555)) => 3

ceil(log10(1000000000)) => 9

显然,如果需要符号,则需要额外的空间,另一个空间用于'\0'。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接