6

我有一个如下结构:

struct pts_t 
{
    uint32_t lsb;
    uint32_t msb;
};

我想把它变成双重的。直接写是否安全:

pts_t t; 

double timestamp = t;

更复杂的是,如果结构类型是 C dll API 的一部分,没有“打包”属性(对于所有编译器),在这种情况下,我必须pts_t*通过 API 将接收复制到我创建的 pts_t 实例以控制结构打包?

void f(pts_t* t)
{ 
   pts_t myt; myt.lsb = t->lsb; myt.msb = t->msb;

   double timestamp = *(double*)(&myt.lsb);
}
4

4 回答 4

4

即使您假设您double是 64 位宽,如果您正在寻找可移植代码,您应该非常小心:您的结构double可能有不同的对齐约束,并且您的编译器可能会因为别名规则而感到困惑。避免出现问题的一种方法是使用union

union {
 struct pts_t pts;
 double timestamp;
} x = { .pts = t };

然后使用x.timestamp.

还要小心,double像这样的“组合”可能会导致奇怪的值,例如你不会遇到的无穷大。

于 2013-08-09T08:14:33.550 回答
3

你必须写

double timestamp = *( (double*)(&t.lsb));

你应该使用类似的东西

struct __attribute__((__packed__)) pts_t {
    ...
};

确保您的结构被打包(尽管在这种情况下我不明白为什么任何编译器会在 lsb 之后填充一些东西)。

...

实际上,取决于您的平台是大端还是小端,您可能必须切换 lsb 和 msb 或执行以下操作:

double timestamp;
double* p_timestamp = &timestamp;
*((uint32_t*)p_timestamp) = t.msb;
*( ((uint32_t*)p_timestamp) + 1) = t.lsb;
于 2013-08-09T07:04:55.783 回答
3

最初的想法是写以下内容:

double timestamp = *( ( double * ) &( t.lsb ) );

要逐步完成此操作(假设您处于 32 位环境中):

  1. 您正在获取标识符的地址,t.lsb因为您需要找到结构中第一个字节的内存地址。请注意,您也可以执行&t.
  2. 然后,您将该内存地址转换为指向双精度(8 个字节)的指针。
  3. 您最后取消引用该指针并将所有 8 个字节存储在标识符timestamp使用的 8 字节内存块中。

评论:

  1. 您将需要考虑小/大字节序。
  2. 您假设结构和双精度都正确对齐(如下所述)。
  3. 这不是可移植的,您假设双精度为 8 个字节。

现在,评论简介中的三点令人担忧。当跨多个平台移植此代码时,它变得非常痛苦。如下所述,使用 Cunions是一种更好、更正确的可移植解决方案。

用 C 写成如下unions

double timestamp = ( union { double d; struct pts_t pts; } ) { t } .d;
于 2013-08-09T07:13:00.757 回答
0

你有:

struct pts_t 
{
    uint32_t lsb;
    uint32_t msb;
};

这个:

pts_t t; 
double timestamp = t;

完全“安全”,因为它不会编译,因此不会造成任何伤害。你还没有定义类型pts_t;你已经定义了一个 type struct pts_t

这个:

struct pts_t t;
double timestamp = t;

也不会编译,因为您不能将结构类型的值转换(显式地,使用强制转换,或隐式地,使用赋值)转换为数字类型的对象。

我谦虚地建议,如果您在发布之前尝试过,您会节省一些时间。

可能最直接的方法是使用memcpy()

#include <assert.h>
#include <string.h>
/* ... */
struct pts_t t = { some_lsb_value, some_msbZ_value };
double timestamp;
assert(sizeof t == sizeof timestamp);
memcpy(&timestamp, &t, sizeof timestamp);

通过使用memcpy()而不是指针转换,您可以避免任何未对齐的内存访问风险;根据系统的不同,您的结构可能需要比double.

也不能保证您的 struct 与 a 的大小相同double。该结构几乎可以肯定是 64 位,并且可能是double64位,但实际上都不能保证,并且明确地做出假设也没有什么坏处。

这就留下了一个问题,即您存储在t.lsb和中的值是否t.msb构成了有效值的表示double,尤其是您想要的值。该语言很少说明浮点类型的表示方式。特别是,字节顺序可以并且确实在不同系统中有所不同。您需要确保以这种方式重新解释表示实际上是有意义的——并且您的代码可能是不可移植的。

于 2013-08-09T20:44:54.367 回答