代码之家  ›  专栏  ›  技术社区  ›  Nicolas C.

实现一个好的“itoa()”函数的正确方法是什么?

  •  9
  • Nicolas C.  · 技术社区  · 16 年前

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <ctype.h>
    
    char * itoa(int i) {
      char * res = malloc(8*sizeof(int));
      sprintf(res, "%d", i);
      return res;
    }
    
    int main(int argc, char *argv[]) {
     ...
    
    11 回复  |  直到 10 年前
        1
  •  7
  •   Steve Jessop    12 年前

    malloc 为空。

    名字 itoa

    char *itoa(int value, char * str, int base);
    

    如果你不想依赖你的平台,我还是建议你遵循这个模式。在C中返回新分配内存的字符串处理函数通常比它们在长期运行中的价值要麻烦得多,因为大多数情况下,您最终会进行进一步的操作,因此您必须释放大量中间结果。例如,比较:

    void delete_temp_files() {
        char filename[20];
        strcpy(filename, "tmp_");
        char *endptr = filename + strlen(filename);
        for (int i = 0; i < 10; ++i) {
            itoa(endptr, i, 10); // itoa doesn't allocate memory
            unlink(filename);
        }
    }
    

    与。

    void delete_temp_files() {
        char filename[20];
        strcpy(filename, "tmp_");
        char *endptr = filename + strlen(filename);
        for (int i = 0; i < 10; ++i) {
            char *number = itoa(i, 10); // itoa allocates memory
            strcpy(endptr, number);
            free(number);
            unlink(filename);
        }
    }
    

    伊藤 sprintf 不支持,那么你可以考虑不打电话 把格式数据写成串 . 但是如果你想要一根10进制的弦,那么你的第一直觉是对的。绝对没有什么不正确的 %d

    下面是 伊藤 ,仅适用于基数10:

    char *itobase10(char *buf, int value) {
        sprintf(buf, "%d", value);
        return buf;
    }
    

    下面是一个结合了snprintf风格的缓冲区长度方法:

    int itobase10n(char *buf, size_t sz, int value) {
        return snprintf(buf, sz, "%d", value);
    }
    
        2
  •  10
  •   dodgy_coder    12 年前
    // Yet, another good itoa implementation
    // returns: the length of the number string
    int itoa(int value, char *sp, int radix)
    {
        char tmp[16];// be careful with the length of the buffer
        char *tp = tmp;
        int i;
        unsigned v;
    
        int sign = (radix == 10 && value < 0);    
        if (sign)
            v = -value;
        else
            v = (unsigned)value;
    
        while (v || tp == tmp)
        {
            i = v % radix;
            v /= radix; // v/=radix uses less CPU clocks than v=v/radix does
            if (i < 10)
              *tp++ = i+'0';
            else
              *tp++ = i + 'a' - 10;
        }
    
        int len = tp - tmp;
    
        if (sign) 
        {
            *sp++ = '-';
            len++;
        }
    
        while (tp > tmp)
            *sp++ = *--tp;
    
        return len;
    }
    
    // Usage Example:
    char int_str[15]; // be careful with the length of the buffer
    int n = 56789;
    int len = itoa(n,int_str,10);
    
        3
  •  3
  •   kbrimington    16 年前

    malloc(8*sizeof(int)) 在大多数机器上会给您32个字节,这对于int的文本表示可能是多余的。

        4
  •  3
  •   chux    7 年前

    好的 int 一串 itoa()

    • [INT_MIN...INT_MAX] ,底座 [2...36] 没有缓冲区溢出。
    • 不假定 内景
    • 不需要2的补码。
    • 不需要 unsigned 比…有更大的正范围 . 换句话说,不使用 .
    • 允许使用 '-' 对于负数,即使 base != 10 .

    根据需要定制错误处理(需要C99或更高版本):

    char* itostr(char *dest, size_t size, int a, int base) {
      // Max text needs occur with itostr(dest, size, INT_MIN, 2)
      char buffer[sizeof a * CHAR_BIT + 1 + 1]; 
      static const char digits[36] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    
      if (base < 2 || base > 36) {
        fprintf(stderr, "Invalid base");
        return NULL;
      }
    
      // Start filling from the end
      char* p = &buffer[sizeof buffer - 1];
      *p = '\0';
    
      // Work with negative `int`
      int an = a < 0 ? a : -a;  
    
      do {
        *(--p) = digits[-(an % base)];
        an /= base;
      } while (an);
    
      if (a < 0) {
        *(--p) = '-';
      }
    
      size_t size_used = &buffer[sizeof(buffer)] - p;
      if (size_used > size) {
        fprintf(stderr, "Scant buffer %zu > %zu", size_used , size);
        return NULL;
      }
      return memcpy(dest, p, size_used);
    }
    
        5
  •  2
  •   llasram    16 年前

    我不太清楚你从哪儿来的 8*sizeof(int) 作为最大可能的字符数-- ceil(8 / (log(10) / log(2))) 产生的乘数 3* sprintf() :

    char *
    itoa(int i) 
    {
        int n = snprintf(NULL, 0, "%d", i) + 1;
        char *s = malloc(n);
    
        if (s != NULL)
            snprintf(s, n, "%d", i);
        return s;
    }
    

    HTH公司

        6
  •  2
  •   Adam    10 年前

    我发现了一个有趣的资源,用于处理itoa实现的几个不同问题
    你可能也想查一下
    itoa() implementations with performance tests

        7
  •  1
  •   R.. GitHub STOP HELPING ICE    16 年前

    printf stdout 或文件,使用 / fprintf . 否则,请使用 snprintf 有足够大的缓冲器 3*sizeof(type)+2 字节或更多。

        8
  •  1
  •   Sergey Solovyev    8 年前

    sprintf相当慢,如果性能很重要的话,它可能不是最好的解决方案。

    int  num_iter = sizeof(int) / 4;
    

    const char digits[]={0',1',2',3',4',5',6',7',8',9',a',b',c',d',e',f'};

    /* skip zeros in the highest positions */
    int i = num_iter;
    for (; i >= 0; i--)
    {
        int digit = (value >> (bits_per_digit*i)) & 15;
        if ( digit > 0 )  break;
    }
    
    for (; i >= 0; i--)
    {
        int digit = (value >> (bits_per_digit*i)) & 15;
        result[len++] = digits[digit];
    }
    

    对于小数,最好使用一个足够大的静态数组,以逆序记录数字,请参见 here

        9
  •  1
  •   Joseph Soliman    7 年前
    • 整数到ASCII需要从标准整数类型转换数据 转换为ASCII字符串。
    • 所有操作都需要使用指针算法来执行,而不是数组索引。
    • 要转换的数字将作为有符号32位整数传入。
    • 有符号的32位数字将有一个最大字符串大小(提示:考虑基数2)。
    • 必须在转换后的c字符串的末尾放置空终止符函数应返回转换数据的长度(包括负号)。
    • 示例myu itoa(ptr,1234,10)应返回长度为5的ASCII字符串(包括空终止符)。
    • 此函数需要处理签名数据。
    • 不能使用任何字符串函数或库。

    uint8_t my_itoa(int32_t data, uint8_t *ptr, uint32_t base){
            uint8_t cnt=0,sgnd=0;
            uint8_t *tmp=calloc(32,sizeof(*tmp));
            if(!tmp){exit(1);}
            else{
                for(int i=0;i<32;i++){
                if(data<0){data=-data;sgnd=1;}
                if(data!=0){
                   if(data%base<10){
                    *(tmp+i)=(data%base)+48;
                    data/=base;
                   }
                   else{
                    *(tmp+i)=(data%base)+55;
                    data/=base;
                   }
                cnt++;     
                }
               }
            if(sgnd){*(tmp+cnt)=45;++cnt;}
            }
         my_reverse(tmp, cnt);
         my_memcopy(tmp,ptr,cnt);
         return ++cnt;
    }
    
    • ASCII到整数需要将数据从ASCII表示的字符串转换回整数类型。
    • 所有操作都需要使用指针算法来执行,而不是数组索引
    • 要转换的字符串作为uint8\t*指针(ptr)传入。
    • 字符集中的位数作为uint8\t整数(数字)传入。
    • 你应该能够支持基地2至16。
    • 不能使用任何字符串函数或库。

    int32_t my_atoi(uint8_t *ptr, uint8_t digits, uint32_t base){
        int32_t sgnd=0, rslt=0;
        for(int i=0; i<digits; i++){
            if(*(ptr)=='-'){*ptr='0';sgnd=1;}
            else if(*(ptr+i)>'9'){rslt+=(*(ptr+i)-'7');}
            else{rslt+=(*(ptr+i)-'0');}
            if(!*(ptr+i+1)){break;}
            rslt*=base;
        }
        if(sgnd){rslt=-rslt;}
        return rslt;
    }
    
        10
  •  0
  •   nategoose    16 年前

    #include <string.h>
    #include <stdlib.h>
    #include <math.h>
    
    char * itoa_alloc(int x) {
       int s = x<=0 ? 1 ? 0; // either space for a - or for a 0
       size_t len = (size_t) ceil( log10( abs(x) ) );
       char * str = malloc(len+s + 1);
    
       sprintf(str, "%i", x);
    
       return str;
    }
    

    如果您不想使用数学/浮点函数(并且必须在数学库中链接),您应该能够通过搜索Web找到非浮点版本的log10,并执行以下操作:

    尺寸长度=我的log10(abs(x))+1;

    这可能会给你比你需要的多1个字节,但你已经有足够的字节了。

        11
  •  0
  •   Brandon Horsley    16 年前

    我可以提几个建议。您可以使用静态缓冲区和strdup来避免在后续调用中重复分配过多内存。我还要添加一些错误检查。

    char *itoa(int i)
    {
      static char buffer[12];
    
      if (snprintf(buffer, sizeof(buffer), "%d", i) < 0)
        return NULL;
    
      return strdup(buffer);
    }
    

    如果这将在多线程环境中调用,请从缓冲区声明中删除“static”。

        12
  •  -1
  •   user411313    16 年前
    main()
    {
      int i=1234;
      char stmp[10];
    #if _MSC_VER
      puts(_itoa(i,stmp,10));
    #else
      puts((sprintf(stmp,"%d",i),stmp));
    #endif
      return 0;
    }