嵌入式C语言中将无符号整数转换为字符串的源代码

3

不使用标准库utoa,我正在寻找utoa的源代码,以便可以为特定项目自定义它。我有一个无符号整数(32位),其输出格式为0xFFFF_FFFF。

我还在寻找无符号整数和半字转换为二进制字符串的源代码。


2
utoa 不是 C 标准库的一部分。(也许它是你的 libc 实现的一部分,但不是标准。) - Chris Lutz
这是一项作业任务吗?如果是,请标记为作业。 - old_timer
不,这不是一份作业,为什么每个人都认为是呢! - riscy
6个回答

7

试试这个:

char *dec(unsigned x, char *s)
{
    *--s = 0;
    if (!x) *--s = '0';
    for (; x; x/=10) *--s = '0'+x%10;
    return s;
}

你需要使用调用者提供缓冲区的末尾指针来调用它,并且该函数将返回字符串的开头指针。为了安全起见,缓冲区的长度至少应为3*sizeof(int)+1
当然,这很容易适应其他进制。

谢谢,我尝试一下十六进制(hex)的编码。 - riscy
对于基数为16的情况,您需要单独处理数字<10和>=10,或者使用Kerrek的字符串索引解决方案。 - R.. GitHub STOP HELPING ICE
非常聪明!不过我有一个问题:为什么你要减小指针并在函数开头分配空终止符?这样看来,你会浪费缓冲区的一个字节。为什么不使用 *s = 0 呢? - Nedo
1
@Nedo:我所说的“end”是指最后一个索引之后,即buf + sizeof buf。在我看来,这是C语言中“end”的标准习惯用法;另一种版本具有类似于基于1的索引的偏移量问题。 - R.. GitHub STOP HELPING ICE

3

在谷歌上很容易找到很多itoa源文件...这应该能给你想要的结果,例如来自opensource.apple.com的itoa

或者从头开始编写,这并不太难。


1
大部分在这里提到/建议使用模数%运算符的函数对于嵌入式系统来说非常昂贵。因此,我想使用除以10的方法是唯一明显的选择。这里是方法:
/*for byte which is 3 digit most*/


void itoa(unsigned char value,char *desitination)
{
desitination[0] = '\0';
desitination[1] = '\0';
desitination[2] = '\0';
desitination[3] = '\0';//you at least 4 char array, last char is NULL
while (value >= 100)
{
    desitination[0]++;
    value -= 100;
}
desitination[1] = '0';

while (value >= 10)
{
     desitination[1]++;
     value -= 10;
}
value+= '0';
desitination[2] =value;
}

1

这并不是非常困难。不断地除以10,并使用余数模10作为索引进入“0123455679”。您从右到左构建它,因此必须缓冲结果并反向返回:

char * utoa(unsigned int n)
{
  char * res, buf[30]; // long enough for largest number
  unsigned int i, counter = 0;

  if (n == 0)
    buf[counter++] = '0';

  for ( ; n; n /= 10)
    buf[counter++] = "0123456789"[n%10];

  res = malloc(counter);

  for (i = 0; i < counter; ++i)
    res[i] = buf[counter - i - 1];

  return res;
}

5
我很难抵制将对一个关于嵌入式系统问题的无用的malloc的答案打-1的冲动... - R.. GitHub STOP HELPING ICE
好的,你必须把结果放在某个地方。我相信原帖作者足够灵活,能够根据自己的需求进行调整,因为这个细节显然与基本算法无关! - Kerrek SB
这就是为什么我没有将其减1。但实际上,我们这些更懂行的人应该树立一个好榜样,而不是让问题提出者来修复解决方案(我猜有一半的问题提问者会“修复”您的答案以使用静态缓冲区而不是调用者提供的缓冲区,这不是一件好事...) - R.. GitHub STOP HELPING ICE
1
顺便提一下,对“0123456789”进行索引只是编写'0'+n的一种非常低效的方式,而后者不是一种黑客技巧,它是作为语言的一部分而必需的。 - R.. GitHub STOP HELPING ICE
前者允许使用任意符号,不过 :-) 但是使用你喜欢的任何东西。 (关于“真的很低效”:我的方法只会比 '0'+n%10 多出 一条 汇编指令...) - Kerrek SB
2
还有11个字节的常量字符串数据。:-) 在PIC库中,即使没有-fPIC,它也可以将GOT寄存器依赖(用于相对寻址)引入到本来完全是位置无关的函数中。 - R.. GitHub STOP HELPING ICE

1
#include <stdint.h>
#include <string.h>

char * utox(uint32_t n) {
    static char hexstr[sizeof(n)*2+1];
    char * p = hexstr + sizeof(hexstr) -1;
    int x;

    memset(hexstr, '0', sizeof(hexstr));

    *p-- = '\0';

    while (n) {
        x = n % 16;
        if (x < 10)
            *p-- = '0' + x;
        else
            *p-- = 'A' + x - 10;

        n /= 16;
    }

    return hexstr;
}

这样就行了,它可以进行零填充。只需更改函数参数中的n类型,即可使其适用于任何整数类型/大小。


显然,这个解决方案不是线程安全的。(并不是OP将其作为要求引用的。) - EboMike
这是一个嵌入式系统,通常情况下这没问题,除非他想在ISR或带线程的RTOS中使用它。大多数时候我喜欢这个解决方案,因为我可以确保数组边界,所以更安全。而且很容易操作指针,而不是自己管理数组。 - Vinicius Kamakura
我更喜欢的解决方案是让调用者传递一个缓冲区(和缓冲区大小)。但是嘿,通往罗马的路有很多... - EboMike

0

有许多itoa的实现方式。这是其中一种,考虑了从2到36的基数:

/**
 * C++ version 0.4 char* style "itoa":
 * Written by Lukás Chmela
 * Released under GPLv3.

 */
char* itoa(int value, char* result, int base) {
    // check that the base if valid
    if (base < 2 || base > 36) { *result = '\0'; return result; }

    char* ptr = result, *ptr1 = result, tmp_char;
    int tmp_value;

    do {
        tmp_value = value;
        value /= base;
        *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)];
    } while ( value );

    // Apply negative sign
    if (tmp_value < 0) *ptr++ = '-';
    *ptr-- = '\0';
    while(ptr1 < ptr) {
        tmp_char = *ptr;
        *ptr--= *ptr1;
        *ptr1++ = tmp_char;
    }
    return result;
}

更多变体可以在这里找到,其中包括各种实现的速度性能。


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