C89中直接计算确定char的范围(不要使用limits.h)
Determine the ranges of char by direct computation in C89 (do not use limits.h)
我正在尝试解决 K&R 的 C 书的 Ex 2-1。该练习要求通过直接计算(而不是直接从 limits.h
打印值)来确定 char 的范围。关于如何很好地完成这项工作有什么想法吗?
聪明的方法,只需计算变量的 sizeof()
,您就会知道它比 sizeof()=1
的变量大很多倍,通常是 char
。鉴于您可以使用数学来计算范围。如果你有奇数大小的类型,例如 3 位 char
s 或其他东西,则不起作用。
努力尝试,将 0 放入类型中,然后递增直到它不再递增(环绕或保持不变,具体取决于机器)。无论之前的数字是多少,那都是最大值。对 min.
做同样的事情
您可以随时尝试 ol' 待机,printf...
为了简单起见,让我们把事情简化一下。
这不是您问题的完整答案,但它会检查一个字符是否为 8 位——需要一点帮助(是的,代码中有错误)。我会留给你自己想办法。
#include <stdio.h>
#DEFINE MMAX_8_BIT_SIGNED_CHAR 127
main ()
{
char c;
c = MAX_8_BIT_SIGNED_CHAR;
printf("%d\n", c);
c++;
printf("%d\n", c);
}
查看输出。我不会给你剩下的答案,因为我认为如果你自己弄清楚你会从中得到更多,但我会说你可能想看看位移运算符。
假设类型将智能换行1,您可以简单地从将 char 变量设置为零开始。
然后增加它直到新值比以前的值少。
新值是最小值,以前的值是最大值。
下面的代码应该是一个好的开始:
#include<stdio.h>
int main (void) {
char prev = 0, c = 0;
while (c >= prev) {
prev = c;
c++;
}
printf ("Minimum is %d\n", c);
printf ("Maximum is %d\n", prev);
return 0;
}
1 从技术上讲,溢出变量是未定义的行为,任何事情都可能发生,但绝大多数实现都可以。请记住,保证 正常工作。
事实上,以可移植的方式解决这个问题(有些实现对 char
有各种不同的位宽,有些甚至对负数使用不同的编码方案)可能是 正是为什么这些有用的宏首先被放入limits.h
。
x86
& x86_64
上有3个相对简单的函数可以涵盖有符号和无符号类型:
/* signed data type low storage limit */
long long limit_s_low (unsigned char bytes)
{ return -(1ULL << (bytes * CHAR_BIT - 1)); }
/* signed data type high storage limit */
long long limit_s_high (unsigned char bytes)
{ return (1ULL << (bytes * CHAR_BIT - 1)) - 1; }
/* unsigned data type high storage limit */
unsigned long long limit_u_high (unsigned char bytes)
{
if (bytes < sizeof (long long))
return (1ULL << (bytes * CHAR_BIT)) - 1;
else
return ~1ULL - 1;
}
其中 CHAR_BIT
通常为 8。
好的,我把我的版本扔进戒指:
unsigned char uchar_max = (unsigned char)~0;
// min is 0, of course
signed char schar_min = (signed char)(uchar_max & ~(uchar_max >> 1));
signed char schar_max = (signed char)(0 - (schar_min + 1));
它确实假定有符号的 2 的补码和有符号和无符号字符的相同大小。虽然我只是定义了前者,但我确信可以从标准中推断出后者,因为两者都是 char 并且必须保存 "execution charset" 的所有编码(这对像 UTF-8 这样的 RL 编码字符集意味着什么).
直接得到 1 的补码,sing/magnitude-version。请注意,未签名版本始终相同。
一个优点是它完全以 char
类型运行并且没有循环等。因此它在 8 位架构上仍然是高性能的。
嗯...我真的认为这需要一个循环来签名。我错过了什么?
我正在尝试解决 K&R 的 C 书的 Ex 2-1。该练习要求通过直接计算(而不是直接从 limits.h
打印值)来确定 char 的范围。关于如何很好地完成这项工作有什么想法吗?
聪明的方法,只需计算变量的
sizeof()
,您就会知道它比sizeof()=1
的变量大很多倍,通常是char
。鉴于您可以使用数学来计算范围。如果你有奇数大小的类型,例如 3 位char
s 或其他东西,则不起作用。努力尝试,将 0 放入类型中,然后递增直到它不再递增(环绕或保持不变,具体取决于机器)。无论之前的数字是多少,那都是最大值。对 min.
做同样的事情
您可以随时尝试 ol' 待机,printf...
为了简单起见,让我们把事情简化一下。
这不是您问题的完整答案,但它会检查一个字符是否为 8 位——需要一点帮助(是的,代码中有错误)。我会留给你自己想办法。
#include <stdio.h>
#DEFINE MMAX_8_BIT_SIGNED_CHAR 127
main ()
{
char c;
c = MAX_8_BIT_SIGNED_CHAR;
printf("%d\n", c);
c++;
printf("%d\n", c);
}
查看输出。我不会给你剩下的答案,因为我认为如果你自己弄清楚你会从中得到更多,但我会说你可能想看看位移运算符。
假设类型将智能换行1,您可以简单地从将 char 变量设置为零开始。
然后增加它直到新值比以前的值少。
新值是最小值,以前的值是最大值。
下面的代码应该是一个好的开始:
#include<stdio.h>
int main (void) {
char prev = 0, c = 0;
while (c >= prev) {
prev = c;
c++;
}
printf ("Minimum is %d\n", c);
printf ("Maximum is %d\n", prev);
return 0;
}
1 从技术上讲,溢出变量是未定义的行为,任何事情都可能发生,但绝大多数实现都可以。请记住,保证 正常工作。
事实上,以可移植的方式解决这个问题(有些实现对 char
有各种不同的位宽,有些甚至对负数使用不同的编码方案)可能是 正是为什么这些有用的宏首先被放入limits.h
。
x86
& x86_64
上有3个相对简单的函数可以涵盖有符号和无符号类型:
/* signed data type low storage limit */
long long limit_s_low (unsigned char bytes)
{ return -(1ULL << (bytes * CHAR_BIT - 1)); }
/* signed data type high storage limit */
long long limit_s_high (unsigned char bytes)
{ return (1ULL << (bytes * CHAR_BIT - 1)) - 1; }
/* unsigned data type high storage limit */
unsigned long long limit_u_high (unsigned char bytes)
{
if (bytes < sizeof (long long))
return (1ULL << (bytes * CHAR_BIT)) - 1;
else
return ~1ULL - 1;
}
其中 CHAR_BIT
通常为 8。
好的,我把我的版本扔进戒指:
unsigned char uchar_max = (unsigned char)~0;
// min is 0, of course
signed char schar_min = (signed char)(uchar_max & ~(uchar_max >> 1));
signed char schar_max = (signed char)(0 - (schar_min + 1));
它确实假定有符号的 2 的补码和有符号和无符号字符的相同大小。虽然我只是定义了前者,但我确信可以从标准中推断出后者,因为两者都是 char 并且必须保存 "execution charset" 的所有编码(这对像 UTF-8 这样的 RL 编码字符集意味着什么).
直接得到 1 的补码,sing/magnitude-version。请注意,未签名版本始终相同。
一个优点是它完全以 char
类型运行并且没有循环等。因此它在 8 位架构上仍然是高性能的。
嗯...我真的认为这需要一个循环来签名。我错过了什么?