将 C 字节顺序和指针黑魔法移植到 Swift

Porting C endianness & pointers black magic to Swift

我正在尝试翻译这个片段:

ntohs(*(UInt16*)VALUE) / 4.0

和其他一些,看起来很像,从 C 到 Swift。 问题是,我对 Swift 知之甚少,而且我无法理解这段代码的作用...这就是我所知道的:

谢谢!

看起来代码采用的是单字节值[0]。然后取消引用,这应该从低内存地址中检索一个数字,从 1 到 127(可能是 255)。 然后除以 4.

我真的不敢相信我的解释是正确的,也无法检查我没有笔记本电脑。我真的认为您的代码中可能存在拼写错误,因为这不是一件好事。便携、可重复使用

我必须强调,字符串没有被转换成数字。然后使用

  1. VALUE 是指向 32 字节 (char[32]) 的指针。
  2. 指针转换为 UInt16 指针。这意味着 VALUE 的前两个字节被解释为 UInt16(2 个字节)。
  3. * 将取消引用指针。我们得到 VALUE 的两个字节作为一个 16 位数字。但是它有净字节序(净字节顺序),所以我们不能对它进行整数运算。
  4. 我们现在将字节序交换为主机,我们得到一个正常的整数。
  5. 我们将整数除以 4.0

要在 Swift 中做同样的事情,让我们将字节值组合成一个整数。

let host = (UInt(data.0) << 8) | UInt(data.1)

请注意,要除以 4.0,您必须将整数转换为 Float

您引用的 C 在技术上是不正确的,尽管它会按大多数生产 C 编译器的预期进行编译。¹实现相同效果的更好方法,也应该更容易转换为 Swift,是

unsigned int val = ((((unsigned int)VALUE[0]) << 8) |  // ² ³
                    (((unsigned int)VALUE[1]) << 0));  // ⁴

double scaledval = ((double)val) / 4.0;                // ⁵

第一条语句读取VALUE的前两个字节,将它们解释为网络字节顺序的16位无符号数,并将它们转换为主机字节顺序(无论这些字节顺序是否不同).第二条语句将数字转换为 double 并对其进行缩放。

¹ 具体来说,*(UInt16*)VALUE 会引发未定义的行为,因为它违反了基于类型的别名规则,即 非对称:字符类型的指针可用于访问具有任何类型的对象,但具有任何其他类型的指针可能不能用于访问具有(数组)字符类型的对象。

² 在 C 中,为了使后续的移位和 or-ing 以无符号类型发生,必须在此处强制转换为 unsigned int。如果您转换为 uint16_t,这似乎更合适,那么 "usual arithmetic conversions" 会在进行左移之前将其转换为带符号的 int。这会在 int 只有 16 位宽的系统上引发未定义的行为(不允许移入符号位)。 Swift 几乎肯定有 完全不同的 小范围类型的算术规则;您可能需要在轮班前转换为 something,但我不能告诉您是什么。

³ 我将这个表达式用括号括起来,这样即使您对 C 语言不是很熟悉,操作顺序也会很清楚。

⁴ 左移零位无效;它仅包含在并行结构中。

⁵ 在除法运算之前显式转换为 double 在 C 中不是必需的,但它在 Swift 中,所以我在这里这样写。