Преобразование целого числа в массив символов в C

Я разрабатываю встроенное приложение ARM. Я застрял в глупой проблеме - у меня есть массив беззнаковых 8-битных целых чисел:

uint8_t days[42] = { 0 };

Он инициализируется некоторыми данными — алгоритм инициализации вводит множество переменных, запутывающих и не имеющих отношения к задаче, поэтому я не буду его здесь публиковать. Я вижу этот массив в списке переменных отладчика и уверен, что он заполнен целочисленными значениями от 0 до 31.

Я хотел бы взять любой элемент этого массива, скажем, 15-й, и преобразовать его в char*, чтобы его можно было отобразить на моем ЖК-экране. Я переписываю его с помощью функции sprintf:

char d[3] = { '0', '0', '0' };
sprintf(d, "%d", days[15]);

Только одно замечание: нет, я не могу использовать функцию stdlib itoa(), потому что она не соответствует стандартам MISRA-C, которым я обязан следовать.

В результате я получаю только двоичное нулевое значение в моем буфере d. Любые идеи?


person Michał Szydłowski    schedule 16.03.2015    source источник
comment
Возможно, вы инициализируете d неправильно. Если вы хотите, чтобы все значения заканчивались нулем, вам придется использовать \0 вместо 0. Это работает нормально для меня, кстати.   -  person ForceBru    schedule 16.03.2015
comment
Как правило, никогда не используйте функцию sprintf. Используйте snprintf. Вы можете обнаружить, что ваши проблемы были вызваны переполнением буфера. Вам не нужно инициализировать d.   -  person Steen    schedule 16.03.2015
comment
Почему переполняется буфер? Мне никогда не нужно более 3 символов, включая завершающий символ.   -  person Michał Szydłowski    schedule 16.03.2015
comment
Это может быть проблема целочисленных правил продвижения/размера. Может быть, попробовать явно привести days[15] к int?   -  person Drew McGowen    schedule 16.03.2015
comment
Я не могу использовать int, он слишком большой и бессмысленный — зачем мне 32-битное целое число для чисел 0-31?   -  person Michał Szydłowski    schedule 16.03.2015
comment
@MichałSzydłowski, потому что формат "%d" предполагает передачу int в printf. Я не говорю хранить массив как ints, я говорю сделать sprintf(d, "%d", (int)days[15]);, т.е. добавить приведение (int) перед days[15].   -  person Drew McGowen    schedule 16.03.2015
comment
Все равно не помогает   -  person Michał Szydłowski    schedule 16.03.2015
comment
@MichałSzydłowski printf всегда продвигает меньшие аргументы до int.   -  person zoska    schedule 16.03.2015
comment
Символ может содержать значения от -128 до 127. -128 требует 5 символов, включая завершающий символ для отображения.   -  person Steen    schedule 16.03.2015
comment
Он использует MISRA-C, поэтому забудьте о stdio.h. Он также программирует встроенную систему микроконтроллеров, так что забудьте о stdio.h еще больше.   -  person Lundin    schedule 16.03.2015
comment
@MattMcNabb - согласен. Комментарий был запутанным, и я удалил его. Спасибо, что указали на ошибки.   -  person ryyker    schedule 17.03.2015


Ответы (2)


Для соответствия MISRA-C вы, конечно же, не можете использовать sprintf() или что-либо еще из stdio.h. Как правило, вы хотите избежать sprintf как чумы на любой встроенной системе.

Написание простой процедуры преобразования десятичного целого числа в строку - довольно простой материал... вот моя попытка версии, совместимой с MISRA-C (2004 и 2012):

#include <stdint.h>

void dec_to_str (char* str, uint32_t val, size_t digits);

int main (void)
{
  char str[3u + 1u]; // assuming you want null terminated strings?

  dec_to_str(str, 31u, 3u);

  return 0;
}


void dec_to_str (char* str, uint32_t val, size_t digits)
{
  size_t i=1u;

  for(; i<=digits; i++)
  {
    str[digits-i] = (char)((val % 10u) + '0');
    val/=10u;
  }

  str[i-1u] = '\0'; // assuming you want null terminated strings?
}

Примечание. Переменная uint32_t может быть заменена на uint8_t, но тогда вам нужно повсюду добавлять приведения типов, чтобы предотвратить неявное продвижение типов, как того требует MISRA. Тогда код станет действительно уродливым, например:

 str[digits-i] = (char)(uint8_t)((uint8_t)(val % 10u) + '0');

Единственная разумная вещь, которую можно сделать, это разбить этот беспорядок на несколько строк:

uint8_t ch = (uint8_t)(val % 10u);
ch = (uint8_t)(ch + '0');
str[digits-i] = (char)ch;
person Lundin    schedule 16.03.2015
comment
Честно говоря, я пропустил эту часть MISRA. Да, мне нужны строки с завершающим нулем! Это отлично работает, спасибо! - person Michał Szydłowski; 16.03.2015
comment
IMO snprintf — отличная функция для использования во встроенных системах. (хотя я не могу комментировать конкретно MISRA) - person M.M; 17.03.2015
comment
@MattMcNabb Зависит от того, для чего вы его используете. Если вам нужно только простое целочисленное преобразование в строку, как указано выше, мы говорим о: сотнях байтов в бесполезном использовании стека, нескольких килобайтах потраченной впустую программной памяти, медленном выполнении, опасной функции без безопасности типов, которая не повторяется. абитуриент. Для выполнения одной и той же работы. Как именно это отличная функция? - person Lundin; 17.03.2015

person    schedule
comment
sprintf(d, "%s", d); это не имеет смысла - по сути, вы просто копируете строку в себя (которая может быть UB). - person Drew McGowen; 16.03.2015
comment
плюс, это дает нежелательные результаты - person Michał Szydłowski; 16.03.2015
comment
@Drew: Нет. Вы конвертируете целое число в массиве days и конвертируете его в символ в массиве d. - person Bruce; 16.03.2015
comment
@ Брюс, это то, что делают первые две строки. Строка, которая якобы отображает строку, не делает ничего полезного. - person Drew McGowen; 16.03.2015
comment
Если бы вы изменили свою третью строку на printf("%s", d);, то я бы купил ее. В настоящее время строка 3 является просто другим способом выполнения строк 1 и 2. sprintf ничего не отображает, она используется для упаковки буфера с форматированием. - person ryyker; 16.03.2015
comment
Вы отредактировали свой код, но он все еще неверен. Избавьтесь от строки sprintf(d, %s, d);. Это избыточно. Кроме того, собираетесь ли вы использовать один и тот же элемент days[] для каждой конверсии? - person ryyker; 16.03.2015
comment
@ryyker: это, по-видимому, встроенный проект, основанный на желании использовать как можно меньше памяти. У спрашивающего есть ЖК-экран, а это означает, что ему нужно передать буфер d драйверу ЖК-дисплея. printf предназначен только для моих целей отладки. - person Bruce; 16.03.2015
comment
@ryyker: Это всего лишь пример, который спрашивающий может использовать по своему усмотрению. - person Bruce; 16.03.2015
comment
Тем не менее, вы не объяснили, почему вы продолжаете присваивать значения `d[0], d[1] в первых двух строках, а затем присваиваете d d через sprintf(). Вам не кажется это излишним (в лучшем случае)? Строка sprintf(d, %s, d); не нужна. - person ryyker; 16.03.2015
comment
Помимо упомянутых проблем, этот код имеет как минимум 10 нарушений MISRA-C. - person Lundin; 16.03.2015
comment
@ryyker это не просто избыточно, это вызывает неопределенное поведение (перекрытие ввода и вывода) - person M.M; 17.03.2015
comment
@MattMcNabb - согласен. Спасибо. Оп исправил эту проблему. Другие остаются. - person ryyker; 17.03.2015