C中的整数除法与无符号短
Integer division in C with unsigned short
我觉得自己是最血腥的初学者 - 为什么以下内容不起作用:
// declarations
unsigned short currentAddr= 0x0000;
unsigned short addr[20] = {1, 0};
// main
addr[1] = (~currentAddr)/2+1;
printf("addr[1] wert: %hu\n", addr[1]); // equals 1, expecte 0x8000
addr[1] = ~currentAddr>>1;
printf("addr[1] wert: %hu\n", addr[1]); // equals 65535, expected 0x7FFF
在 printf 和我的调试器的监视列表中,addr[1] 的值与预期不符。我的目标是获得变量最大值的一半,这里是 0x8000。
信息:我正在做 ~currentAddr 以获得最大值。 0xFFFF 在我的嵌入式平台上的长度与我的 PC 上的长度不同。
干杯,斯特凡
因为数字2是int,int可以容纳unsigned short,所以,实际操作是addr[1] = (unsigned short)(((int)(~currentAddr)/2)+1)
出了什么问题
对一元操作数执行整数提升 ~
。
在许多系统上 int
大于 short
。在这样的系统上,对于 unsigned short currentAddr = 0
,currentAddr
的值首先在表达式 ~currentAddr
中提升为 int
。然后 ~currentAddr
计算为 -1
(假设二进制补码表示)。
在某些系统上,int
和 short
可能大小相同(尽管 int
必须至少与 short
一样大);这里 currentAddr
将被提升为 unsigned int
,因为 int
不能容纳相同大小的 unsigned
整数类型的所有值。在这种情况下,~currentAddr
将计算为 UINT_MAX
。对于 16 位 int
(short
必须至少为 16 位,因此此处 int
和 short
的大小相同)~currentAddr
的结果将是 65,535。
OP 的系统必须 int
大于 short
。在 addr[1] = (~currentAddr)/2+1;
的情况下,这变成 addr[1] = (-1)/2+1;
计算结果为 1
.
在第二种情况下,addr[1] = ~currentAddr>>1;
的计算结果为 addr[1] = (-1)>>1;
。在这里,右移负值的结果是实现定义的。在本例中,结果似乎是 INT_MAX
,它在对 addr[1]
的赋值中被转换为 unsigned short
,在转换中采用值 USHRT_MAX
。这个值在 OP 的系统上是 65,535。
怎么办
要清楚可靠地获取标准整数类型的最大值和最小值,请使用 limits.h
中的宏,而不是尝试位操作。这个方法不会让人失望:
#include <stdio.h>
#include <limits.h>
int main(void)
{
unsigned short val;
val = (USHRT_MAX / 2) + 1;
printf("(USHRT_MAX / 2) + 1: %#hx\n", val);
val = USHRT_MAX >> 1;
printf(" USHRT_MAX >> 1: %#hx\n", val);
return 0;
}
程序输出:
(USHRT_MAX / 2) + 1: 0x8000
USHRT_MAX >> 1: 0x7fff
问题出在这里:
addr[1] = (~currentAddr)/2+1;
您期望 currentAddr
到 0xFFFF
,这是部分正确的。但是,您可能错过了整数提升规则,这使得 0xFFFFFFFF
是 -1
.
的十六进制表示
现在,有一个简单的数学:
(~currentAddr)/2+1
只不过是 0x01
或 1
,当你 ~currentAddr>>1;
做这个转变时,它又变成了 -1
。
来自
My aim is to have half the maximum of the variable, here 0x8000
如果我没理解错的话,你要做的是获取等于(无符号短整数的最大值)/2 的值。如果是这样,正确的做法是使用 USHRT_MAX
。当然,您需要在源代码中包含 limits.h
文件。
更新:
参考您对 David 的回答的评论,以下更改按预期工作。 (你考过,我没考过)
unsigned short c;
c = ~currentAddr;
unsigned short c_z = sizeof (c);
unsigned short ci;
ci = (c >> 1) + 1;
unsigned short ci_z = sizeof (ci);
addr[1] = ci;
现在,为什么不将其提升为整数而不是之前的情况
c = ~currentAddr;
它被提升了,但它产生了预期的结果,因为正如 chux 所解释的(我无法做到)它在其操作期间(暂时)被提升为 int
,但解析为(转换to) a unsigned short
当它存储在分配给 c
.
的内存中时
C标准回答问题:
来自C99标准:6.5.16.1简单赋值
In simple assignment (=), the value of the right operand is converted to the type of the assignment expression and replaces the value stored in the object designated by the left operand.
In your case since both the LHS and RHS are of the same type, there is no need for any conversion.
另外,它说:
The type of an assignment expression is the type the left operand would have after lvalue conversion.
同样是由C11 6.5.16.1/2指定的:
In simple assignment (=), the value of the right operand is converted to the type of the assignment expression and replaces the value stored in the object designated by the left operand.
自己试试:
int main(void)
{
unsigned short c;
unsigned short currentAddr= 0x0000;
c = ~currentAddr;
printf("\n0x%x", c);
printf("\n0x%x", (~currentAddr));
return 0;
}
这应该打印:
0xffff
0xffffffff
addr[1] = (~currentAddr)/2+1;
我们来分解一下:currentAddr
是一个参与计算的unsigned short
所以value/type首先提升为int
或unsigned
。在 C 中,这是 整数提升。
If an int
can represent all values of the original type ..., the value is converted to an int; otherwise, it is converted to an unsigned int
. These are called the integer promotions. All other types are unchanged by the integer promotions. C11dr §6.3.1.1 2
当USHRT_MAX <= INT_MAX
,(如16位短int/unsigned,32位int/unsigned),代码如下。使用 currentAddr == 0
和典型的 2 的补码行为,~0 --> -1 和 addr[1]
--> 1.
int tmp = currentAddr;
addr[1] = (~tmp)/2+1;
当USHRT_MAX > INT_MAX
时(如16位短int/unsigned、16位int/unsigned),代码如下。对于 currentAddr == 0
和无符号行为,~0 --> 0xFFFF 和 addr[1]
--> 0x8000.
unsigned tmp = currentAddr;
addr[1] = (~tmp)/2+1;
My aim is to have half the maximum of the variable
获得unsigned short
最大值的最佳方法是使用SHRT_MAX
并跳过~
代码。无论 unsigned short, int, unsigned
范围如何,它都会按预期工作。它还能更好地记录代码意图。
#include <limits.h>
addr[1] = USHRT_MAX/2+1;
我觉得自己是最血腥的初学者 - 为什么以下内容不起作用:
// declarations
unsigned short currentAddr= 0x0000;
unsigned short addr[20] = {1, 0};
// main
addr[1] = (~currentAddr)/2+1;
printf("addr[1] wert: %hu\n", addr[1]); // equals 1, expecte 0x8000
addr[1] = ~currentAddr>>1;
printf("addr[1] wert: %hu\n", addr[1]); // equals 65535, expected 0x7FFF
在 printf 和我的调试器的监视列表中,addr[1] 的值与预期不符。我的目标是获得变量最大值的一半,这里是 0x8000。 信息:我正在做 ~currentAddr 以获得最大值。 0xFFFF 在我的嵌入式平台上的长度与我的 PC 上的长度不同。
干杯,斯特凡
因为数字2是int,int可以容纳unsigned short,所以,实际操作是addr[1] = (unsigned short)(((int)(~currentAddr)/2)+1)
出了什么问题
对一元操作数执行整数提升 ~
。
在许多系统上 int
大于 short
。在这样的系统上,对于 unsigned short currentAddr = 0
,currentAddr
的值首先在表达式 ~currentAddr
中提升为 int
。然后 ~currentAddr
计算为 -1
(假设二进制补码表示)。
在某些系统上,int
和 short
可能大小相同(尽管 int
必须至少与 short
一样大);这里 currentAddr
将被提升为 unsigned int
,因为 int
不能容纳相同大小的 unsigned
整数类型的所有值。在这种情况下,~currentAddr
将计算为 UINT_MAX
。对于 16 位 int
(short
必须至少为 16 位,因此此处 int
和 short
的大小相同)~currentAddr
的结果将是 65,535。
OP 的系统必须 int
大于 short
。在 addr[1] = (~currentAddr)/2+1;
的情况下,这变成 addr[1] = (-1)/2+1;
计算结果为 1
.
在第二种情况下,addr[1] = ~currentAddr>>1;
的计算结果为 addr[1] = (-1)>>1;
。在这里,右移负值的结果是实现定义的。在本例中,结果似乎是 INT_MAX
,它在对 addr[1]
的赋值中被转换为 unsigned short
,在转换中采用值 USHRT_MAX
。这个值在 OP 的系统上是 65,535。
怎么办
要清楚可靠地获取标准整数类型的最大值和最小值,请使用 limits.h
中的宏,而不是尝试位操作。这个方法不会让人失望:
#include <stdio.h>
#include <limits.h>
int main(void)
{
unsigned short val;
val = (USHRT_MAX / 2) + 1;
printf("(USHRT_MAX / 2) + 1: %#hx\n", val);
val = USHRT_MAX >> 1;
printf(" USHRT_MAX >> 1: %#hx\n", val);
return 0;
}
程序输出:
(USHRT_MAX / 2) + 1: 0x8000
USHRT_MAX >> 1: 0x7fff
问题出在这里:
addr[1] = (~currentAddr)/2+1;
您期望 currentAddr
到 0xFFFF
,这是部分正确的。但是,您可能错过了整数提升规则,这使得 0xFFFFFFFF
是 -1
.
现在,有一个简单的数学:
(~currentAddr)/2+1
只不过是 0x01
或 1
,当你 ~currentAddr>>1;
做这个转变时,它又变成了 -1
。
来自
My aim is to have half the maximum of the variable, here
0x8000
如果我没理解错的话,你要做的是获取等于(无符号短整数的最大值)/2 的值。如果是这样,正确的做法是使用 USHRT_MAX
。当然,您需要在源代码中包含 limits.h
文件。
更新:
参考您对 David 的回答的评论,以下更改按预期工作。 (你考过,我没考过)
unsigned short c;
c = ~currentAddr;
unsigned short c_z = sizeof (c);
unsigned short ci;
ci = (c >> 1) + 1;
unsigned short ci_z = sizeof (ci);
addr[1] = ci;
现在,为什么不将其提升为整数而不是之前的情况
c = ~currentAddr;
它被提升了,但它产生了预期的结果,因为正如 chux 所解释的(我无法做到)它在其操作期间(暂时)被提升为 int
,但解析为(转换to) a unsigned short
当它存储在分配给 c
.
C标准回答问题:
来自C99标准:6.5.16.1简单赋值
In simple assignment (=), the value of the right operand is converted to the type of the assignment expression and replaces the value stored in the object designated by the left operand. In your case since both the LHS and RHS are of the same type, there is no need for any conversion.
另外,它说:
The type of an assignment expression is the type the left operand would have after lvalue conversion.
同样是由C11 6.5.16.1/2指定的:
In simple assignment (=), the value of the right operand is converted to the type of the assignment expression and replaces the value stored in the object designated by the left operand.
自己试试:
int main(void)
{
unsigned short c;
unsigned short currentAddr= 0x0000;
c = ~currentAddr;
printf("\n0x%x", c);
printf("\n0x%x", (~currentAddr));
return 0;
}
这应该打印:
0xffff
0xffffffff
addr[1] = (~currentAddr)/2+1;
我们来分解一下:currentAddr
是一个参与计算的unsigned short
所以value/type首先提升为int
或unsigned
。在 C 中,这是 整数提升。
If an
int
can represent all values of the original type ..., the value is converted to an int; otherwise, it is converted to anunsigned int
. These are called the integer promotions. All other types are unchanged by the integer promotions. C11dr §6.3.1.1 2
当USHRT_MAX <= INT_MAX
,(如16位短int/unsigned,32位int/unsigned),代码如下。使用 currentAddr == 0
和典型的 2 的补码行为,~0 --> -1 和 addr[1]
--> 1.
int tmp = currentAddr;
addr[1] = (~tmp)/2+1;
当USHRT_MAX > INT_MAX
时(如16位短int/unsigned、16位int/unsigned),代码如下。对于 currentAddr == 0
和无符号行为,~0 --> 0xFFFF 和 addr[1]
--> 0x8000.
unsigned tmp = currentAddr;
addr[1] = (~tmp)/2+1;
My aim is to have half the maximum of the variable
获得unsigned short
最大值的最佳方法是使用SHRT_MAX
并跳过~
代码。无论 unsigned short, int, unsigned
范围如何,它都会按预期工作。它还能更好地记录代码意图。
#include <limits.h>
addr[1] = USHRT_MAX/2+1;