“可表示”在C11中意味着什么?

时间:2014-09-10 23:43:08

标签: c language-lawyer c11

根据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()charint)?换句话说,转换为char 可表示intunsigned char吗?


以下是wiktionary defines "representable"

的方式
  

能够被代表。

char是否能够表示为unsigned char即可。 §6.2.6.1/ 4:

  

存储在任何其他对象类型的非位字段对象中的值   由 n × CHAR_BIT 位组成,其中 n 是该对象的大小   type,以字节为单位。该值可以复制到类型的对象中    unsigned char [ n ](例如, memcpy );得到的字节集是   称为值的对象表示

sizeof(char) == 1因此其对象表示为unsigned char[1],即char能够表示为unsigned char。我哪里错了?

具体示例,我可以将[-2, -1, 0, 1]表示为[0, 1, 2, 3]。如果我不能为什么呢?


相关:根据§6.3.1.3isspace((unsigned char)c) INT_MAX >= UCHAR_MAX是可移植的,否则它是实现定义的。

3 个答案:

答案 0 :(得分:10)

  

类型中可表示什么意思?

重新表述,类型是底层位模式的含义。因此,值可以在类型中表示,如果该类型指定了某种意义上的位模式。

转换(可能需要转换)是从值(用特定类型表示)到目标类型中表示的值(可能不同)的映射。


根据给定的假设(char已签名),CHAR_MIN肯定是否定的,您引用的文字不会留下解释的余地​​:
是的,它是未定义的行为,因为unsigned char不能代表任何负数。

如果该假设不成立,您的程序将被明确定义,因为CHAR_MIN将是0unsigned char的有效值。

因此,我们有一个案例,它是实现定义的程序是未定义的还是定义良好的。


另外,我们无法保证sizeof(int)>1INT_MAX >= CHAR_MAX,因此int可能无法代表unsigned char可能的所有值。

由于转化被定义为保留价值,因此签名的char始终可以转换为int。 但如果它是否定的,那就不会改变将负值表示为unsigned char的不可能性。 (定义转换,因为始终定义从任何整数类型到任何unsigned整数类型的转换,但缩小转换需要转换。)

答案 1 :(得分:4)

假设 char 签名,那么这将是undefined behavior,否则它定义得很好,因为CHAR_MIN将具有值{ {1}}。更容易看出:

的意图和含义
  

其值应表示为unsigned char或shall   等于宏EOF的值

如果我们从Rationale for International Standard—Programming Languages—C中读到0 字符处理&lt; ctype.h&gt; ,那么(强调我的前进):

  

由于这些功能通常主要用作宏,他们的域   仅限于可表示的小正整数   unsigned char,加上EOF的值。 EOF传统上是-1,但可能   是任何负整数,因此可以区别于任何有效   字符代码。因此,可以有效地实现这些宏   使用参数作为一小部分属性的索引。

所以有效值是:

  1. 可以放入unsigned char
  2. 的正整数
  3. 7.4这是一些实施定义的负数
  4. 即使这是C99的基本原理,因为您所指的特定措辞不会从C99更改为C11,因此基本原理仍适用。

    我们还可以找到为什么接口使用 int 作为参数而不是 char ,来自EOF 部分库函数的使用,它说:

      

    根据“加宽”类型,所有库原型都指定为   以前声明为char的参数现在写为int。这个   确保可以使用或不使用a调用大多数库函数   范围内的原型,从而保持向后兼容性   前C89代码。但是请注意,因为像printf和   scanf使用可变长度的参数列表,必须在中调用它们   原型的范围。

答案 2 :(得分:1)

揭示性引用(对我来说)是§6.3.1.3/ 1:

  

如果值可以用新类型表示,则不变。

即,如果必须更改该值,则该值不能用新类型表示。

因此,unsigned类型不能代表负值。

回答标题中的问题:“可表示”是指“可以用§6.3.1.3表示”,与§6.2.6.1中的“对象表示”无关。

回想起来似乎微不足道。我可能会对将b'\xFF'0xff255-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)