在回答这个问题时,我萌生了一个想法,想偷偷地绕过不将浮点数转换为指针的 C 约束,并想出了这段令人讨厌的异国情调的人工代码:
(显然我们不应该像这样编写真正的产品代码 - 请注意“语言律师”标签。)
#include <stdio.h>
void get_value (void* data)
{
printf("Value %f \r\n", *(float*)&(int){(int)data});
}
int main(void) {
float di = 10.10f;
get_value( (void*) ((union cheeky{int i; float f;}){.f=di}.i) );
return 0;
}
据我所知,这是有效的 C。 main() 中的联合应该意味着严格的别名不适用,尽管我不确定这里的“有效类型”是什么,因为该对象具有多种表示形式。通过在中间转换为
int
来初始化另一个 void*
,但至少会打印正确的结果。
当然,整数到指针的转换是实现定义的,
int
和float
的大小也是如此,gcc 会相应地抱怨。显然,如果指针未对齐或陷阱表示等,可能会出现 UB。
但是,当我添加优化时,我得到了这个真正奇怪的诊断。
gcc -std=c2x -pedantic-errors -Wall -Wextra -O3
<source>:5:29: warning: '<U ea0>' is used uninitialized [-Wuninitialized]
5 | printf("Value %f \r\n", *(float*)&(int){(int)data});
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:5:44: note: '({anonymous})' declared here
5 | printf("Value %f \r\n", *(float*)&(int){(int)data});
| ^
所以 this 指的是函数内部的复合文字。这是由
void*
的演员们清楚地初始化的。此警告仅在 -O3
和使用 gcc 11 或更高版本的组合下弹出。较旧的 gcc 不会发出警告,clang 也不会发出警告。
我似乎还得到了一个不同的神秘“十六进制代码”
<U ea0>
等,具体取决于gcc版本,但这可能(?)只是因为我每次在编译器资源管理器中得到不同的构建。
问题:
-O3
下?我尝试过__attribute__((noinline))
,但没有什么区别。我编译了代码的一个小变体(在定义之前明确声明了
get_value()
以避免出现 -Wmissing-prototypes
错误):
/* SO 7840-1960 */
#include <stdio.h>
extern void get_value (void* data);
void get_value (void* data)
{
printf("Value %f \r\n", *(float*)&(int){(int)data});
}
int main(void) {
float di = 10.10f;
get_value( (void*) ((union cheeky{int i; float f;}){.f=di}.i) );
return 0;
}
我使用了 Clang (
Apple clang version 15.0.0 (clang-1500.3.9.4)
) 并得到了以下输出:
$ clang -Weverything -Wno-poison-system-directories -Werror -c ll67.c
ll67.c:8:45: error: cast to smaller integer type 'int' from 'void *' [-Werror,-Wvoid-pointer-to-int-cast]
printf("Value %f \r\n", *(float*)&(int){(int)data});
^~~~~~~~~
ll67.c:8:29: error: implicit conversion increases floating-point precision: 'float' to 'double' [-Werror,-Wdouble-promotion]
printf("Value %f \r\n", *(float*)&(int){(int)data});
~~~~~~ ^~~~~~~~~~~~~~~~~~~~~~~~~~
ll67.c:15:16: error: cast to 'void *' from smaller integer type 'int' [-Werror,-Wint-to-void-pointer-cast]
get_value( (void*) ((union cheeky{int i; float f;}){.f=di}.i) );
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 errors generated.
$
我不确定这告诉了你什么(如果有的话)。但输出太大,在评论中不合理。