使用printf将指针转为double时出现异常行为的原因排查
未解引用double指针用printf("%f")打印时,输出受之前printf的double值影响的原因
原本想演示忘记解引用double指针的后果:用printf("%f", pd)(而非*pd)打印double *pd类型指针,预期得到指针地址对应的double值,却发现之前打印的任意double值都会影响该未解引用指针的输出结果。
示例程序
#include <stdio.h> int main() { double d = 123.456, *pd = &d; printf("%f ", *pd); // 第一次打印 printf("%f\n", pd); return 0; }
不同场景的输出
- 正常运行输出:
123.456000 123.456000
- 注释第一次打印后的输出:
0.000000
- 将第一次打印改为
printf("%f ", 555.555);后的输出:
555.555000 555.555000
运行环境:Ubuntu 22.04,GCC版本11.4.0,编译命令:
$ gcc -O0 example.c -o example
原因分析
这是由x86_64架构下的可变参数函数调用约定和浮点数传递规则导致的:
- 在x86_64系统中,浮点数参数默认通过SSE寄存器(如
%xmm0、%xmm1)传递,而整数、指针这类参数通过通用寄存器(如%rdi、%rsi)传递。 printf是可变参数函数,它完全依赖格式字符串来解析参数来源。当你用printf("%f", pd)时,%f告诉printf要从SSE寄存器中读取double值,但实际上你传递的指针pd是放在通用寄存器里的,printf找不到对应SSE寄存器的有效数据,就会读取之前留在SSE寄存器里的残留值——也就是上一次printf调用传入的double值。- 若注释掉第一次打印,程序运行到该
printf前未使用SSE寄存器传递浮点数,寄存器初始值为0,因此输出0.000000。
你误以为指针和double都是8字节就会被直接当作double读取,但核心问题是调用约定区分了不同类型参数的传递通道,printf只会按格式字符串指定的通道去取值,而非直接从内存或通用寄存器中读取字节解析。
内容的提问来源于stack exchange,提问作者traveh
相关产品推荐
相关产品推荐

