指向char的指针和指向word的指针之间的区别

时间:2012-07-20 09:31:24

标签: c++ c embedded lint

我一直在收到来自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的指针和指向word的指针。有人可以解释这种差异是如何表现出来的,并且可能会在一些处理器上举例说明这些差异吗?我们是在讨论对齐问题吗?

由于它是一个嵌入式系统,我们使用异国情调和内部核心,所以如果可能发生不好的事情,他们可能会这样做。

5 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:3)

编译器假设指向As的指针和指向long的指针(通常是dwords,但在您的情况下可能只是单词)不指向相同的内存区域。这使得一些优化成为可能:例如,当写入指向A *的某个地方时,不需要更新来自long *的先前加载。这称为混叠 - 或者在这种情况下,缺少混叠。但在你的情况下,它产生的代码可能实际上不能按预期工作。

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

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)); 

如果您还有其他问题,请查看“严格别名”规则。它现在实际上是一个众所周知的问题。

答案 2 :(得分:2)

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

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

这意味着如果你在其中一台机器上将某些东西重新解释为char,那么位模式必然是有效的。

因为指向联合的指针在理论上可以指向它的任何成员,这意味着联合指针必须包含一些东西,以允许您成功地使用它指向一个字符或一个单词。这反过来意味着重新解释它会最终产生一些位,这些位意味着编译器被使用,就好像它们是有效地址的一部分一样

例如,如果一个指针是0xfffa,那么'a'是一些魔术,当你说unionptr-&gt; charmember(也许什么都没有)时,编译器使用它来帮助它解决该怎么做以及当你做unionptr时不同的东西&gt; wordmember(可能在使用之前将其转换为3ff),当你将它重新解释为long *时,你仍然有fffa,因为reinterpret_cast对位模式没有任何作用。

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

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

答案 3 :(得分:1)

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

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

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

答案 4 :(得分:0)

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

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

为:

void writeWordsToHwRegister(const void * ptr, unsigned long size)
相关问题