在C中,我有三个内存区域,它们有几百个字节长。我想取两个内存区域的第i
对32位,将它们作为两个无符号32位整数相加,并将结果存储在第三个内存区域的相应64位中(因为两个32位加法器可以产生33位结果),而不是在加法发生之前将它们存储在实际的uint32t
中,而是:直接从内存中阅读它们,告诉编译器将它们视为uint32t'
s,并告诉编译器将它们相加并将结果存储在第三个内存区域的前64位。n1->bits
、n2->bits
和R->bits
分别是指向我的三个内存区域的指针。最初它们的类型是uint8_t*
我已经确定这三个内存区域的大小可以被32整除。
注意:carry
是一个实际的uint32_t
类型的变量,在回答时不需要注意。
问题:由于某种原因,编译器确实将它们作为两个无符号的32位整数读取,但是拒绝将它们的结果存储在Result内存区域的前64位,而是溢出。我是这样做的:
*((uint64_t*)( ((uint32_t*)(R->bits)) + i)) =
*( ((uint32_t*)(n1->bits)) + i)
+
*( ((uint32_t*)(n2->bits)) + i)
+
carry;
;
字符串
以下是我目前对这段代码应该如何工作的理解。请纠正我的错误:
1.使用R->bits
获取内存区域的第一个地址
2.将此指针转换为(uint32t*)
,以便当我们执行指针算术+i
时,编译器以32位为单位递增(因此,使我们获得第i个无符号32位整数)。如果没有这种转换,指针算术+i
将被编译器转换为+ (i * 8) bits
而不是+ (i * 32) bits
,因为R->bits
最初是uint8_t*
。
*
3.* 现在我们已经告诉编译器将R->位视为指向uint32t
的指针,执行实际的指针算术+i
,以获得大内存区域中的第i个32位整数。
4a.在两个ADD操作数的情况下,通过强制转换和指针运算对指针进行解引用,以读取假定的无符号32位整数的实际值。
- (这一步4 b是我认为我的理解是错误的地方)*
4 b.在Result缓冲区的情况下,还不要解引用。首先,将指向第i个32位区域的指针转换为uint64_t*
,然后解引用它并将其用作内置加法的结果,以便告诉编译器将此内置加法的结果存储在第i个32位区域的前64位,因为同样,两个32位操作数的ADD可以产生33位结果。
但是,它没有这样做。
我试着用两个操作数内存区域填充数百个1,它所做的是,它把前32个1加在一起,这应该产生:
**00000001 * 11111111 111111111 111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
由于我的机器是little-endian,因此结果的内存布局应该是:
11111110 11111111 1111111 11111111 000000001
除非,当我查看结果缓冲区的内存时,它在第5个字节中缺少额外的第33个1。第五个字节全是零。这意味着当我告诉它通过转换到uint64_t*
将结果缓冲区中的内存位置视为64位区域时,它拒绝听我的话。
有人能解释一下为什么吗?考虑到我的代码和我目前对它应该如何工作的理解?
3条答案
按热度按时间h6my8fg21#
您的主要问题与指针算术无关(尽管可能涉及未定义的行为),而是与使用的类型有关。
你上面的内容相当于:
字符串
将两个
uint32_t
类型的值相加,结果为uint32_t
类型。因此,如果结果溢出,它将简单地环绕,即。它将“修剪”除低32位以外的所有位。您需要将其中一个参数强制转换为类型
uint64_t
,以便使用该类型完成加法,即:型
回到你的代码:
型
此外,如果所讨论的数组的类型为
uint8_t
,并且存储在R->bits
中的64位值可能彼此重叠,则存在严格的别名冲突。你想要的完全兼容的版本看起来像这样:
型
scyqe7ek2#
您的代码调用未定义的行为。指针双关违反了严格的别名规则。您需要使用
memcpy
或char
访问。字符串
或者是
型
zlhcx6iw3#
我发现错误在哪里:在解引用两个ADD操作数的两个内存位置以获得实际的无符号整数值之后,另外将它们转换为(uint64_t)。同时将
carry
的类型也更改为uint64_t。现在,整个加法只使用uint64_t完成,并按预期工作。