对C中积分算术的理解

问题描述 投票:2回答:2

编辑:修改void *为uint8_t *值。问题仍然存在。编辑:问题是一个简单的变量溢出,与整数促销无关。

我解决了这段简化代码中的错误。类型与原始源代码中的类型相同。

unsigned int entrySize;    // entrySize is 288
int startIndex, endIndex;  // both are 24536838
uint8_t *pStartAddr;          // valid initialized pointer (0x34f1e40)

/*Mystery begins...*/
uint8_t *curr_addr = pStartAddr + entrySize * startIndex;
while (curr_addr <= startAddr + entrySize * endIndex)
{
    externFunc(curr_addr);
    curr_addr+=entrySize;
}

快速浏览一下,这段代码看起来很明显,不包括奇怪的类型选择。

然而,在我们的一次崩溃中,似乎curr_addr获得了无效的指针。我的预感是entrySize * startIndex存在问题,因为它们的乘法设置在第32位,并且将startIndexendIndex作为带符号类型可能会混淆编译器使用所需的值。

在更改了unsigned long类型后,问题就解决了。但我无法弄清楚到底出了什么问题。

我正在开发64位机器,x86_64 CPU,gcc(GCC)4.8.5 20150623和linux red hat distribution(版本4.8.5-28)

我假设当上面的计算设置在entrySize * startIndex的第32位时问题开始发生。但是,当我使用第一个打开第32位的startIndex值时,它仍然有效。它也显示出来了

我的问题是:

  • int * unsigned int被认为是签名或未签名的价值结果?结果类型的等级是多少?乘法可能会溢出到8字节类型,我认为编译器可以防止丢失精度,对吧?
  • startAddr是一个void*。然后将其添加到第一个问题中计算的任何值和类型。 void*被认为是signed还是unsigned?我的预感当然是一个unsigned value,但我不能支持它。
  • 在startAddr + << result >>中进行整数促销。
  • 我们的声明永远不会停止(实际时间)吗?如果不等式的右边是带符号的数字(宽度至少为8个字节),左侧(curr_addr)是否也会被提升为有符号数,导致无限循环?
  • 一步一步解释将是最受欢迎的:)

我读了那些链接中包含的内容,但仍然无能为力:

  1. https://www.oreilly.com/library/view/c-in-a/0596006977/ch04.html
  2. Integer conversion rank of signed and unsigned int
c types integer-overflow
2个回答
2
投票

乘法可能会溢出到8字节类型,我认为编译器可以防止丢失精度,对吧?

这是一个非常不正确的假设。 Per the 3.4.3 description of undefined behavior from the C standard(bolding mine):

3.4.3

1使用不可移植或错误的程序结构或错误数据时的未定义行为行为,本国际标准不对此要求

2注意可能的未定义行为包括完全忽略不可预测的结果,在转换或程序执行期间以环境特征(有或没有发出诊断消息)的方式执行,终止翻译或执行(带有发出诊断信息)。

3示例未定义行为的示例是整数溢出的行为。

整数溢出是C标准本身使用的未定义行为的一个例子。

288 * 24536838等于7066609344,它远远超出了32位int的能力,无论是signed还是unsigned,从而引发了未定义的行为。

所以不,编译器不会“防止丢失精度”。事实恰恰相反。


5
投票
  1. 指针运算在void*上的行为是未定义的。
  2. 指针算法仅在数组中有效。请注意,您可以将指针设置为超出数组最后一个元素的指针,但不要尝试取消引用它。此规则也适用于可以将规则视为单个元素数组的对象。

(1)在你的代码中肯定没有遵守(你的编译器没有警告你 - 如果不是,请把它装箱),(2)可能不是。那时(我本人)有点讽刺,你的具体问题是不相关的。

© www.soinside.com 2019 - 2024. All rights reserved.