问题描述
我正在测试是否可以在同一数组中具有int和float值,我编写了以下代码:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int v[4]={0,0};
*((float*)(&v[1]))=45.6;
printf("%f\n",*((float*)(&v[1])));
printf("%f\n",v[1]);
return 0;
}
我原本期望45.599998和0或在两个printf上都具有相同的值,但是得到不同的结果: 45.599998 45.599983 为什么?,发生了什么事?
编辑
顺便说一句,我想强调的是,对于这个问题,我对替代方法的兴趣不大,而对理解为什么不起作用的兴趣更大。
解决方法
在示例代码中,如注释中指出的那样,调用undefined behavior。 (在这种情况下,我认为违反了strict aliasing。)
” [[是否可以在同一数组中包含int
和float
值”
不在C
中,任何array
中的type
只能包含一种类型。但是,如果这种安排的变体对您有用,则可以在compound type的单个实例中定义这两种类型,例如struct
的数组。例如:
typedef struct {
int iVal;
float fVal;
} val_s;
val_s val[10];
现在,您拥有10
类型的val_s
个元素的数组,为此,每个元素都包含int
和float
类型的成员。
C
中复合类型的另一种变体(已在注释中向我指出,可能是您想要的更多),它是内置的C
类型,它允许多种类型共享相同的内存是union。需要注意的是,由程序员来跟踪最后写入哪个成员,因为在给定的时间只有一个成员可以包含一个值...
示例:
typedef union {
int iVal;
float fVal;
} val_s;
val_s val;
,
我原本希望两个printf的值分别为45.599998和0或具有相同的值,但是得到的结果却不同:45.599998 45.599983为什么?发生了什么事?
假设我们使用此变体消除了代码中的一些未定义行为:
#include <string.h>
#include <stdio.h>
int main() {
int v[4]={0,0};
float f = 45.6;
memcpy(&v[1],&f,sizeof f);
printf("%f\n",f);
printf("%f\n",v[1]);
return 0;
}
这仍然具有未定义的行为,因为格式指令%f
与v[1]
的类型不正确匹配,但是只要float
不大于{{1} },并且int
没有陷阱表示(对于大多数C实现而言,两者都适用)。
即使int
和f
的值具有相同的字节序列表示形式,它们的类型之间的差异对于此代码也具有重要的影响。可变参数(例如v[1]
)的变量参数受“默认参数提升”的约束。它们使类型printf
的值保持不变,但它们将int
提升为类型float
。因此,如果您的double
和float
在实践上通常有所不同,那么
-
在两种情况下,即使仅考虑每个参数的字节序列,
-
double
也会收到不同的参数值,并且 - 在
printf
情况下,v[1]
可能接收不到足够宽的值。
因此,如果您沉迷于在这种未定义行为的情况下假设程序实际执行的操作的可疑做法,那么更可能的可能性之一是在printf
情况下它会查看字节v[1]
的序列,再加上一些恰好位于内存中的随机字节,将它们解释为好像是float
的字节,并根据运气和细节所选的测试值,其数值接近但不完全与您的double
提升到的double
匹配。
使用浮点说明符并传递一个整数值来满足格式字符串中的该参数是未定义行为,也就是说,所有选择均无效。
许多系统以完全不同的方式传递积分参数和浮点参数;有些没有。 “为什么”不能完全回答。您碰巧在一个其调用约定似乎没有不同地传递整数和浮点参数的系统上。