打印联合值的怪癖
Quirk with printing union values
下面的代码导致:
0.000000
10
在这种情况下,'data' 返回了什么?我知道 n.data.idata
和 n.data.fdata
是正确的用法,我只是好奇为什么整数值在这种情况下有效而浮点值无效。
#include<stdio.h>
#include<stdlib.h>
typedef struct node
{
union container
{
int idata;
float fdata;
} data;
struct node *next;
} Node;
int main()
{
Node i = {.data.idata = 10};
Node n = {.data.fdata = 10.0};
printf("%f\n", n.data);
printf("%d\n", i.data);
printf("\nExiting program...\n");
return 0;
}
对于我原来回答错误的问题表示歉意。我觉得这个答案
可能有助于向刚接触 C 的新手回答这个问题,说明 2501 在说什么(强调我的)
Specifier f assumes a default argument promotion from float to double,
which doesn't happen in this case, because a union data is passed to
the function. So the function receives the union data which consists
of 4 bytes and represents a float, but tries to print 8 bytes, because
it expects a double. The result is a nonsense value, in your case 0.0.
类型提升将一种二进制表示安全地或至少转换为另一种
以标准化的方式。以下都是在x86机器上完成的
无符号 64 位格式 10
的二进制值
|01010000|00000000|00000000|00000000|00000000|00000000|00000000|00000000
作为 32 位浮点数的二进制值 10...
|00000000|00000000|00000100|10000010
64 位格式的双精度值 10...
|00000000|00000000|00000000|00000000|00000000|00000000|00100100|00000010
数字 10 的所有三种表示形式都有很大不同。类型提升
没有发生意味着 printf
语句中的数字被视为
double 并且它是这样打印的。在这种情况下,它显示为的唯一原因
零是精确度。
下面代码中的Node
数据结构是8个字节。没有填充
在结构中或者它不能是 8 个字节长。如果我们将 float
设置为 10 并且 next
到 0 我们在内存中有这个表示...
|00000000|00000000|00000100|10000010|00000000|00000000|00000000|00000000
上面如果转换成double并用printf("%f'\n");
打印就可以了
看起来像 0.0
。如果你使用 'printf("%g\n")' 打印它,你会看到一些
位必须设置在 8 个字节中,在我的例子中我得到
5.39824e-315
看看浮动 class 可以使用什么 fpclassify
。请参阅下面的代码...
#include <assert.h>
#include <float.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct node {
union container {
float fdata;
int idata;
} data;
uint32_t next;
} Node;
int main(void) {
size_t nsize1 = sizeof(Node);
assert(nsize1 == 8);
assert(sizeof(int) == 4);
assert(sizeof(float) == 4);
assert(sizeof(double) == 8);
Node n = {.data.fdata = 10.0};
n.next = 0;
double d = *(double*)&n;
int class_of_d = fpclassify(d);
assert(d > 0);
switch(class_of_d) {
case FP_NAN : printf("FP_NAN\n");break;
case FP_INFINITE : printf("FP_INFINITE\n");break;
case FP_ZERO : printf("FP_ZERO\n");break;
case FP_NORMAL : printf("FP_NORMAL\n");break;
case FP_SUBNORMAL : printf("FP_SUBNORMAL\n");break;
case FP_SUPERNORMAL: printf("FP_SUPERNORMAL\n");break;
}
printf("%g\n", d);
return 0;
}
让我们暂时忽略明显的未定义行为,这是因为将不正确的类型传递给说明符 f
的 printf 函数。该类型是匿名联合。
说明符 f
假定默认参数从 float 提升为 double,在这种情况下不会发生这种情况,因为联合 data
被传递给函数。因此函数接收由 4 个字节组成并表示浮点数的联合 data
,但尝试打印 8 个字节,因为它需要一个双精度数。结果是一个无意义的值,在你的例子中是 0.0。
(此答案假设 IEEE 754,并且 sizeof(int)<=4)
下面的代码导致:
0.000000
10
在这种情况下,'data' 返回了什么?我知道 n.data.idata
和 n.data.fdata
是正确的用法,我只是好奇为什么整数值在这种情况下有效而浮点值无效。
#include<stdio.h>
#include<stdlib.h>
typedef struct node
{
union container
{
int idata;
float fdata;
} data;
struct node *next;
} Node;
int main()
{
Node i = {.data.idata = 10};
Node n = {.data.fdata = 10.0};
printf("%f\n", n.data);
printf("%d\n", i.data);
printf("\nExiting program...\n");
return 0;
}
对于我原来回答错误的问题表示歉意。我觉得这个答案 可能有助于向刚接触 C 的新手回答这个问题,说明 2501 在说什么(强调我的)
Specifier f assumes a default argument promotion from float to double, which doesn't happen in this case, because a union data is passed to the function. So the function receives the union data which consists of 4 bytes and represents a float, but tries to print 8 bytes, because it expects a double. The result is a nonsense value, in your case 0.0.
类型提升将一种二进制表示安全地或至少转换为另一种 以标准化的方式。以下都是在x86机器上完成的
无符号 64 位格式 10
的二进制值
|01010000|00000000|00000000|00000000|00000000|00000000|00000000|00000000
作为 32 位浮点数的二进制值 10...
|00000000|00000000|00000100|10000010
64 位格式的双精度值 10...
|00000000|00000000|00000000|00000000|00000000|00000000|00100100|00000010
数字 10 的所有三种表示形式都有很大不同。类型提升
没有发生意味着 printf
语句中的数字被视为
double 并且它是这样打印的。在这种情况下,它显示为的唯一原因
零是精确度。
下面代码中的Node
数据结构是8个字节。没有填充
在结构中或者它不能是 8 个字节长。如果我们将 float
设置为 10 并且 next
到 0 我们在内存中有这个表示...
|00000000|00000000|00000100|10000010|00000000|00000000|00000000|00000000
上面如果转换成double并用printf("%f'\n");
打印就可以了
看起来像 0.0
。如果你使用 'printf("%g\n")' 打印它,你会看到一些
位必须设置在 8 个字节中,在我的例子中我得到
5.39824e-315
看看浮动 class 可以使用什么 fpclassify
。请参阅下面的代码...
#include <assert.h>
#include <float.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct node {
union container {
float fdata;
int idata;
} data;
uint32_t next;
} Node;
int main(void) {
size_t nsize1 = sizeof(Node);
assert(nsize1 == 8);
assert(sizeof(int) == 4);
assert(sizeof(float) == 4);
assert(sizeof(double) == 8);
Node n = {.data.fdata = 10.0};
n.next = 0;
double d = *(double*)&n;
int class_of_d = fpclassify(d);
assert(d > 0);
switch(class_of_d) {
case FP_NAN : printf("FP_NAN\n");break;
case FP_INFINITE : printf("FP_INFINITE\n");break;
case FP_ZERO : printf("FP_ZERO\n");break;
case FP_NORMAL : printf("FP_NORMAL\n");break;
case FP_SUBNORMAL : printf("FP_SUBNORMAL\n");break;
case FP_SUPERNORMAL: printf("FP_SUPERNORMAL\n");break;
}
printf("%g\n", d);
return 0;
}
让我们暂时忽略明显的未定义行为,这是因为将不正确的类型传递给说明符 f
的 printf 函数。该类型是匿名联合。
说明符 f
假定默认参数从 float 提升为 double,在这种情况下不会发生这种情况,因为联合 data
被传递给函数。因此函数接收由 4 个字节组成并表示浮点数的联合 data
,但尝试打印 8 个字节,因为它需要一个双精度数。结果是一个无意义的值,在你的例子中是 0.0。
(此答案假设 IEEE 754,并且 sizeof(int)<=4)