演员阵容和工会一样安全吗?

use*_*215 1 c c++ embedded arduino unions

我想将像浮点数这样的大变量分成字节段,并通过UART逐字节地发送这些变量.我正在使用C/C++.

一种方法可能是深入复制我想要发送给联合的值,然后发送它.我认为这将是100%安全但速度慢.工会看起来像这样:

   union mySendUnion
   {
       mySendType sendVal;
       char[sizeof(mySendType)] sendArray; 
    }
Run Code Online (Sandbox Code Playgroud)

另一种选择可能是将指向我想要发送的值的指针转换为指向特定联合的指针.这还安全吗?

第三个选项可能是将指针强制转换为我要发送给char的值,然后递增指针,如下所示:

            sendType myValue = 443.2;

    char* sendChar = (char*)myValue; 

    for(char i=0; i< sizeof(sendType) ; i++)
    {
        Serial.write(*(sendChar+j), 1);
    }
Run Code Online (Sandbox Code Playgroud)

我已经成功使用了上面的指针算术,但我不确定它在所有情况下是否安全.我担心的是,如果我们使用32位处理器并想发送一个浮点数会怎样.编译器选择将此32位浮点存储到一个存储器单元中,但仅将一个单个字符存储到每个32位单元中.

然后每个计数器增量使程序指针增加一个整个存储单元,我们将错过浮点数.

C标准中是否存在阻止此问题的内容,或者这可能是某个编译器的问题?

The*_*ant 6

首先,你不能用"C/C++"编写代码.没有"C/C++"这样的语言,因为它们基本上是不同的语言.因此,关于工会的答案根本不同.

至于标题:

演员阵容和工会一样安全吗?

不,通常他们不是,因为严格的别名规则.也就是说,如果使用指向不兼容类型的指针键入某个特定类型的指针,则会导致未定义的行为.此规则的唯一例外是当您通过指向(有符号或无符号)的指针对对象进行别名来读取或操作对象的逐字节表示时char.就像你的情况一样.

然而,工会是完全不同的混蛋.在C99及更高版本中允许通过复制到联合并从联合读取来输入类型,但在C89和所有版本的C++中会导致未定义的行为.

一个方向上,如果您将原始联合作为实际对象,则还可以使用指向union的指针安全地键入pun(在C99及更高版本中).像这样:

union p {
    char c[sizeof(float)];
    float f;
} pun;
union p *punPtr = &pun;

punPtr->f = 3.14;
send_bytes(punPtr->c, sizeof(float));
Run Code Online (Sandbox Code Playgroud)

因为"指向联合的指针指向其所有成员,反之亦然"(C99,我不记得确切的段落,它大约是6.2.5,IIRC).但是,在另一个方向却不是这样:

float f = 3.14;
union p *punPtr = &f;
send_bytes(punPtr->c, sizeof(float)); // triggers UB!
Run Code Online (Sandbox Code Playgroud)

总结一下:以下代码片段在C89,C99,C11和C++中都有效:

float f = 3.14;
char *p = (char *)&f;
size_t i;
for (i = 0; i < sizeof f; i++) {
    send_byte(p[i]); // hypotetical function
}
Run Code Online (Sandbox Code Playgroud)

以下内容仅在C99及更高版本中有效:

union {
    char c[sizeof(float)];
    float f;
} pun;

pun.f = 3.14;
send_bytes(pun.c, sizeof float); // another hypotetical function
Run Code Online (Sandbox Code Playgroud)

下面,然而,将不会是有效的:

float f = 3.14;
unsigned *u = (unsigned *)&f;
printf("%u\n", *u); // undefined behavior triggered!
Run Code Online (Sandbox Code Playgroud)

另一种始终保证有效的解决方案memcpy().该memcpy()函数在两个对象之间进行逐字节复制.(不要让我开始"缓慢" - 在大多数现代编译器和stdlib实现中,它是一个内在函数).