Linux中的itoa函数在哪里?

Posted

技术标签:

【中文标题】Linux中的itoa函数在哪里?【英文标题】:Where is the itoa function in Linux? 【发布时间】:2010-09-16 10:56:16 【问题描述】:

itoa() 是一个非常方便的函数,可以将数字转换为字符串。 Linux好像没有itoa(),有没有等价的功能还是非得用sprintf(str, "%d", num)

【问题讨论】:

有什么理由不使用sprintf(str, "%d", num)?是不是比itoa慢很多? @javapowered,一方面,itoa 允许任意基础转换,printf 说明符不允许。 @javapowered sprintf() 不是信号安全的 有什么理由不使用标准库中的gcvt() 【参考方案1】:

编辑:抱歉,我应该记得这台机器绝对是非标准的,为了学术目的插入了各种非标准的libc 实现;-)

由于itoa() 确实是非标准的,正如一些有用的评论者所提到的,最好使用sprintf(target_string,"%d",source_int) 或(更好,因为它可以避免缓冲区溢出)snprintf(target_string, size_of_target_string_in_bytes, "%d", source_int)。我知道它不像 itoa() 那样简洁或酷,但至少你可以编写一次,到处运行 (tm) ;-)

这是旧的(已编辑的)答案

您说默认gcc libc 与其他几个平台一样不包括itoa() 是正确的,因为它在技术上不是标准的一部分。有关更多信息,请参阅here。请注意,您必须

#include <stdlib.h>

当然您已经知道这一点,因为您想在 Linux 上使用 itoa() 在另一个平台上使用它之后,但是......代码(从上面的链接中窃取)看起来喜欢:

示例

/* itoa example */
#include <stdio.h>
#include <stdlib.h>

int main ()

  int i;
  char buffer [33];
  printf ("Enter a number: ");
  scanf ("%d",&i);
  itoa (i,buffer,10);
  printf ("decimal: %s\n",buffer);
  itoa (i,buffer,16);
  printf ("hexadecimal: %s\n",buffer);
  itoa (i,buffer,2);
  printf ("binary: %s\n",buffer);
  return 0;

输出:

Enter a number: 1750
decimal: 1750
hexadecimal: 6d6
binary: 11011010110

希望这会有所帮助!

【讨论】:

嗯,在 Debian 上编译它给了我“对 `itoa' 的未定义引用”。可能是我的系统出了点问题。 我在 Ubuntu 8.04 上得到了同样的结果。我在 stdio.h 或 stdlib.h 中也找不到对 itoa 的引用(并不令人惊讶,因为它不是标准的一部分) 已编辑以确保正确性,谢谢大家!抱歉,我总是忘记这不是一个普通的 Linux 机器 ;-) 我已经编辑了答案以包含缓冲区大小参数;我相信一切都是现在应该的样子,我认为论点本身的顺序没有问题。我错过了什么吗? 它不适用于 Linux?问题/答案的结果是什么(非标准似乎都是 linux?)【参考方案2】:

itoa 不是标准的 C 函数。你可以实现你自己的。它出现在 KernighanRitchie's The C Programming Language 的第一版中,第 60 页。The C Programming Language 的第二版(" K&R2") 包含 itoa 的以下实现,第 64 页。本书指出了此实现的几个问题,包括 它不能正确处理最大负数

 /* itoa:  convert n to characters in s */
 void itoa(int n, char s[])
 
     int i, sign;

     if ((sign = n) < 0)  /* record sign */
         n = -n;          /* make n positive */
     i = 0;
     do        /* generate digits in reverse order */
         s[i++] = n % 10 + '0';   /* get next digit */
      while ((n /= 10) > 0);     /* delete it */
     if (sign < 0)
         s[i++] = '-';
     s[i] = '\0';
     reverse(s);
  

上面使用的函数reverse在前两页实现:

 #include <string.h>

 /* reverse:  reverse string s in place */
 void reverse(char s[])
 
     int i, j;
     char c;

     for (i = 0, j = strlen(s)-1; i<j; i++, j--) 
         c = s[i];
         s[i] = s[j];
         s[j] = c;
     
  

【讨论】:

【参考方案3】:

如果您经常调用它,“只使用 snprintf”的建议可能会很烦人。所以这就是你可能想要的:

const char *my_itoa_buf(char *buf, size_t len, int num)

  static char loc_buf[sizeof(int) * CHAR_BITS]; /* not thread safe */

  if (!buf)
  
    buf = loc_buf;
    len = sizeof(loc_buf);
  

  if (snprintf(buf, len, "%d", num) == -1)
    return ""; /* or whatever */

  return buf;


const char *my_itoa(int num)
 return my_itoa_buf(NULL, 0, num); 

【讨论】:

这不仅仅是非线程安全的,它根本不是很安全:- void some_func(char* a, char* b); some_func(itoa(123), itoa(456));想知道函数接收到什么吗? 另外,const 限定符对函数返回类型没有任何作用——如果你打开编译器警告你会知道这一点:) @cat 但是这里没有任何 const 限定的返回类型。 const char * 是一个指向 const 的非 const 指针,这很有意义并且是正确的。 @Chortos-2 这很有趣,你当然是完全正确的——我没有意识到 constconst int f (void) ...const int* f (void) ... 之间的语义差异,但是现在用编译器试过了,很有意义。 静态缓冲区的大小完全没有意义,因为放入其中的字符串不是二进制的。在典型的机器上它将是 32 个字符,但是(假设 32 位 int)最长的字符串是 "-2147483648",它只需要 12 个字符。不过,也许这是一个安全、慷慨的上限?【参考方案4】:

编辑:我刚刚发现了std::to_string,它在操作上与我自己的以下函数相同。它是在 C++11 中引入的,并且可以在最新版本的 gcc 中使用,如果您启用 c++0x 扩展,至少早在 4.5 中。


gcc 中不仅缺少itoa,而且它不是最方便使用的函数,因为您需要为其提供缓冲区。我需要一些可以在表达式中使用的东西,所以我想出了这个:
std::string itos(int n)

   const int max_size = std::numeric_limits<int>::digits10 + 1 /*sign*/ + 1 /*0-terminator*/;
   char buffer[max_size] = 0;
   sprintf(buffer, "%d", n);
   return std::string(buffer);

通常使用snprintf 而不是sprintf 会更安全,但缓冲区的大小经过精心调整以防溢出。

查看示例:http://ideone.com/mKmZVE

【讨论】:

问题似乎是关于 C 的,它没有 std:: 等内容。【参考方案5】:

正如 Matt J 所写,有 itoa,但这不是标准的。如果您使用snprintf,您的代码将更加可移植。

【讨论】:

【参考方案6】:

以下函数分配刚好足够的内存来保存给定数字的字符串表示,然后使用标准sprintf 方法将字符串表示写入该区域。

char *itoa(long n)

    int len = n==0 ? 1 : floor(log10l(labs(n)))+1;
    if (n<0) len++; // room for negative sign '-'

    char    *buf = calloc(sizeof(char), len+1); // +1 for null
    snprintf(buf, len+1, "%ld", n);
    return   buf;

不要忘记在不需要时free 向上分配内存:

char *num_str = itoa(123456789L);
// ... 
free(num_str);

注意由于 snprintf 复制 n-1 个字节,我们必须调用 snprintf(buf, len+1, "%ld", n) (不仅仅是 snprintf(buf, len, "%ld", n))

【讨论】:

调用你的函数itoa 不是一个好主意,但要赋予它与itoa 的实际实现不同的行为。这个函数是一个不错的主意,但可以叫它别的名字:) 我还建议使用snprintf 来计算缓冲区长度而不是浮点字符串;浮点可能有极端情况的不准确性。还有don't cast calloc 感谢您的建议。 如果需要一个长整数,应该使用labs。否则它可能会被截断。 snprintf 到一个固定大小的 tmp 缓冲区,如 char buf[64] 以获取长度,然后 malloc 并复制到该缓冲区。与malloc 相比,calloc 没有任何好处,因为您写入了所有字节。一个非常短的字符串的额外复制没有调用浮点 log10 那么糟糕。但是,如果您有一个位扫描函数可以可靠地内联到有效的东西(例如 x86 上的bsr),则使用整数 log2 进行快速近似可能会很有用。 (替代方案:malloc 一个 64 字节的缓冲区,然后在您知道最终长度后 realloc。)【参考方案7】:

阅读那些以此为生的人的代码会让你走得很远。

看看 mysql 的人是如何做到的。来源得到了很好的评价,它会教给你比到处都是破解的解决方案更多的东西。

MySQL's implementation of int2str

我在这里提供了提到的实现;该链接可供参考,应用于阅读完整的实现。

char *
int2str(long int val, char *dst, int radix, 
        int upcase)

  char buffer[65];
  char *p;
  long int new_val;
  char *dig_vec= upcase ? _dig_vec_upper : _dig_vec_lower;
  ulong uval= (ulong) val;

  if (radix < 0)
  
    if (radix < -36 || radix > -2)
      return NullS;
    if (val < 0)
    
      *dst++ = '-';
      /* Avoid integer overflow in (-val) for LLONG_MIN (BUG#31799). */
      uval = (ulong)0 - uval;
    
    radix = -radix;
  
  else if (radix > 36 || radix < 2)
    return NullS;

  /*
    The slightly contorted code which follows is due to the fact that
    few machines directly support unsigned long / and %.  Certainly
    the VAX C compiler generates a subroutine call.  In the interests
    of efficiency (hollow laugh) I let this happen for the first digit
    only; after that "val" will be in range so that signed integer
    division will do.  Sorry 'bout that.  CHECK THE CODE PRODUCED BY
    YOUR C COMPILER.  The first % and / should be unsigned, the second
    % and / signed, but C compilers tend to be extraordinarily
    sensitive to minor details of style.  This works on a VAX, that's
    all I claim for it.
  */
  p = &buffer[sizeof(buffer)-1];
  *p = '\0';
  new_val= uval / (ulong) radix;
  *--p = dig_vec[(uchar) (uval- (ulong) new_val*(ulong) radix)];
  val = new_val;
  while (val != 0)
  
    ldiv_t res;
    res=ldiv(val,radix);
    *--p = dig_vec[res.rem];
    val= res.quot;
  
  while ((*dst++ = *p++) != 0) ;
  return dst-1;

【讨论】:

始终欢迎提供指向潜在解决方案的链接,但请add context around the link,以便您的其他用户知道它是什么以及为什么存在。始终引用重要链接中最相关的部分,以防目标站点无法访问或永久离线。考虑到仅仅是指向外部站点的链接是Why and how are some answers deleted? 的一个可能原因。 那么您在此处发布的 sn-p 有什么好处呢?未来的读者应该注意什么?【参考方案8】:

Linux中的itoa函数在哪里?

Linux 中没有这样的功能。我改用这段代码。

/*
=============
itoa

Convert integer to string

PARAMS:
- value     A 64-bit number to convert
- str       Destination buffer; should be 66 characters long for radix2, 24 - radix8, 22 - radix10, 18 - radix16.
- radix     Radix must be in range -36 .. 36. Negative values used for signed numbers.
=============
*/

char* itoa (unsigned long long  value,  char str[],  int radix)

    char        buf [66];
    char*       dest = buf + sizeof(buf);
    boolean     sign = false;

    if (value == 0) 
        memcpy (str, "0", 2);
        return str;
    

    if (radix < 0) 
        radix = -radix;
        if ( (long long) value < 0) 
            value = -value;
            sign = true;
        
    

    *--dest = '\0';

    switch (radix)
    
    case 16:
        while (value) 
            * --dest = '0' + (value & 0xF);
            if (*dest > '9') *dest += 'A' - '9' - 1;
            value >>= 4;
        
        break;
    case 10:
        while (value) 
            *--dest = '0' + (value % 10);
            value /= 10;
        
        break;

    case 8:
        while (value) 
            *--dest = '0' + (value & 7);
            value >>= 3;
        
        break;

    case 2:
        while (value) 
            *--dest = '0' + (value & 1);
            value >>= 1;
        
        break;

    default:            // The slow version, but universal
        while (value) 
            *--dest = '0' + (value % radix);
            if (*dest > '9') *dest += 'A' - '9' - 1;
            value /= radix;
        
        break;
    

    if (sign) *--dest = '-';

    memcpy (str, dest, buf +sizeof(buf) - dest);
    return str;

【讨论】:

您应该编辑您的答案以解释此代码如何回答问题。 代码错误...负值不起作用 calandoa,你能给出不起作用的精确值(值,基数)吗? @rick-rick-rick(不要忘记@,以便通知用户!):正基数和负值。顺便说一句,不要使用负基数,或者正确使用:en.wikipedia.org/wiki/Negative_base【参考方案9】:

我尝试了自己的 itoa() 实现,它似乎可以在二进制、八进制、十进制和十六进制中工作

#define INT_LEN (10)
#define HEX_LEN (8)
#define BIN_LEN (32)
#define OCT_LEN (11)

static char *  my_itoa ( int value, char * str, int base )

    int i,n =2,tmp;
    char buf[BIN_LEN+1];


    switch(base)
    
        case 16:
            for(i = 0;i<HEX_LEN;++i)
            
                if(value/base>0)
                
                    n++;
                
            
            snprintf(str, n, "%x" ,value);
            break;
        case 10:
            for(i = 0;i<INT_LEN;++i)
            
                if(value/base>0)
                
                    n++;
                
            
            snprintf(str, n, "%d" ,value);
            break;
        case 8:
            for(i = 0;i<OCT_LEN;++i)
            
                if(value/base>0)
                
                    n++;
                
            
            snprintf(str, n, "%o" ,value);
            break;
        case 2:
            for(i = 0,tmp = value;i<BIN_LEN;++i)
            
                if(tmp/base>0)
                
                    n++;
                
                tmp/=base;
            
            for(i = 1 ,tmp = value; i<n;++i)
            
                if(tmp%2 != 0)
                
                    buf[n-i-1] ='1';
                
                else
                
                    buf[n-i-1] ='0';
                
                tmp/=base;
            
            buf[n-1] = '\0';
            strcpy(str,buf);
            break;
        default:
            return NULL;
    
    return str;

【讨论】:

【参考方案10】:

直接复制到缓冲区:64 位整数 itoa 十六进制:

    char* itoah(long num, char* s, int len)
    
            long n, m = 16;
            int i = 16+2;
            int shift = 'a'- ('9'+1);


            if(!s || len < 1)
                    return 0;

            n = num < 0 ? -1 : 1;
            n = n * num;

            len = len > i ? i : len;
            i = len < i ? len : i;

            s[i-1] = 0;
            i--;

            if(!num)
            
                    if(len < 2)
                            return &s[i];

                    s[i-1]='0';
                    return &s[i-1];
            

            while(i && n)
            
                    s[i-1] = n % m + '0';

                    if (s[i-1] > '9')
                            s[i-1] += shift ;

                    n = n/m;
                    i--;
            

            if(num < 0)
            
                    if(i)
                    
                            s[i-1] = '-';
                            i--;
                    
            

            return &s[i];
    

注意:对于 32 位机器,将 long 更改为 long long。对于 32 位整数,long 为 int。 m 是基数。减少基数时,增加字符数(变量 i)。增加基数时,减少字符数(更好)。在无符号数据类型的情况下,i 变为 16 + 1。

【讨论】:

【参考方案11】:

这是 Archana 解决方案的改进版本。它适用于任何基数 1-16 和数字

static char _numberSystem[] = "0123456789ABCDEF";
static char _twosComp[] = "FEDCBA9876543210";

static void safestrrev(char *buffer, const int bufferSize, const int strlen)

    int len = strlen;
    if (len > bufferSize)
    
        len = bufferSize;
    
    for (int index = 0; index < (len / 2); index++)
    
        char ch = buffer[index];
        buffer[index] = buffer[len - index - 1];
        buffer[len - index - 1] = ch;
    


static int negateBuffer(char *buffer, const int bufferSize, const int strlen, const int radix)

    int len = strlen;
    if (len > bufferSize)
    
        len = bufferSize;
    
    if (radix == 10)
    
        if (len < (bufferSize - 1))
        
            buffer[len++] = '-';
            buffer[len] = '\0';
        
    
    else
    
        int twosCompIndex = 0;
        for (int index = 0; index < len; index++)
        
            if ((buffer[index] >= '0') && (buffer[index] <= '9'))
            
                twosCompIndex = buffer[index] - '0';
            
            else if ((buffer[index] >= 'A') && (buffer[index] <= 'F'))
            
                twosCompIndex = buffer[index] - 'A' + 10;
            
            else if ((buffer[index] >= 'a') && (buffer[index] <= 'f'))
            
                twosCompIndex = buffer[index] - 'a' + 10;
            
            twosCompIndex += (16 - radix);
            buffer[index] = _twosComp[twosCompIndex];
        
        if (len < (bufferSize - 1))
        
            buffer[len++] = _numberSystem[radix - 1];
            buffer[len] = 0;
        
    
    return len;


static int twosNegation(const int x, const int radix)

    int n = x;
    if (x < 0)
    
        if (radix == 10)
        
            n = -x;
        
        else
        
            n = ~x;
        
    
    return n;


static char *safeitoa(const int x, char *buffer, const int bufferSize, const int radix)

    int strlen = 0;
    int n = twosNegation(x, radix);
    int nuberSystemIndex = 0;

    if (radix <= 16)
    
        do
        
            if (strlen < (bufferSize - 1))
            
                nuberSystemIndex = (n % radix);
                buffer[strlen++] = _numberSystem[nuberSystemIndex];
                buffer[strlen] = '\0';
                n = n / radix;
            
            else
            
                break;
            
         while (n != 0);
        if (x < 0)
        
            strlen = negateBuffer(buffer, bufferSize, strlen, radix);
        
        safestrrev(buffer, bufferSize, strlen);
        return buffer;
    
    return NULL;

【讨论】:

【参考方案12】:

如果您只想打印它们:

void binary(unsigned int n)

    for(int shift=sizeof(int)*8-1;shift>=0;shift--)
    
       if (n >> shift & 1)
         printf("1");
       else
         printf("0");

    
    printf("\n");
 

【讨论】:

【参考方案13】:

Linux中的itoa函数在哪里?

由于itoa() 在 C 中不是标准的,所以存在具有各种函数签名的各种版本。char *itoa(int value, char *str, int base); 在 *nix 中很常见。

如果 Linux 中缺少它,或者如果代码不想限制可移植性,代码可以使其拥有。

下面是一个没有INT_MIN 问题并处理问题缓冲区的版本:NULL 或缓冲区不足返回NULL

#include <stdlib.h>
#include <limits.h>
#include <string.h>

// Buffer sized for a decimal string of a `signed int`, 28/93 > log10(2)
#define SIGNED_PRINT_SIZE(object)  ((sizeof(object) * CHAR_BIT - 1)* 28 / 93 + 3)

char *itoa_x(int number, char *dest, size_t dest_size) 
  if (dest == NULL) 
    return NULL;
  

  char buf[SIGNED_PRINT_SIZE(number)];
  char *p = &buf[sizeof buf - 1];

  // Work with negative absolute value
  int neg_num = number < 0 ? number : -number;

  // Form string
  *p = '\0';
  do 
    *--p = (char) ('0' - neg_num % 10);
    neg_num /= 10;
   while (neg_num);
  if (number < 0) 
    *--p = '-';
  

  // Copy string
  size_t src_size = (size_t) (&buf[sizeof buf] - p);
  if (src_size > dest_size) 
    // Not enough room
    return NULL;
  
  return memcpy(dest, p, src_size);


以下是处理任何基础 [2...36] 的 C99 或更高版本

char *itoa_x(int number, char *dest, size_t dest_size, int base) 
  if (dest == NULL || base < 2 || base > 36) 
    return NULL;
  

  char buf[sizeof number * CHAR_BIT + 2]; // worst case: itoa(INT_MIN,,,2)
  char *p = &buf[sizeof buf - 1];

  // Work with negative absolute value to avoid UB of `abs(INT_MIN)`
  int neg_num = number < 0 ? number : -number;

  // Form string
  *p = '\0';
  do 
    *--p = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[-(neg_num % base)];
    neg_num /= base;
   while (neg_num);
  if (number < 0) 
    *--p = '-';
  

  // Copy string
  size_t src_size = (size_t) (&buf[sizeof buf] - p);
  if (src_size > dest_size) 
    // Not enough room
    return NULL;
  
  return memcpy(dest, p, src_size);

对于符合 C89 及更高版本的代码,将内部循环替换为

  div_t qr;
  do 
    qr = div(neg_num, base);
    *--p = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[-qr.rem];
    neg_num = qr.quot;
   while (neg_num);

【讨论】:

【参考方案14】:

glibc 内部实现

glibc 2.28 有一个内部实现:

stdio-common/_itoa.c sysdeps/generic/_itoa.h

在内部好几个地方都用到了,但是我找不到它是否可以暴露或如何暴露。

如果你愿意提取它,至少这应该是一个健壮的实现。

这个问题问如何滚动你自己的:How to convert an int to string in C?

【讨论】:

【参考方案15】:

我更喜欢这个:https://github.com/wsq003/itoa_for_linux

它应该是有史以来最快的 itoa()。出于性能原因,我们使用 itoa() 而不是 sprintf(),因此具有有限功能的最快 itoa() 是合理且值得的。

【讨论】:

【参考方案16】:

snprintf 的替换还没有完成!

它仅涵盖碱基:2、8、10、16,而 itoa 适用于 2 到 36 之间的碱基。

由于我正在寻找 base 32 的替代品,我想我必须自己编写代码!

【讨论】:

【参考方案17】:

我在 RedHat 6 和 GCC 编译器上使用了 _itoa(...)。它有效。

【讨论】:

【参考方案18】:

你可以用这个程序代替 sprintf。

void itochar(int x, char *buffer, int radix);

int main()

    char buffer[10];
    itochar(725, buffer, 10);
    printf ("\n %s \n", buffer);
    return 0;


void itochar(int x, char *buffer, int radix)

    int i = 0 , n,s;
    n = s;
    while (n > 0)
    
        s = n%radix;
        n = n/radix;
        buffer[i++] = '0' + s;
    
    buffer[i] = '\0';
    strrev(buffer);

【讨论】:

这段代码有很多错误:1) 实际上没有正确转换十六进制。 2) 根本不转换 0。 3) 不适用于负数。 4) 不检查缓冲区溢出。我将很快发布此代码的改进版本。

以上是关于Linux中的itoa函数在哪里?的主要内容,如果未能解决你的问题,请参考以下文章

struct“proto_ops”中的函数指针指向哪里?

在哪里可以找到linux文件中的进程表?

Linux的api在哪里能查?

qmake - 查询 Linux 中的内部设置 - 它们在哪里?

Linux 中的 PATH_MAX 定义在哪里?

Linux 中的 web_sockets 等价物在哪里?