为什么我的程序对我来说似乎是有效的?
有两种可能性:
1. 你测试了所有输入和条件,程序对于这些都能正常工作。但是,你没有测试其它输入和/或条件,导致程序在这些情况下无法运行。特殊情况下,程序可能会依赖于未定义、实现定义或未指定的行为(确实如此),即使在测试环境中以期望的方式工作,它也本质上是错误的。
2. 你关于程序的正确工作存在误解,很可能是由于对所需输出的误解造成的。
未定义/实现定义的行为:
首先,像@chux首先观察到的那样,在具有32位(或更小)int的系统上评估表达式1 << 31会产生未定义行为,例如Windows提供的编译器和Visual Studio的C编译器。两个操作数都是int类型,因此结果是int类型,但算术上正确的结果超出了该类型可以表示的值的范围。对于无符号整数结果,该情况下的行为将被定义,但对于int等带符号整数类型明确地未定义。由于您将结果分配给unsigned int类型的变量,因此您只需要将表达式更改为1u << 31即可解决该问题。
此外,任何类型的表示中的位数未指定,但是您的代码假设为32位unsigned int。这确实是Visual Studio C编译器提供的unsigned ints的大小,但您不需要依赖于它。通过计算unsigned int的表示中的位数作为CHAR_BIT * sizeof(unsigned int),您将在每个环境中获得正确的实现相关结果。
只要我们谈论实现依赖性,就不一定所有对象表示中的所有位都对其值有贡献。还可以有填充位,并且在具有少于32个值位的类型unsigned int的表示的实现上,表达式1u << 31或其等效物将评估为零。为了完全正确,unsigned int表示中value bit的数量的计算必须基于UINT_MAX的值。一个绕过此问题的可行掩码的替代表达式将是~(UINT_MAX >> 1)。
输出格式:
关于输出格式,不清楚“int”的“二进制形式”是什么,特别是考虑到你想提供负数和正数值。如果你要提供负数值的形式而不使用“-”符号,就像你的代码尝试做的那样,那么要么必须指定所需输出形式的细节或假设(例如big-endian,32位二补数),要么你打算探测输入值的机器特定表示。由于你没有指定特定的格式,如果问题的一部分在于输出格式,则我只能得出结论:要求使用机器特定表示或符号/幅度。
机器表示
如果目标是探测
int
值的机器表示,则您的程序至少有两个错误。
首先,评估表达式
n&i
涉及将
i
的值从类型
int
转换为类型
unsigned int
。因此,您打印的是
转换值的表示形式,这不能保证与原始
int
值的表示形式相同。实际上,这种情况不太可能发生在具有实际差异的机器和C实现中。当然,在Windows上的Visual Studio不属于这种环境。
此外,您的代码输出一个逻辑值的表示,这不一定符合物理表示。即使假设您没有遇到转换或各种对象表示的大小等问题,您的代码也假定物理布局是从最高位到最低位的字节。也就是说,它打印了一个big-endian表示,而不管实际的物理表示如何。在x86和x86_64上,
int
的本机物理表示是小端字节序,我下面的代码用于打印机器表示将打印与您的代码不同的结果。
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');
}
该方法没有任何对于类型为int
的值表示不做假设的情况,这些值并未受到C标准的支持。特别需要注意的是当n
等于INT_MIN
时需要进行特殊处理,虽然这样做比较麻烦,但必须这样做是因为对于表达式-INT_MIN
的求值可能会(在x86上)产生未定义行为。
sizeof(int) * CHAR_BIT
吗? - William Pursell