Понимание того, как sprintf преобразует целые числа в строки - PullRequest
1 голос
/ 20 марта 2019

Я нахожусь в процессе изучения 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.Но я не могу найти, где это находится в коде .Google и GitHub не дают результатов.Интересно, есть ли у кого-нибудь полная реализация sprintf или нет, я могу объяснить, как он работает.

Я хотел бы знать, как реализовать itoa, как это делает sprintf, что звучит так, как будто это хорошая реализация.

Я не совсем понимаю, как этот код может работать, если нет реализации.

1 Ответ

2 голосов
/ 20 марта 2019

выглядит как _IO_vfprintf - скрытый псевдоним _IO_vfprintf_internal.

При компиляции файла vfprintf.c file для обычных (не широких) символов fprintf становится макросом, определенным как _IO_vfprintf_internal, с здесь и неопределенным до наложения псевдонима здесь .

Итак, _IO_vfprintf_internal объявление начинается здесь , где vfprintf объявлено (ну, «в необработанном исходном файле», идентификатор vfprintf get никогда не объявляется).

После того, как функции vfprintf прекратили обработку всех значений ширины, пробелов, минусов и других спецификаторов формата, он, наконец, будет перебирать таблиц перехода (что по сути является умным способом перехода к различным goto метки в таблице переходов с адресами, полученными с помощью расширений GCC метки в качестве значений ), они будут помещены на метку form_integer:. Оттуда мы прыгаем до number:. Оттуда мы называем _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 - это простая таблица поиска, массив символов, инициализированный из строкового литерала, используемый для преобразования десятичных дробей в соответствующие им представление ascii.

После _itoa_word есть некоторый код для обработки выравнивания по левому краю строки, печати символа начального знака и заполнения строки пробелами или нулями, но в итоге наша строка получает выведено (или выравнивание по левому краю выводимый ). * * тысяча пятьдесят девять

пример преобразования целых чисел в строку

Так что 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));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...