C 问题:off_t(和其他有符号整数类型)最小值和最大值

发布于 2024-10-09 00:46:52 字数 3410 浏览 5 评论 0原文

我偶尔会遇到一种整数类型(例如 POSIX 有符号整数类型 off_t),如果有一个用于其最小值和最大值的宏会很有帮助,但我不知道如何制作一个是真正的便携式。


对于无符号整数类型,我一直认为这很简单。 0 为最小值,~0 为最大值。此后我读到了几个不同的 SO 线程,它们建议使用 -1 而不是 ~0 以实现可移植性。这里有一个有趣的话题,有一些争议:
c++ - 使用安全吗 - 1 将所有位设置为真? - Stack Overflow

然而,即使在阅读了这个问题之后,我仍然感到困惑。另外,我正在寻找同时兼容 C89 和 C99 的东西,所以我不知道相同的方法是否适用。假设我有一个 uint_whatever_t 类型。我不能先转换为 0,然后按位求补吗?这样可以吗?:

#define UINT_WHATEVER_T_MAX ( ~ (uint_whatever_t) 0 )


有符号整数类型看起来是一个更难破解的难题。我已经看到了几种不同的可能解决方案,但只有 一个 似乎是可移植的。要么是这样,要么是不正确的。我在谷歌搜索 OFF_T_MAX 和 OFF_T_MIN 时发现了它。感谢克里斯蒂安·比埃尔:

#define MAX_INT_VAL_STEP(t) \
    ((t) 1 << (CHAR_BIT * sizeof(t) - 1 - ((t) -1 < 1))) 

#define MAX_INT_VAL(t) \
    ((MAX_INT_VAL_STEP(t) - 1) + MAX_INT_VAL_STEP(t))

#define MIN_INT_VAL(t) \
    ((t) -MAX_INT_VAL(t) - 1)

[...]
#define OFF_T_MAX MAX_INT_VAL(off_t) 


我在 C89 中找不到有关不同允许类型的有符号整数表示的任何内容,但 C99 在 §J.3.5 中有关于整数可移植性问题的注释:

有符号整数类型是否使用符号和大小、二进制表示 补语,还是一个人的补语,以及非凡的价值是否是一个陷阱 表示或普通值(6.2.6.2)。

这似乎意味着只能使用列出的三个有符号数字表示形式。含义是否正确,上面的宏是否与所有三种表示形式兼容?


Other thoughts:
It seems that the function-like macro MAX_INT_VAL_STEP() would give an incorrect result if there were padding bits. I wonder if there is any way around this.

通读 维基百科上的有符号数字表示,我发现对于所有三个有符号整数表示,任何有符号整数类型的 MAX 为:
符号位关闭,所有值位打开(全部三个)
它的 MIN 可以是:
符号位打开,所有值位打开(符号和幅度)
符号位打开,所有值位关闭(一个/两个补码)

我想我可以通过这样做来测试符号和幅度:

#define OFF_T_MIN ( ( ( (off_t)1 | ( ~ (off_t) -1 ) ) != (off_t)1 ) ? /* sign and magnitude minimum value here */ : /* ones and twos complement minimum value here */ )

然后,由于符号和幅度是符号位打开,并且所有值位打开不会是 off_t 的最小值情况是 ~ (off_t) 0 ?对于一/二补码最小值,我需要某种方法来关闭所有值位,但保留符号位。在不知道值位数的情况下不知道如何做到这一点。另外,符号位是否保证始终比最高有效值位高一位?

谢谢,如果这篇文章太长,请告诉我



编辑于 2010 年 12 月 29 日下午 5 点(美国东部时间):
正如下面 ephemient 回答的那样,为了获取无符号类型最大值,(unsigned type)-1~0 甚至 ~(unsigned type)0 更正确。据我所知,当您使用 -1 时,它与 0-1 相同,这总是会导致无符号类型中的最大值。

此外,因为可以确定无符号类型的最大值,所以可以确定无符号类型中有多少个值位。感谢 Hallvard B. Furuseth 在回复 关于 comp.lang.c 的问题

/* Number of bits in inttype_MAX, or in any (1<<b)-1 where 0 <= b < 3E+10 */
#define IMAX_BITS(m) ((m) /((m)%0x3fffffffL+1) /0x3fffffffL %0x3fffffffL *30 \
                  + (m)%0x3fffffffL /((m)%31+1)/31%31*5 + 4-12/((m)%31+3))

IMAX_BITS(INT_MAX) 计算 int 中的位数,IMAX_BITS((unsigned_type)-1) 计算 unsigned_type 中的位数。无论如何,直到有人实现 4 GB 整数:-)

然而,我的问题的核心仍然没有得到解答:如何通过宏确定有符号类型的最小值和最大值。我还在研究这个。也许答案是没有答案。

如果您没有在 StackOverflow 上查看此问题,则在大多数情况下您无法看到建议的答案,直到它们被接受为止。建议在 StackOverflow 上查看此问题

I occasionally will come across an integer type (e.g. POSIX signed integer type off_t) where it would be helpful to have a macro for its minimum and maximum values, but I don't know how to make one that is truly portable.

For unsigned integer types I had always thought this was simple. 0 for the minimum and ~0 for the maximum. I have since read of several different SO threads which suggest using -1 instead of ~0 for portability. An interesting thread with some contention is here:
c++ - Is it safe to use -1 to set all bits to true? - Stack Overflow

However even after reading about this issue I'm still confused. Also, I'm looking for something both C89 and C99 compliant so I don't know if the same methods apply. Say I had a type of uint_whatever_t. Couldn't I just cast to 0 first and then bitwise complement? Would this be ok?:

#define UINT_WHATEVER_T_MAX ( ~ (uint_whatever_t) 0 )

Signed integer types look like they'll be a tougher nut to crack. I've seen several different possible solutions but only one appears to be portable. Either that or it's incorrect. I found it while googling for an OFF_T_MAX and OFF_T_MIN. Credit to Christian Biere:

#define MAX_INT_VAL_STEP(t) \
    ((t) 1 << (CHAR_BIT * sizeof(t) - 1 - ((t) -1 < 1))) 

#define MAX_INT_VAL(t) \
    ((MAX_INT_VAL_STEP(t) - 1) + MAX_INT_VAL_STEP(t))

#define MIN_INT_VAL(t) \
    ((t) -MAX_INT_VAL(t) - 1)

[...]
#define OFF_T_MAX MAX_INT_VAL(off_t) 

I couldn't find anything regarding the different allowable types of signed integer representations in C89, but C99 has notes for integer portability issues in §J.3.5:

Whether signed integer types are represented using sign and magnitude, two’s
complement, or ones’ complement, and whether the extraordinary value is a trap
representation or an ordinary value (6.2.6.2).

That would seem to imply that only those three listed signed number representations can be used. Is the implication correct, and are the macros above compatible with all three representations?


Other thoughts:
It seems that the function-like macro MAX_INT_VAL_STEP() would give an incorrect result if there were padding bits. I wonder if there is any way around this.

Reading through signed number representations on Wikipedia it occurs to me that for all three signed integer representations any signed integer type's MAX would be:
sign bit off, all value bits on (all three)
And its MIN would be either:
sign bit on, all value bits on (sign and magnitude)
sign bit on, all value bits off (ones/twos complement)

I think I could test for sign and magnitude by doing this:

#define OFF_T_MIN ( ( ( (off_t)1 | ( ~ (off_t) -1 ) ) != (off_t)1 ) ? /* sign and magnitude minimum value here */ : /* ones and twos complement minimum value here */ )

Then as sign and magnitude is sign bit on and all value bits on wouldn't the minimum for off_t in that case be ~ (off_t) 0 ? And for ones/twos complement minimum I would need some way to turn all the value bits off but leave the sign bit on. No idea how to do this without knowing the number of value bits. Also is the sign bit guaranteed to always be one more significant than the most significant value bit?

Thanks and please let me know if this is too long a post


EDIT 12/29/2010 5PM EST:
As answered below by ephemient to get the unsigned type max value, (unsigned type)-1 is more correct than ~0 or even ~(unsigned type)0. From what I can gather when you use -1 it is just the same as 0-1 which will always lead to the maximum value in an unsigned type.

Also, because the maximum value of an unsigned type can be determined it is possible to determine how many value bits are in an unsigned type. Credit to Hallvard B. Furuseth for his IMAX_BITS() function-like macro that he posted in reply to a question on comp.lang.c

/* Number of bits in inttype_MAX, or in any (1<<b)-1 where 0 <= b < 3E+10 */
#define IMAX_BITS(m) ((m) /((m)%0x3fffffffL+1) /0x3fffffffL %0x3fffffffL *30 \
                  + (m)%0x3fffffffL /((m)%31+1)/31%31*5 + 4-12/((m)%31+3))

IMAX_BITS(INT_MAX) computes the number of bits in an int, and IMAX_BITS((unsigned_type)-1) computes the number of bits in an unsigned_type. Until someone implements 4-gigabyte integers, anyway:-)

The heart of my question however remains unanswered: how to determine the minimum and maximum values of a signed type via macro. I'm still looking into this. Maybe the answer is there is no answer.

If you are not viewing this question on StackOverflow in most cases you cannot see the proposed answers until they are accepted. It is suggested to view this question on StackOverflow.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

泪痕残 2024-10-16 00:46:52

我相信我终于解决了这个问题,但该解决方案仅在配置时可用,而不是在编译时或运行时可用,所以它仍然不知道。如下:

HEADERS="#include <sys/types.h>"
TYPE="off_t"
i=8
while : ; do
printf "%s\nstruct { %s x : %d; };\n" "$HEADERS" "$TYPE" $i > test.c
$CC $CFLAGS -o /dev/null -c test.c || break
i=$(($i+1))
done
rm test.c
echo $(($i-1))

这个想法来自 6.7.2.1 第 3 段:

指定位域宽度的表达式应为整数常量
具有不超过对象宽度的非负值的表达式
省略冒号和表达式时将指定的类型。如果该值为零,
声明不应有声明符。

如果这能带来任何在编译时解决问题的想法,我将非常高兴。

I believe I have finally solved this problem, but the solution is only available at configure-time, not compile-time or runtime, so it's still not idea. Here it is:

HEADERS="#include <sys/types.h>"
TYPE="off_t"
i=8
while : ; do
printf "%s\nstruct { %s x : %d; };\n" "$HEADERS" "$TYPE" $i > test.c
$CC $CFLAGS -o /dev/null -c test.c || break
i=$(($i+1))
done
rm test.c
echo $(($i-1))

The idea comes from 6.7.2.1 paragraph 3:

The expression that specifies the width of a bit-field shall be an integer constant
expression with a nonnegative value that does not exceed the width of an object of the
type that would be specified were the colon and expression omitted. If the value is zero,
the declaration shall have no declarator.

I would be quite pleased if this leads to any ideas for solving the problem at compile-time.

琴流音 2024-10-16 00:46:52

令人惊讶的是,C 在算术运算之前将类型提升到 int,结果至少为 int 大小。 (类似的奇怪之处包括 'a' 字符文字的类型为 int,而不是 char。)

int a = (uint8_t)1 + (uint8_t)-1;
   /* = (uint8_t)1 + (uint8_t)255 = (int)256 */
int b = (uint8_t)1 + ~(uint8_t)0;
   /* = (uint8_t)1 + (int)-1 = (int)0 */

因此 #define UINT_WHATEVER_T_MAX ( ~ (uint_whatever_t) 0 ) 不一定没问题。

Surprisingly, C promotes types up to int before arithmetic operations, with results being at least int sized. (Similarly oddities include 'a' character literal having type int, not char.)

int a = (uint8_t)1 + (uint8_t)-1;
   /* = (uint8_t)1 + (uint8_t)255 = (int)256 */
int b = (uint8_t)1 + ~(uint8_t)0;
   /* = (uint8_t)1 + (int)-1 = (int)0 */

So #define UINT_WHATEVER_T_MAX ( ~ (uint_whatever_t) 0 ) isn't necessarily okay.

对你的占有欲 2024-10-16 00:46:52

有符号最大值:

#define GENERIC_S_MAX(stype) ((stype) ((1ULL << ((sizeof(stype) * 8) - 1)) - 1ULL))

假设您的系统使用二进制补码,有符号最小值应该是:

#define GENERIC_S_MIN(stype) ((stype) -1 - GENERIC_S_MAX(stype))

这些应该是完全可移植的,除了 long long 在技术上是 C89 中的编译器扩展。这也避免了有符号整数上溢/下溢的未定义行为。

Signed max:

#define GENERIC_S_MAX(stype) ((stype) ((1ULL << ((sizeof(stype) * 8) - 1)) - 1ULL))

Assuming your system uses two's complement, the signed min should be:

#define GENERIC_S_MIN(stype) ((stype) -1 - GENERIC_S_MAX(stype))

These should be totally portable, except that long long is technically a compiler extension in C89. This also avoids the undefined behavior of over/underflowing a signed integer.

不美如何 2024-10-16 00:46:52

对于符号数值表示,这相当简单(无论如何,对于至少与 int 一样宽的类型):

#define SM_TYPE_MAX(type) (~(type)-1 + 1)
#define SM_TYPE_MIN(type) (-TYPE_MAX(type))

不幸的是,符号数值表示在地面上相当薄弱;)

For sign-magnitude representations, it's fairly easy (for types at least as wide as int, anyway):

#define SM_TYPE_MAX(type) (~(type)-1 + 1)
#define SM_TYPE_MIN(type) (-TYPE_MAX(type))

Unfortunately, sign-magnitude representations are rather thin on the ground ;)

我很OK 2024-10-16 00:46:52

仅快速回答:

#define UINT_WHATEVER_T_MAX ( ~ (uint_whatever_t) 0 ) 对我来说看起来不错,-1 的偏好是 uint_whatever_t = -1;更简洁code>uint_whatever_t = ~(uint_whatever_t)0;

(CHAR_BIT * sizeof(t)) 看起来并不严格符合我的要求。您对填充位的看法是正确的,因此该值可能比类型的宽度大得多,除非 Posix 关于 off_t 另有说明。

相比之下,C99 中的固定宽度整数类型不得具有填充位,因此对于intN_t,您可以使用大小来推断宽度。它们也保证是补码。

这似乎意味着只有
这三个列出的签名号码
可以使用表示法。是
暗示正确

是的。 6.2.6.2/2 列出了符号位的三种允许的含义,以及三种允许的带符号数字表示形式。

是符号位,保证始终
比最重要的人更重要
有效值位

它间接要求比值位更重要,事实上(再次参见 6.2.6.2/2)“作为值位的每个位应具有与
相应无符号类型的对象表示中的相同位”。因此值位必须是从最低有效位开始的连续范围。

但是,您不能仅可移植地设置符号位。请阅读 6.2.6.2/3 和 / 4,关于负零,请注意,即使实现原则上使用具有它们的表示,它也不必支持它们,并且在符号+幅度实现上没有保证生成负零的方法。 。

[编辑:哦,我读错了,你只需要在排除符号+幅度之后生成该值,所以

说实话,如果我听起来有点麻木 Posix 定义了一个整数类型,但没有为它们提供限制,我可能会采用旧的“移植标头”方法,将可能在任何地方都可以工作的东西放在标头中,并记录下来。与他们通常必须做的事情相比,他们可能会在任何奇怪的实现上编译代码之前检查它,他们会很高兴地接受这一点。

Quick answers only:

#define UINT_WHATEVER_T_MAX ( ~ (uint_whatever_t) 0 ) looks OK to me, the preference for -1 is that uint_whatever_t = -1; is more concise than uint_whatever_t = ~(uint_whatever_t)0;

(CHAR_BIT * sizeof(t)) looks not strictly conforming to me. You're right about padding bits, so this value might be considerably more than the width of the type unless Posix says otherwise about off_t.

In contrast, the fixed-width integer types in C99 must not have padding bits, so for intN_t you're on firmer ground using the size to deduce the width. They're also guaranteed two's complement.

That would seem to imply that only
those three listed signed number
representations can be used. Is the
implication correct

Yes. 6.2.6.2/2 lists the three permissible meanings of the sign bit, and hence the three permissible signed number representations.

is the sign bit guaranteed to always
be one more significant than the most
significant value bit

It's indirectly required to be more significant than the value bits, by the fact (6.2.6.2/2 again) that "Each bit that is a value bit shall have the same value as
the same bit in the object representation of the corresponding unsigned type". So the value bits must be a contiguous range starting at the least significant.

However, you can't portably set just the sign bit. Read 6.2.6.2/3 and /4, about negative zeros, and note that even if the implementation uses a representation that has them in principle, it doesn't have to support them, and there's no guaranteed way of generating one. On a sign+magnitude implementation, the thing you want is a negative zero.

[Edit: oh, I misread, you only need to generate that value after you've ruled out sign+magnitude, so you could still be OK.

To be honest, it sounds a bit numpty to me if Posix has defined an integer type and not provided limits for it. Boo to them. I'd probably go with the old, "porting header" approach, where you put the thing that probably works everywhere in a header, and document that someone should probably check it before compiling the code on any freakish implementations. Compared with what they normally have to do to get anybody's code to work, they'll happily live with that.]

海拔太高太耀眼 2024-10-16 00:46:52

从技术上讲,它不是一个宏,但实际上,以下内容应始终折叠为 off_t 或任何有符号类型的常量最小值,无论符号表示如何。虽然我不确定什么不需要两个人的恭维,如果有的话。

POSIX 要求 off_t 使用有符号整数类型,因此 C99 有符号精确宽度值应该足够了。有些平台实际上定义了 OFF_T_MIN (OSX),但不幸的是 POSIX 并不需要它。

#include <stdint.h>
#include <assert.h>

#include <sys/types.h>

  assert(sizeof(off_t) >= sizeof(int8_t) && sizeof(off_t) <= sizeof(intmax_t));

  const off_t OFF_T_MIN = sizeof(off_t) == sizeof(int8_t)   ? INT8_MIN    :
                          sizeof(off_t) == sizeof(int16_t)  ? INT16_MIN   :
                          sizeof(off_t) == sizeof(int32_t)  ? INT32_MIN   :
                          sizeof(off_t) == sizeof(int64_t)  ? INT64_MIN   :
                          sizeof(off_t) == sizeof(intmax_t) ? INTMAX_MIN  : 0;

同样可用于获得最大值。

  assert(sizeof(off_t) >= sizeof(int8_t) && sizeof(off_t) <= sizeof(intmax_t));

  const off_t OFF_T_MAX = sizeof(off_t) == sizeof(int8_t)   ? INT8_MAX    :
                          sizeof(off_t) == sizeof(int16_t)  ? INT16_MAX   :
                          sizeof(off_t) == sizeof(int32_t)  ? INT32_MAX   :
                          sizeof(off_t) == sizeof(int64_t)  ? INT64_MAX   :
                          sizeof(off_t) == sizeof(intmax_t) ? INTMAX_MAX  : 0;

不过,可以使用 autoconf 或 cmake 将其转换为宏。

It's technically not a macro, but in practice the following should always be folded into a constant minimum for off_t, or any signed type, regardless of sign representation. Although I'm not sure what doesn't use two's compliment, if anything.

POSIX requires a signed integer type for off_t, so the C99 signed exact width values should be sufficient. Some platforms actually define OFF_T_MIN (OSX), but POSIX unfortunately doesn't require it.

#include <stdint.h>
#include <assert.h>

#include <sys/types.h>

  assert(sizeof(off_t) >= sizeof(int8_t) && sizeof(off_t) <= sizeof(intmax_t));

  const off_t OFF_T_MIN = sizeof(off_t) == sizeof(int8_t)   ? INT8_MIN    :
                          sizeof(off_t) == sizeof(int16_t)  ? INT16_MIN   :
                          sizeof(off_t) == sizeof(int32_t)  ? INT32_MIN   :
                          sizeof(off_t) == sizeof(int64_t)  ? INT64_MIN   :
                          sizeof(off_t) == sizeof(intmax_t) ? INTMAX_MIN  : 0;

The same is usable to obtain the maximum value.

  assert(sizeof(off_t) >= sizeof(int8_t) && sizeof(off_t) <= sizeof(intmax_t));

  const off_t OFF_T_MAX = sizeof(off_t) == sizeof(int8_t)   ? INT8_MAX    :
                          sizeof(off_t) == sizeof(int16_t)  ? INT16_MAX   :
                          sizeof(off_t) == sizeof(int32_t)  ? INT32_MAX   :
                          sizeof(off_t) == sizeof(int64_t)  ? INT64_MAX   :
                          sizeof(off_t) == sizeof(intmax_t) ? INTMAX_MAX  : 0;

This could be turned into a macro using autoconf or cmake though.

千紇 2024-10-16 00:46:52

从 C11 开始,您可以使用 _Generic 来查找基础类型。在此之前,带有 __typeof__builtin_types_known_p 的 __builtin_choose_expr 是相当可移植的。

如果您不想使用其中任何一个,您可以根据其大小和符号来猜测类型。

#include <stdio.h>
#include <limits.h>
#define TP_MAX(Tp) ((Tp)-1>0 ? ( \
                        1==sizeof(Tp) ? ((Tp)2==1?1:UCHAR_MAX) \
                        : sizeof(unsigned short)==sizeof(Tp) ? USHRT_MAX \
                        : sizeof(unsigned int)==sizeof(Tp) ? UINT_MAX \
                        : sizeof(unsigned long)==sizeof(Tp) ? ULONG_MAX \
                        : sizeof(unsigned long long)==sizeof(Tp) ? ULLONG_MAX : 0 \
                   ) :  ( 1==sizeof(Tp) ? SCHAR_MAX \
                        : sizeof(short)==sizeof(Tp) ? SHRT_MAX \
                        : sizeof(int)==sizeof(Tp) ? INT_MAX \
                        : sizeof(long)==sizeof(Tp) ? LONG_MAX \
                        : sizeof(long long)==sizeof(Tp) ? LLONG_MAX : 0)) \


#define STC_ASSERT(X) ((void)(sizeof(struct { int stc_assert:(X)?1:-1; })))

int main()
{
    STC_ASSERT(TP_MAX(signed char)==SCHAR_MAX);
    STC_ASSERT(TP_MAX(short)==SHRT_MAX);
    STC_ASSERT(TP_MAX(int)==INT_MAX);
    STC_ASSERT(TP_MAX(long)==LONG_MAX);
    STC_ASSERT(TP_MAX(long long)==LLONG_MAX);
    STC_ASSERT(TP_MAX(unsigned char)==UCHAR_MAX);
    STC_ASSERT(TP_MAX(unsigned short)==USHRT_MAX);
    STC_ASSERT(TP_MAX(unsigned int)==UINT_MAX);
    STC_ASSERT(TP_MAX(unsigned long)==ULONG_MAX);
    STC_ASSERT(TP_MAX(unsigned long long)==ULLONG_MAX);
}

(如果您想在没有限制的情况下执行此操作,请查看我的答案 https://stackoverflow.com/a/53470064 /1084774)。

Since C11 you can use _Generic to find the underlying type. Before then __builtin_choose_expr with __typeof and __builtin_types_compatible_p was fairly portable.

If you don't want to use any of those, you could guess the type based on its size and signedness.

#include <stdio.h>
#include <limits.h>
#define TP_MAX(Tp) ((Tp)-1>0 ? ( \
                        1==sizeof(Tp) ? ((Tp)2==1?1:UCHAR_MAX) \
                        : sizeof(unsigned short)==sizeof(Tp) ? USHRT_MAX \
                        : sizeof(unsigned int)==sizeof(Tp) ? UINT_MAX \
                        : sizeof(unsigned long)==sizeof(Tp) ? ULONG_MAX \
                        : sizeof(unsigned long long)==sizeof(Tp) ? ULLONG_MAX : 0 \
                   ) :  ( 1==sizeof(Tp) ? SCHAR_MAX \
                        : sizeof(short)==sizeof(Tp) ? SHRT_MAX \
                        : sizeof(int)==sizeof(Tp) ? INT_MAX \
                        : sizeof(long)==sizeof(Tp) ? LONG_MAX \
                        : sizeof(long long)==sizeof(Tp) ? LLONG_MAX : 0)) \


#define STC_ASSERT(X) ((void)(sizeof(struct { int stc_assert:(X)?1:-1; })))

int main()
{
    STC_ASSERT(TP_MAX(signed char)==SCHAR_MAX);
    STC_ASSERT(TP_MAX(short)==SHRT_MAX);
    STC_ASSERT(TP_MAX(int)==INT_MAX);
    STC_ASSERT(TP_MAX(long)==LONG_MAX);
    STC_ASSERT(TP_MAX(long long)==LLONG_MAX);
    STC_ASSERT(TP_MAX(unsigned char)==UCHAR_MAX);
    STC_ASSERT(TP_MAX(unsigned short)==USHRT_MAX);
    STC_ASSERT(TP_MAX(unsigned int)==UINT_MAX);
    STC_ASSERT(TP_MAX(unsigned long)==ULONG_MAX);
    STC_ASSERT(TP_MAX(unsigned long long)==ULLONG_MAX);
}

(If you want to do it even without limits.h, please check out my answer at https://stackoverflow.com/a/53470064/1084774).

冷了相思 2024-10-16 00:46:52

TL;DR:使用下面列出的头文件,然后使用 TYPE_MAX(someType) 获取 someType 使用的类型的最大值。

您可以使用 C11 中引入的 _Generic 表达式。
您需要编译器支持的每个基本整数类型(例如 charlong 等)的列表,以及每个类型定义的整数(最有可能的是 uint8_t< /code>, off_t) 将被视为基础类型。

下面是一个示例头文件:

#include <float.h>
#include <limits.h>
#include <stdint.h>
#include <stdbool.h>

#if UINTMAX_MAX==ULLONG_MAX
  #define TYPE_UINTMAX_MAX
  #define TYPE_UINTMAX_MIN
#else  //UINTMAX_MAX!=ULLONG_MAX
  #define TYPE_UINTMAX_MAX                    \
      , uintmax_t           : UINTMAX_MAX     \
      , intmax_t            : INTMAX_MAX      \

  #define TYPE_UINTMAX_MIN                    \
      , uintmax_t           : UINTMAX_MIN     \
      , intmax_t            : INTMAX_MIN      \

#endif //UINTMAX_MAX==ULLONG_MAX


#define TYPE_MAX(variable) _Generic          \
  (                                          \
    (variable)                               \
    , bool                : 1                \
    , char                : CHAR_MAX         \
    , unsigned char       : UCHAR_MAX        \
    , signed   char       : SCHAR_MAX        \
    , unsigned short      : USHRT_MAX        \
    , signed   short      : SHRT_MAX         \
    , unsigned int        : UINT_MAX         \
    , signed   int        : INT_MAX          \
    , unsigned long       : ULONG_MAX        \
    , signed   long       : LONG_MAX         \
    , unsigned long long  : ULLONG_MAX       \
    , signed   long long  : LLONG_MAX        \
    TYPE_UINTMAX_MAX                         \
                                             \
    , float               : FLT_MAX          \
    , double              : DBL_MAX          \
    , long double         : LDBL_MAX         \
  )

#define TYPE_MIN(variable) _Generic          \
  (                                          \
    (variable)                               \
    , bool                : 0                \
    , char                : CHAR_MIN         \
    , unsigned char       : 0                \
    , signed   char       : SCHAR_MIN        \
    , unsigned short      : 0                \
    , signed   short      : SHRT_MIN         \
    , unsigned int        : 0                \
    , signed   int        : INT_MIN          \
    , unsigned long       : 0                \
    , signed   long       : LONG_MIN         \
    , unsigned long long  : 0                \
    , signed   long long  : LLONG_MIN        \
    TYPE_UINTMAX_MIN                         \
                                             \
    , float               : -FLT_MAX         \
    , double              : -DBL_MAX         \
    , long double         : -LDBL_MAX        \
  )

假设 off_t 是用 typedef int64_t off_t 定义的,而 int64_t 是用 typedef long long int64_t< 定义的/code>,那么C编译器将处理off_t foo; TYPE_MAX(foo)long long foo 相同; TYPE_MAX(foo) 并将选择值为 LLONG_MAX 的选项,为您提供最大值。

如果系统具有此头文件中未列出的其他本机类型,请创建一个预处理器变量,该变量在其他系统上为空,但在使用该类型的系统上进行编译时包含该本机类型的值。然后将此预处理器变量添加到列表中,类似于此处使用 uintmax_t 完成的操作。

TL;DR: Use the header file listed below and then use TYPE_MAX(someType) to get the maximum of the type used by someType.

You can use the _Generic expression, introduced with C11.
You need a list of every base integer type (like char, long, ...) your compiler supports, every typedefed integer (most likely things like uint8_t, off_t) will be treated like the underlying type.

Here is an example header file:

#include <float.h>
#include <limits.h>
#include <stdint.h>
#include <stdbool.h>

#if UINTMAX_MAX==ULLONG_MAX
  #define TYPE_UINTMAX_MAX
  #define TYPE_UINTMAX_MIN
#else  //UINTMAX_MAX!=ULLONG_MAX
  #define TYPE_UINTMAX_MAX                    \
      , uintmax_t           : UINTMAX_MAX     \
      , intmax_t            : INTMAX_MAX      \

  #define TYPE_UINTMAX_MIN                    \
      , uintmax_t           : UINTMAX_MIN     \
      , intmax_t            : INTMAX_MIN      \

#endif //UINTMAX_MAX==ULLONG_MAX


#define TYPE_MAX(variable) _Generic          \
  (                                          \
    (variable)                               \
    , bool                : 1                \
    , char                : CHAR_MAX         \
    , unsigned char       : UCHAR_MAX        \
    , signed   char       : SCHAR_MAX        \
    , unsigned short      : USHRT_MAX        \
    , signed   short      : SHRT_MAX         \
    , unsigned int        : UINT_MAX         \
    , signed   int        : INT_MAX          \
    , unsigned long       : ULONG_MAX        \
    , signed   long       : LONG_MAX         \
    , unsigned long long  : ULLONG_MAX       \
    , signed   long long  : LLONG_MAX        \
    TYPE_UINTMAX_MAX                         \
                                             \
    , float               : FLT_MAX          \
    , double              : DBL_MAX          \
    , long double         : LDBL_MAX         \
  )

#define TYPE_MIN(variable) _Generic          \
  (                                          \
    (variable)                               \
    , bool                : 0                \
    , char                : CHAR_MIN         \
    , unsigned char       : 0                \
    , signed   char       : SCHAR_MIN        \
    , unsigned short      : 0                \
    , signed   short      : SHRT_MIN         \
    , unsigned int        : 0                \
    , signed   int        : INT_MIN          \
    , unsigned long       : 0                \
    , signed   long       : LONG_MIN         \
    , unsigned long long  : 0                \
    , signed   long long  : LLONG_MIN        \
    TYPE_UINTMAX_MIN                         \
                                             \
    , float               : -FLT_MAX         \
    , double              : -DBL_MAX         \
    , long double         : -LDBL_MAX        \
  )

Let's say off_t is defined with typedef int64_t off_t and int64_t is defined with typedef long long int64_t, then the C compiler will treat off_t foo; TYPE_MAX(foo) the same as long long foo; TYPE_MAX(foo) and will chose the option with the value LLONG_MAX, giving you the maximum.

If a system has another native type not listed in this header file, make a preprocessor variable that is empty on other systems but contains the values for that native type when you compile on a system with that type. And then add this preprocessor variable to the list, similar to how it was done here with uintmax_t.

旧城空念 2024-10-16 00:46:52

我使用以下模式来解决问题(假设没有填充位):

((((type) 1 << (number_of_bits_in_type - 2)) - 1) << 1) + 1

number_of_bits_in_type 派生为 CHAR_BIT * sizeof (type),如其他答案中所示。

我们基本上将 1 位“推”到位,同时避免符号位。

您可以看到这是如何工作的。假设宽度为16位。然后我们将 1 左移 16 - 2 = 14,生成位模式 0100000000000000。我们小心翼翼地避免将 1 移入符号位。接下来,我们从中减去 1,得到 0011111111111111。看看这是怎么回事?我们将其左移 1,得到 0111111111111110,再次避免符号位。最后加1,得到0111111111111111,这是最高的有符号16位值。

如果你在博物馆工作,那里有这样的东西,那么这在补码和符号数值机器上应该可以很好地工作。如果你有填充位,它就不起作用。为此,您可能所能做的就是#ifdef,或者切换到编译器和预处理器之外的替代配置机制。

因此,对于 off_t

// probably bad idea to use these symbol names

#define OFF_T_BITS (sizeof (off_t) * CHAR_BIT)
#define OFF_T_MAX (((((off_t) 1 << (OFF_T_BITS - 2)) - 1) << 1) + 1)

I have used the following pattern to solve the problem (assuming there are no padding bits):

((((type) 1 << (number_of_bits_in_type - 2)) - 1) << 1) + 1

The number_of_bits_in_type is derived as CHAR_BIT * sizeof (type) as in the other answers.

We basically "nudge" the 1 bits into place, while avoiding the sign bit.

You can see how this works. Suppose that the width is 16 bits. Then we take 1 and shift it left by 16 - 2 = 14, producing the bit pattern 0100000000000000. We carefully avoided shifting a 1 into the sign bit. Next, we subtract 1 from this, obtaining 0011111111111111. See where this is going? We shift this left by 1 obtaining 0111111111111110, again avoiding the sign bit. Finally we add 1, obtaining 0111111111111111, which is the highest signed 16 bit value.

This should work fine on one's complement and sign-magnitude machines, if you work in a museum where they have such things. It doesn't work if you have padding bits. For that, probably all you can do is #ifdef, or switch to alternative configuration mechanisms outside of the compiler and preprocessor.

So, for off_t:

// probably bad idea to use these symbol names

#define OFF_T_BITS (sizeof (off_t) * CHAR_BIT)
#define OFF_T_MAX (((((off_t) 1 << (OFF_T_BITS - 2)) - 1) << 1) + 1)
戒ㄋ 2024-10-16 00:46:52

您可能想查看 limit.h (在 C99 中添加),该标头提供了应设置为匹配编译器范围的宏。 (它与编译器附带的标准库一起提供,或者第三方标准库替换负责使其正确)

You probably want to look at limits.h (added in C99) this header provides macros that should be set to match the compiler's ranges. (either it is provided along with the Standard library that came with the compiler, or a third party standard library replacement is responsible for getting it right)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文