为什么 itoa 期望有符号字符而不是无符号字符?

Why does itoa expect a signed character instead of an unsigned?

在使用 PIC24FJ128GB204 的 MPLAB X 中工作时学习嵌入式 C。

到目前为止,我大多听说在嵌入式设备上应尽可能(尤其是?)使用无符号类型,所以我开始使用 uint8_t 数组来保存字符串。但是,如果我从 stdlib.h 调用 itoa,它需要一个指向有符号字符 (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 的实现,这似乎是常见的情况。

这是为什么?

(我还注意到大多数实现都期望 value/pointer/radix 而-出于某种原因- Microchip 的 stdlib.h 首先期望指针。我花了一段时间才意识到这一点。)

So far, I've mostly heard that you should use unsigned types as much as possible

首先-这根本不是事实-您应该使用正确的类型What is the correct type? 最符合您需求的类型。 How can I know which type is best for me? 这取决于您使用它的目的。它应该有一个类型来存储您的程序可能想要存储在其中的所有可能值。

所以你不应该再听这个人的了。

char as signed or unsigned 是几十年前的妥协-那时带来一定程度的意义与当时的编译器保持一致。

itoa() 虽然不是标准的 C 库函数,但遵循该约定,因为 字符串 char 组成。

许多库函数使用 string 指针。 itoa() 也这样做,并像 unsigned char 一样处理内部工作。请记住,string 是代表 text,而不是数字 - 所以 char 本身的符号性不是很好忧虑。当然itoa()的重点是取一个数(int)组成一个字符串.

C 库在功能上 char "as if" 在许多情况下 unsigned char

  • int fgetc() returns 值 EOF 或在 unsigned char 范围内。

  • printf() "%c": "int 参数转换为 unsigned char,并且 结果字符被写入。

  • <string.h> "For all functions in this subclause, each character shall be interpreted as if it had the type unsigned char (and therefore every possible object representation is valid and has a different value)."

  • <ctype.h> “在所有情况下,参数都是 int,其值应表示为 unsigned char 或等于宏 EOF.

So far, I've mostly heard that you should use unsigned types as much as possible (especially?) on embedded devices,

您听取此消息的人是否解释过原因?这种解释是基于可靠的分析和工程,还是凭空而来?

经验法则的问题在于,它们经常会在错误的情况下不假思索地应用。当你需要使用无符号类型时使用无符号类型,当你需要使用有符号类型时使用有符号类型。

I've started to use uint8_t arrays to hold strings.

不要。这不是它的目的。

Plain char 可能已签名或未签名,具体取决于环境。 基本字符集(大写和小写拉丁字母、十进制数字和基本图形字符集)的字符编码始终为非负数,但扩展字符可能有正或负编码。

6.2.5 Types
...
3 An object declared as type <strong>char</strong> is large enough to store any member of the basic execution character set. If a member of the basic execution character set is stored in a <strong>char</strong> object, its value is guaranteed to be nonnegative. If any other character is stored in a char object, the resulting value is implementation-defined but shall be within the range of values that can be represented in that type.

C 2011 Online Draft

处理字符串的 C 库函数需要指向 char 的指针,而不是 unsigned charuint8_t 或其他任何指针。虽然对于提供它的任何平台来说,uint8_t 很可能只是 unsigned char 的 typedef 名称,但这并不能保证。 char 必须 至少 8 位宽,但有些平台可以更宽(旧 PDP 之一使用 9 位字节和 36 位字,并且根据应用程序,我可以看到一些特殊用途的嵌入式系统使用不稳定的尺寸)。

So far, I've mostly heard that you should use unsigned types as much as possible (especially?) on embedded devices

之所以如此,主要是因为(无意或有意地)将带符号的操作数与按位运算符混合在一起会造成严重破坏。但在低级编程中,您实际上需要使用签名类型的情况并不多。

例如,MISRA-C 强制您始终使用无符号变量、操作数和整数常量,除非意图实际使用有符号类型。所以这不仅仅是基于意见的东西,MISRA-C 是大多数专业嵌入式系统的事实上的行业标准。

so I've started to use uint8_t arrays to hold strings

没关系,但为此目的使用 char 也没有错。 只有 可以使用char 的时间是您打算存储文本的时间。请注意 char 特别讨厌,因为与该语言中的所有其他类型不同,它具有未知的符号。每个编译器都可以使 char 有符号或无符号并且仍然符合 C 标准。因此依赖于 char 被签名或未签名的代码被破坏了。但是,对于文本字符串,这无关紧要,因为它们始终为正数。

However, if I call itoa from stdlib.h, it expects a pointer to a signed char (int8_t) array:

您的编译器显然将 char 视为已签名。首先请注意 itoa 不是标准 C,并且在需要严格的 C 标准一致性时不允许存在于 stdlib.h 中。但更重要的是,不同的编译器可能会以不同的方式实现该功能,因为它没有标准化。

事实证明,您可以安全地在各种字符类型之间任意转换:charunsigned charsigned charint8_tuint8_t (stdint.h 8 位类型几乎肯定是字符类型,即使标准没有明确说明)。字符类型特别有各种与之关联的特殊规则,这意味着您始终可以将某些内容转换为字符类型。

只要不存在限定符(const 等),您就可以安全地将 uint8_t 数组转换为 char*