Endianness Work-around Needed

2020-03-31 09:12发布

Consider the following piece of code:

#include "stdio.h"

typedef struct CustomStruct
{
  short Element1[10];
}CustomStruct;

void F2(char* Y)
{
  *Y=0x00;
  Y++; 
  *Y=0x1F;    
}

void F1(CustomStruct* X)
{
  F2((char *)X);
  printf("s = %x\n", (*X).Element1[0]);
}

int main(void)
{
  CustomStruct s;
  F1(&s);

  return 0;
}

At run-time, by the end of calling the function F1, I get different results by using different compilers.

(*X).Element1[0] = 0x1f00 in some compiler and (*X).Element1[0] = 0x001f with another one.

It's clear to me that it's an endianness issue.

Is there any compiler option or work-around to use so that I get (*X).Element1[0] = 0x001f regardless the used compiler?

2条回答
放荡不羁爱自由
2楼-- · 2020-03-31 09:37

Endianness is not a compiler issue, nor even an operating system issue, but a platform issue. There are no compiler options or "workarounds" for endianness. There are however conversion routines so that you can normalize the endianness of stored data.

The ntoh routines documented here will reorder the bytes pointed to from network order (big endian) to host order (either big or little, depending on the type of host). There are also hton functions that go in the opposite direction, from host order to network order.

If you want to normalize the bytes stored in your data structure, you need to do it yourself either when you store the data or when you try to read it.

Here are function templates I wrote for ntohx and htonx that are generalized on the type of data store, be it a 2 byte, 4 byte or 8 byte type:

template<class Val> inline Val ntohx(const Val& in)
{
    char out[sizeof(in)] = {0};
    for( size_t i = 0; i < sizeof(Val); ++i )
        out[i] = ((char*)&in)[sizeof(Val)-i-1];
    return *(reinterpret_cast<Val*>(out));
}

template<> inline unsigned char ntohx<unsigned char>(const unsigned char & v )
{
    return v;
}
template<> inline uint16_t ntohx<uint16_t>(const uint16_t & v)
{
    return ntohs(v);
}

template<> inline uint32_t ntohx<uint32_t>(const uint32_t & v)
{
    return ntohl(v);
}

template<> inline uint64_t ntohx<uint64_t>(const uint64_t & v)
{
    uint32_t ret [] =
    {
        ntohl(((const uint32_t*)&v)[1]),
        ntohl(((const uint32_t*)&v)[0])
    };
    return *((uint64_t*)&ret[0]);
}
template<> inline float ntohx<float>(const float& v)
{
    uint32_t const* cast = reinterpret_cast<uint32_t const*>(&v);
    uint32_t ret = ntohx(*cast);
    return *(reinterpret_cast<float*>(&ret));
};

template<class Val> inline Val htonx(const Val& in)
{
    char out[sizeof(in)] = {0};
    for( size_t i = 0; i < sizeof(Val); ++i )
        out[i] = ((char*)&in)[sizeof(Val)-i-1];
    return *(reinterpret_cast<Val*>(out));
}

template<> inline unsigned char htonx<unsigned char>(const unsigned char & v )
{
    return v;
}
template<> inline uint16_t htonx<uint16_t>(const uint16_t & v)
{
    return htons(v);
}

template<> inline uint32_t htonx<uint32_t>(const uint32_t & v)
{
    return htonl(v);
}

template<> inline uint64_t htonx<uint64_t>(const uint64_t & v)
{
    uint32_t ret [] =
    {
        htonl(((const uint32_t*)&v)[1]),
        htonl(((const uint32_t*)&v)[0])
    };
    return *((uint64_t*)&ret[0]);
}
template<> inline float htonx<float>(const float& v)
{
    uint32_t const* cast = reinterpret_cast<uint32_t const*>(&v);
    uint32_t ret = htonx(*cast);
    return *(reinterpret_cast<float*>(&ret));
};
查看更多
够拽才男人
3楼-- · 2020-03-31 09:44

If F2() is receiving a char *, then it must be doing something pretty strange in order to cause endian-related problems.

These only happen when accessing more than one char at a time, unless it's manually doing such accesses while being broken. Is it casting it's argument back to short * or something?

In short, show more code.

查看更多
登录 后发表回答