【问题标题】:Understanding how sprintf converts integers to strings了解 sprintf 如何将整数转换为字符串
【发布时间】:2019-03-20 16:16:41
【问题描述】:

我正在学习 C 和一个将整数转换为字符串的基本示例(您在其他语言中认为这是理所当然的)。我找到的答案是这样的:

sprintf(str, "%d", num);

但我想看看这是如何实现的。所以我查了一下sprintf,它把我带到了这里:

#define vsprintf(s, f, a) _IO_vsprintf (s, f, a)

int
__sprintf (char *s, const char *format, ...)
{
  va_list arg;
  int done;

  va_start (arg, format);
  done = vsprintf (s, format, arg);
  va_end (arg);

  return done;
}

然后我查找 _IO_vsprintf 并显示:

int
__IO_vsprintf (char *string, const char *format, va_list args)
{
  _IO_strfile sf;
  int ret;

#ifdef _IO_MTSAFE_IO
  sf._sbf._f._lock = NULL;
#endif
  _IO_no_init (&sf._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
  _IO_JUMPS (&sf._sbf) = &_IO_str_jumps;
  _IO_str_init_static_internal (&sf, string, -1, string);
  ret = _IO_vfprintf (&sf._sbf._f, format, args);
  _IO_putc_unlocked ('\0', &sf._sbf._f);
  return ret;
}

这似乎围绕_IO_vfprintf 展开。但我在code 中找不到那个位置。谷歌和 GitHub 没有带来任何结果。想知道是否有人提供了 sprintf 的完整实现,如果没有,可以解释它是如何工作的。

我想知道如何像 sprintf 那样实现 itoa,这听起来是一个很好的实现。

如果没有实现,我对这段代码如何实际运行感到困惑。

【问题讨论】:

  • 寻找vfprintf
  • 那么vfprintf字符串对应的int是什么。
  • vfprintf 导致__nldbl_vfprintf 也导致空白。其实可能this.
  • 无论如何,如果你想要一个itoa 功能,xprintf 是一个严重的矫枉过正。有一个strtol 家族的表现比这些家族中的任何一个都好。
  • 取决于“缓冲区溢出问题”的含义。从某种意义上说,C 总体上存在“缓冲区溢出问题”。

标签: c printf


【解决方案1】:

Looks like _IO_vfprintf_IO_vfprintf_internal 的隐藏别名。

当为普通(非宽)字符编译文件vfprintf.c 文件时,fprintf 变为定义为_IO_vfprintf_internal 的宏,从here 和在别名here 之前未定义。

所以_IO_vfprintf_internal 声明开始于here,其中vfprintf 被声明(嗯,“在未经处理的源文件中”,标识符vfprintf 从未声明过)。

vfprintf 函数停止处理所有宽度、空格、减号和其他格式说明符后,它最终会跳过jump tables(这基本上是一种通过跳转跳转到不同goto 标签的聪明方法通过 GCC 扩展获得地址的表labels as values) 它将落在form_integer: 标签上。从那里我们jumpnumber:。从那里我们打电话给_itoa_word

现在_itoa_word 简单易行,甚至可以在这里发布预处理函数:

char *
_itoa_word (_ITOA_WORD_TYPE value, char *buflim,
        unsigned int base, int upper_case) {
        const char *digits = (upper_case
               ? _itoa_upper_digits
               : _itoa_lower_digits);

      ...
      switch (base)
      {
      ...
      // SPECIAL (10); expands to:
      case 10:
          do
              *--buflim = digits[value % Base];
          while ((value /= Base) != 0);
          break;
      ...
      }
      ...
}

digits,如_itoa_upper_digits_itoa_lower_digits 中的_itoa_lower_digits 是一个简单的查找表,一个从字符串字面量初始化的char 数组,用于将小数转换为相关的ascii 表示形式。

_itoa_word 之后,有一些代码用于处理左对齐字符串、打印前导符号字符并用空格或零填充字符串,但最后我们的字符串得到outputted(或左对齐outputted)。

整数转字符串示例

所以 99% 的工作都是为了处理所有奇怪的事情

printf("%+- 3lld %+- 4.3Lf %-+04hhd %+- 5zd", 1llu, 2.L, 3, (ssize_t)4);

它需要遵守的格式说明符。转换只是使用查找字符串(处理非 ASCII 系统)的简单转换,它通过数字向后工作:

#include <stdio.h>

char *my_itoa(char *dest, size_t destlen, int number)
{
    // we will work our way from the ending
    char *p = &dest[destlen];
    // null terminate the string
    *--p = '\0';

    while (number) {
        if (p == dest) {
            // destination buffer too small!!
            return NULL;
        }
        *--p = "0123456789"[number % 10];
        number /= 10;
    }

    return p;
}

int main()
{
    char buf[20];
    printf("%s\n", my_itoa(buf, sizeof(buf), 123));
}

【讨论】:

  • 希望我能给更多+1,这太棒了!谢谢。
猜你喜欢
  • 2019-04-03
  • 1970-01-01
  • 2010-10-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多