【问题标题】:Pointer address standards-compliant conversion指针地址符合标准的转换
【发布时间】:2013-05-17 08:34:50
【问题描述】:

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

我在下面有两个版本,我相信第一个版本包含未定义的行为,第二个版本应该只包含根据 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 的地址?或者有什么办法可以绕过?

【问题讨论】:

  • 您确定要传输地址吗?无论如何,它们在其他主机或其他地址空间上都无效。
  • 我不知道任何具有 16 位指针的 C 实现,但假设你是对的(一些小型嵌入式系统或其他东西?) main1() 没有任何问题。您显然在 main2() 中使用了 union 或其他东西,但您没有向我们展示该代码,因此我们无法帮助您。
  • int i = (int) &(a[0]); 会出现问题,因为 sizeof(int) != sizeof(void*) 在某些系统上
  • 正如我所说,我假设他对 16 位指针是正确的(在我所知道的任何系统上都不是这种情况)。唯一可能的“未定义行为”是丢弃除指针的低 16 位之外的所有内容。
  • @LeeDanielCrocker 摩托罗拉 6811 指针是 16 位的。此外,还有 8086 个“近”指针。

标签: c undefined-behavior standards-compliance


【解决方案1】:

对于指针,最好使用类型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;
        }
    }
}

【讨论】:

  • 好的,所以如果我的工具告诉我所有三个程序(我的版本和你的版本)都有未定义的行为,那么我的工具只接受 C 的子集? *d-- = u &amp; 0xFF 行是否包含未定义的行为?或者只是实现定义的行为?如果是这样,有没有办法避免它?
  • 此代码对所有机器都是正确的。不,您指出的那条线是 100% 正确且合规的。如果你有一些工具告诉你不是这样,那就毫无价值了。
  • 只是为了澄清我可以从标准中掌握的内容:u &amp; 0xFF 包含一个指针类型 (u) 和一个按位与运算符。然后将指针类型转换为整数类型,根据C99标准中的第6.3.2.3.6项“结果由实现定义”,然后对转换的结果进行按位运算。换句话说,我相信如果不依赖至少一些实现定义的行为,就无法执行此转换,这意味着您的解决方案(可能还有我的)都符合标准。
  • @dhekir u 不是指针类型。它是一个整数类型,大小足以容纳指针值。
  • @ValeriAtamaniouk 好的,所以实现定义的从指针到整数的转换发生在uintptr_t u = (uintptr_t)ptr
猜你喜欢
  • 2014-07-23
  • 2016-06-02
  • 1970-01-01
  • 1970-01-01
  • 2021-10-31
  • 1970-01-01
  • 2016-03-30
  • 2021-04-08
  • 1970-01-01
相关资源
最近更新 更多