1

我收到了来自 Lint 的警告(http://www.gimpel.com/html/pub/msg.txt上的 740 ),大意是它警告我不要将指向联合的指针转换为指向无符号的指针长。我知道我正在转换不兼容的类型,所以我使用了 reinterpret_cast ,但我仍然收到令我惊讶的警告。

例子:

// bar.h
void writeDWordsToHwRegister(unsigned long* ptr, unsigned long size)
{
  // write double word by double word to HW registers 
  ...
};

// foo.cpp
#include "bar.h"

struct fooB
{
  ...
}

union A 
{
  unsigned long dword1;
  struct fooB; // Each translation unit has unique content in the union
  ...
}

foo()
{
  A a;
  a = ...; // Set value of a

  // Lint warning
  writeDWordsToHwRegister(reinterpret_cast<unsigned long*> (&a), sizeof(A));

  // My current triage, but a bad one since someone, like me, in a future refactoring 
  // might redefine union A to include a dword0 variable in the beginning and forget
  // to change below statement.      
  writeDWordsToHwRegister(reinterpret_cast<unsigned long*> (&(a.dword1)), sizeof(A)); 
}

撇开我这样做的确切原因以及如何以最佳方式解决它(接口中的 void* 和 writeDWordsToHwRegister 中的无符号 long* 转换?),阅读 Lint 警告解释说,在某些机器上,指向 char 的指针之间存在差异和指向单词的指针。有人可以解释这种差异是如何表现出来的,并可能在某些处理器上举例说明这些差异吗?我们在谈论对齐问题吗?

由于它是一个嵌入式系统,我们确实使用了外来和内部内核,所以如果可能发生坏事,他们可能会发生。

4

5 回答 5

3

编译器假定指向 As 的指针和指向 long 的指针(通常是双字,但在您的情况下可能只是单词)不指向同一内存区域。这使得一些优化变得可行:例如,当写入指向 A* 的某个位置时,不需要更新来自 long* 的先前加载。这称为混叠——或者在这种情况下,称为混叠。但是在您的情况下,它的效果是生成的代码实际上可能无法按预期工作。

为了使其可移植,您首先必须通过 char 缓冲区复制数据,该缓冲区对抗锯齿规则有一个例外。chars 与所有内容的别名。所以当看到一个字符时,编译器必须假设它可以指向任何东西。例如,您可以这样做:

char buffer[sizeof(A)];
// chars aliases with A
memcpy(buffer, reinterpret_cast<char*>(&a), sizeof(A));
// chars also aliases with unsigned long
writeWordsToHwRegister(reinterpret_cast<unsigned long*> (buffer), sizeof(A)); 

如果您还有其他问题,请查看“严格别名”规则。到目前为止,这实际上是一个众所周知的问题。

于 2012-07-20T09:44:49.650 回答
3

通常,指针之间的差异确实是指不同类型具有不同大小的事实,如果您执行指针+=1,如果 p 是指向 char 的指针或指向 word 的指针,则会得到不同的结果。

于 2012-07-20T09:39:22.150 回答
2

我知道在某些机器上,指向 char 的指针和指向 word 的指针实际上是不同的,因为指向 char 的指针由于内存的寻址方式需要额外的位。

有一些机器(主要是 DSP,但我认为旧的 DEC 机器也这样做了)就是这种情况。

这意味着如果您在其中一台机器上将某些内容重新解释为 char,则位模式必然有效。

由于指向联合的指针理论上可以指向它的任何成员,这意味着联合指针必须包含一些内容才能成功地使用它来指向一个字符或一个单词。这反过来意味着 reinterpret_casting 最终会得到一些位,这些位对正在使用的编译器来说意味着某些东西,就好像它们是有效地址的一部分一样

例如,如果一个指针是 0xfffa,其中 'a' 是编译器用来帮助它在你说 unionptr->charmember (也许什么都没有)时帮助它计算出做什么而在你说 unionptr->wordmember (也许在使用之前将其转换为 3ff),当您将其 reinterpret_cast 转换为 long * 时,您仍然拥有 fffa,因为 reinterpret_cast 对位模式没有任何作用。

现在你有一些编译器认为是指向long的指针,包含fffa,而它应该是(比如)3ff。

这很可能导致严重的崩溃。

于 2012-07-20T09:51:47.963 回答
1

char* 可以字节对齐(任何东西!),而 long* 通常需要与任何现代处理器上的 4 字节边界对齐。

在更大的铁上,当您尝试在未对齐的边界上访问 long 时(例如 *nix 上的 SIGBUS),您会遇到一些崩溃。但是,在某些嵌入式系统上,您可以悄悄地得到一些奇怪的结果,这使得检测变得困难。

我在 ARM7 上看到过这种情况,是的,很难看到发生了什么。

于 2012-07-30T07:14:45.360 回答
0

我不确定为什么您认为涉及到 char 的指针 - 您正在将指向 union A 的指针转换为指向 long 的指针。最好的解决办法可能是改变:

void writeWordsToHwRegister(unsigned long* ptr, unsigned long size)

至:

void writeWordsToHwRegister(const void * ptr, unsigned long size)
于 2012-07-20T09:46:26.207 回答