理解 C 中的积分运算

Understanding of integral arithmetics in C

编辑:将 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 作为签名类型可能会使编译器混淆所需的值使用。

改成unsinged long类型后,问题解决。 但我无法弄清楚究竟哪里错了。

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

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

我的问题是:

我阅读了那些链接中包含的内容,但仍然一无所知:

  1. https://www.oreilly.com/library/view/c-in-a/0596006977/ch04.html
  2. Integer conversion rank of signed and unsigned int
  1. void* 上的指针运算行为未定义。

  2. 指针算法只在数组中有效。请注意,您可以 设置 指向数组最后一个元素之后的指针,但不要尝试取消引用它。此规则也适用于可被视为单元素数组的对象。

(1) 在你的代码中肯定没有被遵守(你的编译器没有警告你 - 如果没有,将它装箱),(2)可能不是。有点滑稽(就我而言),你的具体问题不相关。

multiplication can probably overflow to 8 bytes types and I assume the compiler prevents losing precision, right?

这是一个严重错误的假设。每 the 3.4.3 description of undefined behavior from the C standard(加粗我的):

3.4.3

1 undefined behavior behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements

2 NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

3 EXAMPLE An example of undefined behavior is the behavior on integer overflow.

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

288 * 24536838等于7066609344,这远远超出了32位int的容量,无论是signed还是unsigned,因此调用未定义的行为。

所以不,编译器不会"prevent losing precision"。事实上恰恰相反。