将 C 字节顺序和指针黑魔法移植到 Swift
Porting C endianness & pointers black magic to Swift
我正在尝试翻译这个片段:
ntohs(*(UInt16*)VALUE) / 4.0
和其他一些,看起来很像,从 C 到 Swift。
问题是,我对 Swift 知之甚少,而且我无法理解这段代码的作用...这就是我所知道的:
ntohs
将字节序交换为主机字节序
VALUE
是一个 char[32]
- 我刚刚发现 Swift :
(UInt(data.0) << 6) + (UInt(data.1) >> 2)
做同样的事情。可以解释一下吗?
- 我愿意return一个Swift
Uint
(UInt64
)
谢谢!
看起来代码采用的是单字节值[0]。然后取消引用,这应该从低内存地址中检索一个数字,从 1 到 127(可能是 255)。
然后除以 4.
我真的不敢相信我的解释是正确的,也无法检查我没有笔记本电脑。我真的认为您的代码中可能存在拼写错误,因为这不是一件好事。便携、可重复使用
我必须强调,字符串没有被转换成数字。然后使用
VALUE
是指向 32 字节 (char[32]
) 的指针。
- 指针转换为
UInt16
指针。这意味着 VALUE
的前两个字节被解释为 UInt16
(2 个字节)。
*
将取消引用指针。我们得到 VALUE
的两个字节作为一个 16 位数字。但是它有净字节序(净字节顺序),所以我们不能对它进行整数运算。
- 我们现在将字节序交换为主机,我们得到一个正常的整数。
- 我们将整数除以
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 中,所以我在这里这样写。
我正在尝试翻译这个片段:
ntohs(*(UInt16*)VALUE) / 4.0
和其他一些,看起来很像,从 C 到 Swift。 问题是,我对 Swift 知之甚少,而且我无法理解这段代码的作用...这就是我所知道的:
ntohs
将字节序交换为主机字节序VALUE
是一个char[32]
- 我刚刚发现 Swift :
(UInt(data.0) << 6) + (UInt(data.1) >> 2)
做同样的事情。可以解释一下吗? - 我愿意return一个Swift
Uint
(UInt64
)
谢谢!
看起来代码采用的是单字节值[0]。然后取消引用,这应该从低内存地址中检索一个数字,从 1 到 127(可能是 255)。 然后除以 4.
我真的不敢相信我的解释是正确的,也无法检查我没有笔记本电脑。我真的认为您的代码中可能存在拼写错误,因为这不是一件好事。便携、可重复使用
我必须强调,字符串没有被转换成数字。然后使用
VALUE
是指向 32 字节 (char[32]
) 的指针。- 指针转换为
UInt16
指针。这意味着VALUE
的前两个字节被解释为UInt16
(2 个字节)。 *
将取消引用指针。我们得到VALUE
的两个字节作为一个 16 位数字。但是它有净字节序(净字节顺序),所以我们不能对它进行整数运算。- 我们现在将字节序交换为主机,我们得到一个正常的整数。
- 我们将整数除以
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 中,所以我在这里这样写。