根据C11 WG14 draft version N1570:
标题
<ctype.h>
声明了几个对字符分类和映射有用的函数。在所有情况下,论证都是int
,其值应表示为unsigned char
或等于宏EOF
的值。如果参数具有任何其他值,则行为未定义。
是不确定的行为?:
#include <ctype.h>
#include <limits.h>
#include <stdlib.h>
int main(void) {
char c = CHAR_MIN; /* let assume that char is signed and CHAR_MIN < 0 */
return isspace(c) ? EXIT_FAILURE : EXIT_SUCCESS;
}
该标准是否允许将char
传递给isspace()
(char
到int
)?换句话说,char
转换为int
后可以表示为unsigned char
吗?
这是wiktionary defines "representable"的方式:
能够代表。
char
能否代表unsigned char
?是。 §6.2.6.1/ 4:
存储在任何其他对象类型的非位字段对象中的值由n个
×
CHAR_BIT
位组成,其中n是该类型对象的大小(以字节为单位)。该值可以复制到unsigned char [n]类型的对象中(例如,通过memcpy);生成的字节集称为值的对象表示。
sizeof(char) == 1
因此它的对象表示是unsigned char[1]
,即char
能够表示为unsigned char
。我哪里错了?
具体的例子,我可以将[-2, -1, 0, 1]
表示为[0, 1, 2, 3]
。如果我不能那么?
相关:根据§6.3.1.3,isspace((unsigned char)c)
是可移植的,如果INT_MAX >= UCHAR_MAX
,否则它是实现定义的。
在char被签名的假设下,这将是undefined behavior,否则它是明确定义的,因为CHAR_MIN
将具有值0
。更容易看出以下内容的意图和含义:
其值应表示为无符号字符或等于宏EOF的值
如果我们从7.4
读到部分Rationale for International Standard—Programming Languages—C字符处理<ctype.h>(强调我的前进):
由于这些函数通常主要用作宏,因此它们的域仅限于无符号字符中可表示的小正整数加上EOF的值。 EOF传统上是-1,但可以是任何负整数,因此可以与任何有效的字符代码区分开。因此,通过将参数用作小数组属性的索引,可以有效地实现这些宏。
所以有效值是:
EOF
是一些实现定义的负数尽管这是C99的基本原理,因为您所指的特定措辞不会从C99变为C11,因此基本原理仍然适用。
我们还可以找到为什么接口使用int作为参数而不是char,来自7.1.4
部分使用库函数,它说:
所有库原型都是根据“加宽”类型指定的,以前声明为char的参数现在写为int。这确保了可以在范围内使用或不使用原型来调用大多数库函数,从而保持与C89之前代码的向后兼容性。但请注意,由于printf和scanf等函数使用可变长度参数列表,因此必须在原型范围内调用它们。
一个类型中可表示什么意思?
重新制定,类型是基础位模式意味着什么的约定。因此,值可以在类型中表示,如果该类型指定了某种意义的位模式。
转换(可能需要转换)是从值(用特定类型表示)到目标类型中表示的值(可能不同)的映射。
在给定的假设下(char
签名),CHAR_MIN
肯定是否定的,你引用的文字没有留下解释的余地:
是的,它是未定义的行为,因为unsigned char
不能代表任何负数。
如果这个假设不成立,那么你的程序将是明确定义的,因为CHAR_MIN
将是0
,unsigned char
的有效值。
因此,我们有一个案例,它是实现定义的程序是未定义的还是定义良好的。
顺便说一句,不能保证sizeof(int)>1
或INT_MAX >= CHAR_MAX
,所以int
可能无法代表unsigned char
可能的所有值。
由于转换定义为保值,因此签名的char
始终可以转换为int
。
但如果它是否定的,那就不会改变将负值表示为unsigned char
的不可能性。 (定义转换,因为始终定义从任何整数类型到任何unsigned
积分类型的转换,但缩小转换需要转换。)
(对我而言)揭示的引用是§6.3.1.3/ 1:
如果值可以由新类型表示,则不会更改。
即,如果必须更改该值,则该值不能由新类型表示。
因此,unsigned
类型不能代表负值。
要回答标题中的问题:“可表示”是指“可以用§6.3.1.3表示”,与§6.2.6.1中的“对象表示”无关。
回想起来似乎微不足道。我可能会对将b'\xFF'
,0xff
,255
,-1
视为Python中相同字节的习惯感到困惑:
>>> (255).to_bytes(1, 'big')
b'\xff'
>>> int.from_bytes(b'\xFF', 'big')
255
>>> 255 == 0xff
True
>>> (-1).to_bytes(1, 'big', signed=True)
b'\xff'
并且不相信将字符传递给字符分类函数(例如isspace(CHAR_MIN)
)是一种未定义的行为。