将sizeof()的结果分配给ssize_t [英] Assign result of sizeof() to ssize_t
问题描述
我碰巧需要将sizeof(x)
的结果与ssize_t
进行比较.
当然,GCC给出了一个错误(幸运的是,我(我使用了-Wall -Wextra -Werror
)),所以我决定做一个宏来拥有sizeof()
的签名版本.
#define ssizeof (ssize_t)sizeof
然后我可以像这样使用它:
for (ssize_t i = 0; i < ssizeof(x); i++)
问题是,我可以保证SSIZE_MAX >= SIZE_MAX
吗?我以为可悲的是,这永远不会成为现实.
或者至少是sizeof(ssize_t) == sizeof(size_t)
,它将减少一半的值,但仍然足够接近.
我在POSIX文档中没有找到ssize_t
和size_t
之间的任何关系.
相关问题:
不能保证SSIZE_MAX >= SIZE_MAX
.实际上,情况不太可能,因为size_t
和ssize_t
可能是对应的无符号和有符号类型,因此(在所有实际体系结构上)SIZE_MAX > SSIZE_MAX
.将无符号的值强制转换为无法保存该值的带符号的类型是未定义的行为.因此,从技术上讲,您的宏存在问题.
实际上,至少在64位平台上,如果要转换为ssize_t
的值是实际存在的对象的大小,则不太可能遇到麻烦.但是,如果对象是理论性的(例如sizeof(char[3][1ULL<<62])
),则可能会令人不快.
请注意,类型ssize_t
的唯一有效负值是-1,这是一个错误指示.您可能会将Posix定义的ssize_t
与size_t
对应的有符号整数类型,但是任何一种标准都不能保证这些行为.但是,这两种类型的语义是不同的,因此在使用它们时应注意以下几点:
-
ssize_t
由多个Posix接口返回,以使该函数可以发信号通知已处理的字节数或错误指示.错误指示必须为-1.没想到ssize_t
可以容纳任何可能的大小; Posix的基本原理指出:符合条件的应用程序将被限制为不能以大于
{SSIZE_MAX}
的片段的形式执行I/O.对于大多数返回
ssize_t
的接口来说,这不是问题,因为Posix通常不需要接口来保证处理所有数据.例如,read
和write
都接受描述要读取/写入的缓冲区长度的size_t
,并返回描述实际读取/写入的字节数的ssize_t
.这意味着即使有更多数据可用,也不会读取/写入不超过SSIZE_MAX
个字节.但是,Posix原理还指出,特定的实现可能会提供一个扩展,该扩展允许处理更大的块(如果扩展提供了扩展范围,则使用扩展的兼容应用程序将能够使用整个范围"),该实现可以例如指定将-1以外的返回值转换为size_t
来解释.这样的扩展将无法移植;在实践中,大多数实现确实将一次调用中可以处理的字节数限制为可以在ssize_t
中报告的字节数. -
ptrdiff_t
是(在标准C中)两个指针之间的差值结果的类型.为了正确定义指针的减法,两个指针必须指向同一对象,方法是指向该对象或指向紧随该对象之后的字节. C委员会认识到,如果ptrdiff_t
是size_t
的有符号等效项,则可能无法表示两个指针之间的差异,从而导致不确定的行为,但他们宁愿要求ptrdiff_t
为a类型大于size_t
.您可以对这个决定提出异议-很多人已经做出了-但自C90以来它就一直存在,并且现在看来改变的可能性不大. (来自§ 6.5.6/9的当前标准措辞:如果结果无法在类型[ptrdiff_t
]的对象中表示,则该行为是不确定的.")与Posix一样,C标准没有定义未定义的行为,因此将禁止解释为非常大的对象中的两个指针相减是错误的.始终允许实现定义标准未定义的行为结果,因此对于实现来说,指定
P
和Q
是指向同一对象的两个指针,而P >= Q
则完全有效.(size_t)(P - Q)
是指针之间在数学上正确的差,即使减法溢出也是如此.当然,依赖于这种扩展的代码不会完全可移植,但是如果扩展足够普遍,那可能就不成问题了.
ssize_t
中)和作为可能的指针减法结果(在ptrdiff_t
中)使用模棱两可size_t
和指针一样大.如果size_t
与指针一样大,则P-Q
的数学正确值可以是(size_t)(-1)
(又名SIZE_MAX
)的唯一方法是P
和Q
所引用的对象是大小SIZE_MAX
,假定size_t
与指针的宽度相同,则表示对象加上下一个字节占据了每个可能的指针值.这与某些指针值(NULL
)与任何有效地址不同的要求相矛盾,因此我们可以得出结论,对象的真实最大大小必须小于SIZE_MAX
. It happened to me that I needed to compare the result of sizeof(x)
to a ssize_t
.
Of course GCC gave an error (lucky me (I used -Wall -Wextra -Werror
)), and I decided to do a macro to have a signed version of sizeof()
.
#define ssizeof (ssize_t)sizeof
And then I can use it like this:
for (ssize_t i = 0; i < ssizeof(x); i++)
The problem is, do I have any guarantees that SSIZE_MAX >= SIZE_MAX
? I imagine that sadly this is never going to be true.
Or at least that sizeof(ssize_t) == sizeof(size_t)
, which would cut half of the values but would still be close enough.
I didn't find any relation between ssize_t
and size_t
in the POSIX documentation.
Related question:
What type should be used to loop through an array?
There is no guarantee that SSIZE_MAX >= SIZE_MAX
. In fact, it is very unlikely to be the case, since size_t
and ssize_t
are likely to be corresponding unsigned and signed types, so (on all actual architectures) SIZE_MAX > SSIZE_MAX
. Casting an unsigned value to a signed type which cannot hold that value is Undefined Behaviour. So technically, your macro is problematic.
In practice, at least on 64-bit platforms, you're unlikely to get into trouble if the value you are converting to ssize_t
is the size of an object which actually exists. But if the object is theoretical (eg sizeof(char[3][1ULL<<62])
), you might get an unpleasant surprise.
Note that the only valid negative value of type ssize_t
is -1, which is an error indication. You might be confusing ssize_t
, which is defined by Posix, with ptrdiff_t
, which is defined in standard C since C99. These two types are the same on most platforms, and are usually the signed integer type corresponding to size_t
, but none of those behaviours is guaranteed by either standard. However, the semantics of the two types are different, and you should be aware of that when you use them:
ssize_t
is returned by a number of Posix interfaces in order to allow the function to signal either a number of bytes processed or an error indication; the error indication must be -1. There is no expectation that any possible size will fit intossize_t
; the Posix rationale states that:A conforming application would be constrained not to perform I/O in pieces larger than
{SSIZE_MAX}
.This is not a problem for most of the interfaces which return
ssize_t
because Posix generally does not require interfaces to guarantee to process all data. For example, bothread
andwrite
accept asize_t
which describes the length of the buffer to be read/written and return anssize_t
which describes the number of bytes actually read/written; the implication is that no more thanSSIZE_MAX
bytes will be read/written even if more data were available. However, the Posix rationale also notes that a particular implementation may provide an extension which allows larger blocks to be processed ("a conforming application using extensions would be able to use the full range if the implementation provided an extended range"), the idea being that the implementation could, for example, specify that return values other than -1 were to be interpreted by casting them tosize_t
. Such an extension would not be portable; in practices, most implementations do limit the number of bytes which can be processed in a single call to the number which can be reported inssize_t
.ptrdiff_t
is (in standard C) the type of the result of the difference between two pointers. In order for subtraction of pointers to be well defined, the two pointers must refer to the same object, either by pointing into the object or by pointing at the byte immediately following the object. The C committee recognised that ifptrdiff_t
is the signed equivalent ofsize_t
, then it is possible that the difference between two pointers might not be representable, leading to undefined behaviour, but they preferred that to requiring thatptrdiff_t
be a larger type thansize_t
. You can argue with this decision -- many people have -- but it has been in place since C90 and it seems unlikely that it will change now. (Current standard wording from , §6.5.6/9: "If the result is not representable in an object of that type [ptrdiff_t
], the behavior is undefined.")As with Posix, the C standard does not define undefined behaviour, so it would be a mistake to interpret that as forbidding the subtraction of two pointers in very large objects. An implementation is always allowed to define the result of behaviour left undefined by the standard, so that it is completely valid for an implementation to specify that if
P
andQ
are two pointers to the same object whereP >= Q
, then(size_t)(P - Q)
is the mathematically correct difference between the pointers even if the subtraction overflows. Of course, code which depends on such an extension won't be fully portable, but if the extension is sufficiently common that might not be a problem.
As a final point, the ambiguity of using -1 both as an error indication (in ssize_t
) and as a possibly castable result of pointer subtraction (in ptrdiff_t
) is not likely to be a present in practice provided that size_t
is as large as a pointer. If size_t
is as large as a pointer, the only way that the mathematically correct value of P-Q
could be (size_t)(-1)
(aka SIZE_MAX
) is if the object that P
and Q
refer to is of size SIZE_MAX
, which, given the assumption that size_t
is the same width as a pointer, implies that the object plus the following byte occupy every possible pointer value. That contradicts the requirement that some pointer value (NULL
) be distinct from any valid address, so we can conclude that the true maximum size of an object must be less than SIZE_MAX
.
这篇关于将sizeof()的结果分配给ssize_t的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!