将sizeof()的结果分配给ssize_t

问题描述 投票:3回答:4

碰巧我需要将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_tsize_t之间的任何关系。

相关问题:

What type should be used to loop through an array?

c posix sizeof
4个回答
5
投票

没有保证SSIZE_MAX >= SIZE_MAX。实际上,情况不太可能如此,因为size_tssize_t可能是相应的无符号和有符号类型,所以(在所有实际架构上)SIZE_MAX > SSIZE_MAX。将无符号值转换为无法保存该值的带符号类型的是Undefined Behavior。从技术上讲,你的宏是有问题的。

实际上,至少在64位平台上,如果转换为ssize_t的值是实际存在的对象的大小,则不太可能遇到麻烦。但如果对象是理论上的(例如sizeof(char[3][1ULL<<62])),你可能会得到一个令人不快的惊喜。

请注意,ssize_t类型的唯一有效负值为-1,这是一个错误指示。您可能会将由Posix定义的ssize_tptrdiff_t混淆,size_t是自C99以来在标准C中定义的。这两种类型在大多数平台上都是相同的,并且通常是对应于ssize_t的有符号整数类型,但这些行为都不受任何标准的保证。但是,这两种类型的语义是不同的,您在使用它们时应该注意这一点:

  • ssize_t由许多Posix接口返回,以便允许函数发出信号,表示处理的字节数或错误指示;错误指示必须为-1。没有任何期望任何可能的大小适合{SSIZE_MAX}; Posix的理由说明: 符合要求的应用程序将被限制为不执行大于ssize_t的I / O. 对于返回read的大多数接口来说,这不是问题,因为Posix通常不需要接口来保证处理所有数据。例如,writesize_t都接受一个ssize_t,它描述了要读/写的缓冲区的长度,并返回一个SSIZE_MAX,它描述了实际读/写的字节数;这意味着即使有更多数据可用,也不会读取/写入size_t字节。但是,Posix基本原理还指出,特定实现可以提供允许处理更大块的扩展(“如果实现提供扩展范围,则使用扩展的符合应用程序将能够使用全范围”),该想法是例如,实现可以指定通过将它们转换为ssize_t来解释除-1以外的返回值。这种扩展不可移植;在实践中,大多数实现确实将单个调用中可处理的字节数限制为可以在ptrdiff_t中报告的数字。
  • ptrdiff_t是(在标准C中)两个指针之间差异的结果类型。为了明确定义指针的减法,两个指针必须通过指向对象或通过指向紧跟在对象之后的字节来引用同一个对象。 C委员会认识到,如果size_tptrdiff_t的签名等价物,则两个指针之间的差异可能无法表示,导致未定义的行为,但他们更倾向于要求size_tptrdiff_t更大。你可以争论这个决定 - 很多人都有 - 但它自C90以来就已存在,现在看来它不太可能改变。 (当前的标准措辞,§6.5.6/ 9:“如果结果在该类型的对象[P]中无法表示,则行为未定义。”) 与Posix一样,C标准没有定义未定义的行为,因此将其解释为禁止在非常大的对象中减去两个指针是错误的。总是允许实现定义标准未定义的行为结果,因此它对于实现来说是完全有效的,如果QP >= Q是指向(size_t)(P - Q)的同一对象的两个指针,那么ssize_t是数学上正确的差异即使减法溢出,指针之间也是如此。当然,依赖于这种扩展的代码将不是完全可移植的,但是如果扩展是足够普遍的可能不是问题。

作为最后一点,使用-1作为错误指示(在ptrdiff_t中)和指针减法(在size_t中)可能的可投射结果的模糊性在实践中不太可能存在,只要size_t与a一样大。指针。如果P-Q和指针一样大,那么(size_t)(-1)的数学正确值可能是SIZE_MAX(又名P)的唯一方法是,如果QSIZE_MAX所指的对象的大小为size_t,假设NULL是与指针相同的宽度意味着对象加上后面的字节占用每个可能的指针值。这与某些指针值(SIZE_MAX)与任何有效地址不同的要求相矛盾,因此我们可以得出结论,对象的真实最大大小必须小于SSIZE_T_MAX


3
投票

请注意,您实际上无法做到这一点。

x86 Linux中最大的可能对象大小低于0xB0000000,而read为0x7FFFFFFF。

我没有检查ssize_t result = read(fd, buf, count); if (result != -1) { size_t offset = (size_t) result; /* handle success */ } else { /* handle failure */ } 和东西是否真的可以处理最大可能的对象,但是如果它们可以这样工作:

libc

你可能会发现ssize_t result = sys_read(fd, buf, count); if (result >= 0 || result < -256) { size_t offset = (size_t) result; /* handle success */ } else { errno = (int)-result; /* handle failure */ } 被破坏了。如果是这样,如果内核是好的,这将有效:

sizeof

2
投票

我会把它作为一个X-Y问题。您遇到的问题是您要将签名号码与无符号号码进行比较。而不是将ssize_t的结果转换为ssize_t,你应该检查你的size_t值是否小于零。如果是,那么你知道它小于你的size_t值。如果没有,那么你可以将其投射到-1然后进行比较。

例如,这是一个比较函数,如果有符号数小于无符号数,则返回int compare(ssize_t signed_number, size_t unsigned_number) { int ret; if (signed_number < 0 || (size_t) signed_number < unsigned_number) { ret = -1; } else { ret = (size_t) signed_number > unsigned_number; } return ret; } ,如果相等则返回0,如果有符号数大于无符号数,则返回1:

<

如果你想要的只是(signed_number < 0 || (size_t) signed_number < unsigned_number)) 操作,你可以用这样的东西更简单:

1

如果signed_number小于unsigned_number,那条线将给你<,它没有分支开销。只需要额外的logical-OR操作和official spec for read()


1
投票

ssize_t是POSIX类型,它未被定义为C标准的一部分。 POSIX定义ssize_t必须能够处理区间[-1,SSIZE_MAX]中的数字,因此原则上它甚至不需要是普通的签名类型。这个稍微奇怪的定义的原因是使用ssize_t的唯一地方是读/写/等的返回值。功能。

在实践中,它始终是与size_t大小相同的普通签名类型。但是如果你想对你的类型非常迂腐,那么除了处理IO系统调用的返回值之外,你不应该将它用于其他目的。对于通用的“指针大小”有符号整数类型,C89定义了ptrdiff_t。实际上它与ssize_t相同。

另外,如果你看一下qazxswpoi,你会看到'nbyte'参数说它'如果nbyte的值大于{SSIZE_MAX},结果是实现定义的。'。因此,即使size_t能够表示比SSIZE_MAX更大的值,它的实现定义行为也要使用比IO系统调用更大的值(如上所述,使用ssize_t的唯一地方)。类似于write()等。

© www.soinside.com 2019 - 2024. All rights reserved.