我想打印int
的二进制表示。我的解决方案似乎适用于Visual Studio中的int
和unsigned int
,但有人告诉我这是错误的。有人看到错误吗?如果是这样,为什么我的程序似乎对我有用?
void printbin(int n)
{
unsigned int i = 1<<31;
for (int j=0; j<32; j++)
{
if ((n & i) != 0)
printf("1");
else
printf("0");
i = i>>1;
}
printf("\n");
}
答案 0 :(得分:2)
为什么我的程序似乎对我有用?
有两种非排他性的可能性:
从未定义的行为开始:首先观察@chux,评估表达式1<<31
会在具有32位(或更小)int
的系统上产生未定义的行为,例如Windows和Visual Studio的C编译器。两个操作数都是int
类型,因此结果是int
类型,但是算术上正确的结果超出了可以由该类型表示的值范围。在这种情况下的行为将为 unsigned 整数结果定义,但对于有符号整数类型(例如int
)显式未定义。由于您将结果分配给unsigned int
类型的变量,因此只需将表达式更改为1u<<31
即可解决该问题。
此外,未指定任何类型表示中的位数,但您的代码假定为32位unsigned int
。这确实是Visual Studio C编译器提供的unsigned int
的大小,但您不需要依赖它。通过将unsigned int
的表示中的位数计算为CHAR_BIT * sizeof(unsigned int)
,您将获得针对每个环境的正确的依赖于实现的结果。
然而,只要我们讨论实现依赖性,就不一定是对象表示中的所有位都有助于其值的情况。也可以有填充位,并且在类型unsigned int
的表示中具有少于32个值位的实现上,表达式1u << 31
或等效值计算为零。为完全正确,unsigned int
表示中值位数的计算必须基于UINT_MAX
的值。您创建的用于回避此问题的位掩码的替代表达式为~(UINT_MAX >> 1)
。
至于输出格式,目前还不清楚&#34;&#34;&#34; int
的二进制形式,特别是考虑到你想要提供负值和正值。如果您应该在不使用-
符号的情况下提供负值的表单,就像您的代码尝试那样,那么必须指定或假设所需输出表单的详细信息(例如big-endian,32-第二位的补充),否则你打算探测输入值的机器特定表示。由于您没有指定特定格式,如果(部分)问题出现在输出格式中,那么我只能得出结论是需要特定于机器的表示或符号/幅度。
如果目标是探测int
值的机器表示,那么您的程序在至少两个(额外)计数上是不正确的。
首先,评估表达式n&i
涉及将i
的值从类型int
转换为类型unsigned int
。因此,您打印的是转换的值的表示,该值不保证与原始int
值的表示相同。但实际上,你不太可能遇到存在实际差异的机器和C实现。当然,Windows上的Visual Studio不是这样的环境。
此外,您的代码输出的值的逻辑表示不一定符合物理表示。即使假设您没有遇到各种对象表示的转换或大小等问题,您的代码也假定物理布局是从最重要的字节到最不重要的字节。也就是说,它打印一个大端表示,而不管实际的物理表示。在x86和x86_64上,int
的本机物理表示是 little -endian,下面打印机器表示的代码将打印出与您的代码不同的结果。
void printbin(int n)
{
unsigned char *p = (unsigned char *) &n;
for (int j=0; j < sizeof(n); j++)
{
for (unsigned char mask = 1u << (CHAR_BIT - 1); mask; mask >>= 1) {
putchar((*p & mask) ? '1' : '0');
}
p += 1;
}
putchar('\n');
}
该标准允许不同指针类型之间的转换,并且它特别认为该程序中的转换将导致p
被初始化为指向n
表示中的第一个字节。程序逐步执行表示中的每个字节(通过sizeof
运算符确定的总数)并打印每个字节中的位,从最重要到最不重要,与您的版本类似。如果有填充位,则包括它们。
另一方面,如果您想要一个带符号的二进制数字字符串,从最重要的非零位到最低有效位,那么您可以这样做:
void printbin_digits(unsigned int n) {
char bits[CHAR_BIT * sizeof(unsigned int)] = {0};
int bit_count = 0;
while (n) {
bits[bit_count++] = n % 2;
n >>= 1;
}
while (bit_count) {
putchar(bits[--bit_count] ? '1' : 0);
}
}
void printbin(int n)
{
if (n == 0) {
putchar('0');
} else if (n == INT_MIN) {
putchar('-');
printbin_digits(-(n / 2));
putchar((n % 2) ? '1' : '0');
} else if (n < 0) {
putchar('-');
printbin_digits(-n);
} else {
printbin_digits(n);
}
putchar('\n');
}
对于没有C标准支持的类型int
的值的表示没有任何假设。请特别注意当n
具有值INT_MIN
时的特殊处理 - 它很麻烦,但它是必要的,因为评估表达式-INT_MIN
可以(并且在x86上)确实产生了未定义的行为。
答案 1 :(得分:2)
1<<31
移位一位通过值位并可能移位到符号(或填充)位。这是C中未定义的行为。
n & i
正在尝试&#34;和&#34; unsigned int
的位和signed int
的符号。
OP使用32假设int
为32位宽。
以下是打印符号和可变位数的示例 - 工作[INT_MIN...INT_MAX]
。
#include <limits.h>
void printbin_c(int n) {
char buf[CHAR_BIT * sizeof n + 1];
char *p = &buf[sizeof buf - 1];
*p = '\0';
int i = n;
if (i > 0) {
i = -i;
}
do {
p--;
*p = '0' - i%2;
i /= 2;
} while (i);
if (n < 0) {
p--;
*p = '-';
}
puts(p);
}
[编辑]应对1&#39补; @John Bollinger
使用带有if (i > 0) i = -i;
的负绝对值作为正绝对值与INT_MIN
2补码不兼容。