指针符合标准兼容的转换

时间:2013-05-22 10:19:49

标签: c undefined-behavior standards-compliance

我需要找到最符合标准的方法来获取指针的地址并单独存储它的字节(例如,串行传输它们)。

我有两个版本,第一个包含,我相信,未定义的行为,第二个版本应该只包含根据C99定义的行为。但我的工具告诉我,我在第二个上也有未定义的行为。有人可以确认一下,并指出一个解决方案,既没有未定义的行为,也没有实现定义的行为,如果可能的话?

修改:我将类型从int更改为unsigned long,以帮助查找非依赖于实现的解决方案。我还删除了“16位宽指针”。

unsigned long a[2];
unsigned char b0, b1, b2, b3;

int main1() {
  unsigned long l = (unsigned long) &(a[0]);
  b0 = (l >> 24) & 0xFF;
  b1 = (l >> 16) & 0xFF;
  b2 = (l >> 8) & 0xFF;
  b3 = l & 0xFF;
  return 0;
}


typedef union { unsigned long* p; char c[sizeof(unsigned long *)]; } u;

int main2() {
  u x;
  x.p = a;
  b0 = x.c[3];
  b1 = x.c[2];
  b2 = x.c[1];
  b3 = x.c[0];
  return 0;
}

编辑2 :添加了有关这些计划的C99标准部分的参考:

  

任何指针类型都可以转换为整数类型。除非事先指明,否则   结果是实现定义的。如果结果无法以整数类型表示,   行为未定义。结果不必在任何整数的值范围内   类型。

是否意味着在不依赖某些实现定义的行为的情况下无法读取数组a的地址?或者有办法规避它吗?

1 个答案:

答案 0 :(得分:2)

对于指针,最好使用类型unsigned long(或unsigned long long)。除非有uintptr_t数据类型。 为什么unsigned?因为移位操作仅对无符号整数有用。对于已签名的,它与平台有关。

所以你要传输地址(无论出于何种原因,因为地址通常是进程本地的),你可以这样做:

/**
 * @param ptr Pointer to serialize
 * @param buf Destination buffer
 * @param be  If 0 - little endian, 1 - big endian encoding
 */
void ptr2buf(const void *ptr, void *buf, int be)
{
    uintptr_t u = (uintptr_t)ptr;
    unsigned char *d = buf;

    if (be)
    {
        /* big endian */
        d += sizeof(t) - 1;

        for (i = 0; i < sizeof(t); ++i)
        {
            *d-- = u & 0xFF;
            u >>= 8;
        }
    }
    else
    {
        /* little endian */

        for (i = 0; i < sizeof(t); ++i)
        {
            *d++ = u & 0xFF;
            u >>= 8;
        }
    }
}