Почему Итоа ожидает подписанный символ вместо неподписанного? - PullRequest
0 голосов
/ 18 февраля 2020

Изучение встроенного C при работе в MPLAB X с PIC24FJ128GB204.

До сих пор я в основном слышал, что вы должны как можно больше использовать типы без знака (особенно?) На встроенных устройствах, поэтому Я начал использовать массивы uint8_t для хранения строк. Однако, если я вызываю itoa из stdlib.h, он ожидает указатель на массив со знаком (int8_t) со знаком:

extern char * itoa(char * buf, int val, int base);

Это становится особенно ясно, когда я пытаюсь скомпилировать после использование itoa для неподписанного массива:

main.c:317:9: warning: pointer targets in passing argument 1 of 'itoa' differ in signedness
c:\program files (x86)\microchip\xc16\v1.36\bin\bin\../..\include/stdlib.h:131:15: note: expected 'char *' but argument is of type 'unsigned char *'

Поиск реализаций itoa на других платформах, это, кажется, распространенный случай.

Почему это так?

( Я также заметил, что большинство реализаций ожидают значение / указатель / основание, тогда как - по какой-то причине - stdlib.h от Microchip ожидает указатель первым. Мне потребовалось время, чтобы понять это.)

Ответы [ 4 ]

6 голосов
/ 18 февраля 2020

char как со знаком или без знака - это компромисс десятилетий go - Тогда имело смысл довести уровень согласованности до компиляторов дня.

itoa(), хотя и не является стандартной библиотечной функцией C, следует этому соглашению, поскольку строка состоит из char.

Многие библиотечные функции используют указатель string . itoa() делает то же самое и обрабатывает внутреннюю работу как unsigned char. Имейте в виду, что строка должна представлять текст , а не цифры - так что подпись char сама по себе не является большой проблемой. Конечно, смысл itoa() состоит в том, чтобы взять число (int) и сформировать строку .

Библиотека C обрабатывает char функционально «как если бы» во многих случаях было unsigned char.

  • int fgetc() возвращает значение EOF или в диапазоне unsigned char.

  • printf() "%c": «Аргумент int преобразуется в unsigned char, а полученный символ записывается.»

  • <string.h> «Для всех функций в этом подпункте каждый символ должен интерпретироваться так, как если бы он имел тип unsigned char (и, следовательно, каждое возможное представление объекта является допустимым и имеет другое значение).»

  • <ctype.h> "Во всех случаях аргументом является int, значение которого должно быть представлено как unsigned char или равным значению макроса EOF.

4 голосов
/ 18 февраля 2020

До сих пор я в основном слышал, что вы должны как можно больше использовать типы без знака (особенно?) На встроенных устройствах,

Объяснили ли люди, от которых вы слышали это почему ? Это объяснение основано на solid анализе и разработке, или оно взято из воздуха?

Проблема с эмпирическими правилами заключается в том, что они часто применяются бездумно в неправильной ситуации. Используйте неподписанные типы, когда вам нужно для использования неподписанных типов, используйте подписанные типы, когда вам нужен для использования подписанных типов.

Я начал использовать массивы uint8_t для хранения строк.

Не надо. Это не то, для чего это там.

Обычная char может быть подписана или не подписана, в зависимости от среды. Кодировки символов для набора символов basi c (латинские буквы верхнего и нижнего регистра, десятичные цифры и набор графических символов basi c) всегда будут неотрицательными, но расширенные символы могут иметь положительную или отрицательную кодировку.

6.2.5 Типы
...
3 Объект, объявленный как тип <strong>char</strong>, достаточно велик для хранения любого члена базового набора символов выполнения. Если элемент набора базовых c исполняемых символов хранится в объекте <strong>char</strong>, его значение гарантированно будет неотрицательным. Если какой-либо другой символ хранится в объекте char, результирующее значение определяется реализацией, но должно находиться в диапазоне значений, которые могут быть представлены в этом типе.

C 2011 Online Draft

Библиотечные функции C, обрабатывающие строки, ожидают указатели на char, а не unsigned char или uint8_t или что-нибудь еще. Хотя весьма вероятно, что для любой платформы, которая предлагает uint8_t, это просто имя typedef для unsigned char, это не гарантия. char должен быть по крайней мере 8 бит в ширину, но есть платформы, где он может быть шире (один из старых PDP использовал 9-битные байты и 36-битные слова, и в зависимости от приложения я могу увидеть некоторые встроенные системы специального назначения, использующие шаткие размеры).

2 голосов
/ 18 февраля 2020

До сих пор я в основном слышал, что вы должны как можно больше использовать типы без знака (особенно?) На встроенных устройствах

Это верно главным образом по той причине, что (случайно или намеренно) операнды со знаком, смешанные с побитовыми операторами, создают havo c. Но в низкоуровневом программировании не так много случаев, когда вам действительно нужно использовать подписанные типы.

Например, MISRA- C заставляет вас всегда использовать беззнаковые переменные, операнды и целочисленную константу если только не является намерением использовать подписанный тип. Так что это не просто что-то основанное на мнениях, MISRA- C является де-факто отраслевым стандартом для большинства профессиональных встраиваемых систем.

, поэтому я начал использовать массивы uint8_t для хранения строк

Это нормально, но не неправильно использовать char для этой цели. только время, когда можно использовать char, - это когда вы собираетесь хранить текст. Обратите внимание, что char особенно неприятен, потому что в отличие от всех других типов в языке, он имеет неизвестную подпись. Каждый компилятор может сделать char подписанным или неподписанным и при этом соответствовать стандарту C. Поэтому код, полагающийся на то, что char подписан или не подписан, не работает. Однако для текстовых строк это не имеет значения, поскольку они всегда положительны.

Однако, если я вызываю itoa из stdlib.h, он ожидает указатель на массив char (int8_t) со знаком:

Ваш компилятор обрабатывает char как подписанное тогда. Прежде всего, обратите внимание, что itoa не является стандартным C и не может существовать внутри stdlib.h, когда требуется строгое соответствие C стандарту. Но что еще более важно, разные компиляторы могут реализовывать функцию по-разному, поскольку она не стандартизирована.

Как оказалось, вы можете безопасно выполнять дикое приведение между различными типами символов: char, unsigned char, signed char, int8_t и uint8_t (8-битные типы stdint.h почти наверняка являются символьными типами, даже если стандарт не говорит об этом явно). С типами символов, в частности, связаны различные специальные правила, означающие, что вы всегда можете привести что-либо к типу символа.

Вы можете безопасно преобразовать свой массив uint8_t в char*, если есть квалификаторы отсутствуют (const et c).

1 голос
/ 18 февраля 2020

До сих пор я в основном слышал, что вы должны использовать как можно больше беззнаковых типов

Во-первых - это вообще не правда - вы должны использовать правильный тип . What is the correct type? Это тип, который наилучшим образом соответствует вашим потребностям. How can I know which type is best for me? Это зависит от того, для чего вы его используете. Он должен иметь тип для хранения всех возможных значений, которые ваша программа может захотеть сохранить в нем.

Так что вам больше не следует слушать этого человека.

...