指针类型强制转换会改变非预期内存

Pointer type casting altering unintended memory

本文关键字:内存 改变 类型 转换 指针      更新时间:2023-10-16
#define ARRAY_SIZE 20
float DataSource[ARRAY_SIZE];
void Read(unsigned char const *Source, unsigned char *Destination, unsigned long DataSize)
{
  for ( unsigned long i = 0; i < DataSize; i++)
  {
    *(Destination + i*DataSize) = *(Source + i*DataSize);
  }
}
void fun()
{
  int Index;
  float Dest;
  for ( Index = 0; Index < ARRAY_SIZE; Index++ )
  {
    Read((unsigned char *)&DataSource[Index], (unsigned char *)&Dest, sizeof(DataSource[Index]));
  }
}

我在上面的代码中遇到了一个问题,在调用Read()时,我的Index变量被覆盖,我确信丑陋的指针强制转换是罪魁祸首,但我很难理解这里到底发生了什么。

unsigned char指针类型是强制性的,因为上述代码旨在模拟某些驱动级软件并保持相同的原型。

谁能帮我理解一下这里的问题?除了Read()的原型之外,上述所有代码都是可以更改的。

错误在这里:

for ( unsigned long i = 0; i < DataSize; i++)
{
  //              vvvvvvvvvv               vvvvvvvvvv
  *(Destination + i*DataSize) = *(Source + i*DataSize);
}

i * DataSize总是大于i => "越界"访问

替换为:

for ( unsigned long i = 0; i < DataSize; i++)
{
  *(Destination + i) = *(Source + i);
}

您将单个float的地址传递给Read (&Dest),然后继续将许多值写入连续的内存位置。因为你写的是随机内存在这一点上,它不太可能覆盖index(和其他东西),因为堆栈通常向下增长。

这是错误的:

*(Destination + i*DataSize) = *(Source + i*DataSize);

你想复制DataSize相邻的字节,而不是DataSize分开的字节(total span DataSize*DataSize)

只是说

Destination[i] = Source[i];

一种有趣的(对我来说)c++方式

template<typename Data>
struct MemBlockRefHelper {
  typedef Data value_type;
  Data* data;
  size_t size;
  MemBlockRefHelper( Data* d, size_t s):data(d), size(s) {}
  template<typename Target, typename Other=typename Target::value_type>
  Target& Assign( MemBlockRefHelper<Other> const& other ) {
    Assert(size == other.size);
    for (size_t i = 0; i < size; ++i) {
      if (i < other.size) {
        data[i] = other.data[i];
      } else {
        data[i] = 0;
      }
    }
    Target* self = static_cast<Target*>(this);
    return *self;
  }
};
struct MemBlockRef;
struct MemBlockCRef:MemBlockRefHelper<const unsigned char> {
  MemBlockCRef( const unsigned char* d, size_t s ):MemBlockRefHelper<const unsigned char>( d, s ) {}
  MemBlockCRef( const MemBlockRef& other );
};
struct MemBlockRef:MemBlockRefHelper<unsigned char> {
  MemBlockRef( unsigned char* d, size_t s ):MemBlockRefHelper<unsigned char>( d, s ) {}
  MemBlockRef& operator=( MemBlockRef const& other ) {
    return Assign< MemBlockRef >( other );
  }
  MemBlockRef& operator=( MemBlockCRef const& other ) {
    return Assign< MemBlockRef, const unsigned char >( other );
  }
};
inline MemBlockCRef::MemBlockCRef( const MemBlockRef& other ): MemBlockRefHelper<const unsigned char>( other.data, other.size ) {}
void Read( unsigned char const* Source, unsigned char* Dest, unsigned long DataSize ) {
  MemBlockCRef src( Source, DataSize );
  MemBlockRef dest( Dest, DataSize );
  dest = src;
}

大量过度设计,但其思想是将一定大小的POD内存块的思想包装起来,并为其内容提供引用语义(初始化是创建对相同数据的新引用,赋值是在被引用的数据上复制)。

一旦有了这样的类,Read的代码就变成了一个3行代码。你可以一次完成:

  MemBlockRef( Dest, DataSize ) = MemBlockCRef( Source, DataSize );

但这是不必要的。

这是整个框架。

但是我觉得写这个很有趣

让我们仔细看看你的Read(): i从0到DataSize-1的变化;每次访问内存的偏移量为i*DataSize…即,从0到DataSize*(DataSize-1)的偏移量。看起来不对,因为DataSize**2-DataSize没有意义。

不像其他答案,我不想猜你想要什么。只是展示一种"维度分析",可以帮助发现代码中最错误的部分,而无需读取作者的思想

您将在fun()中声明的标量变量Dest视为Read()中的数组。似乎DestIndex变量都放在堆栈上相邻,这解释了IndexRead()内的循环为i==1执行时被覆盖。

所以解决方案是:将Dest也声明为数组:

float Dest[ARRAY_SIZE];