Tre*_*key 86 c c++ printf undefined-behavior implicit-conversion
该声明
printf("%f\n",0.0f);
Run Code Online (Sandbox Code Playgroud)
打印0.
但是,声明
printf("%f\n",0);
Run Code Online (Sandbox Code Playgroud)
打印随机值.
我意识到我表现出某种未定义的行为,但我无法弄明白为什么具体.
所有位都为0的浮点值仍然有效float,值为0.
float并且int在我的机器上具有相同的大小(如果它甚至相关).
为什么使用整数文字而不是浮点文字printf会导致此行为?
如果我使用PS,可以看到相同的行为
int i = 0;
printf("%f\n", i);
Run Code Online (Sandbox Code Playgroud)
Kei*_*son 119
的"%f"格式需要类型的参数double.你给它一个类型的参数int.这就是行为未定义的原因.
该标准并不能保证所有位归零是一个有效的表示0.0(尽管它经常是),或任何的double价值,或者说int并double具有相同的尺寸(记住它的double,不是float),或者,即使是相同的大小,它们以相同的方式作为参数传递给可变参数函数.
它可能恰好在您的系统上"工作".这是未定义行为的最坏可能症状,因为它使诊断错误变得困难.
N1570 7.21.6.1第9段:
...如果任何参数不是相应转换规范的正确类型,则行为未定义.
类型的参数float被提升为double,这就是为什么printf("%f\n",0.0f)有效.整数类型的窄参数比int被提升到int或unsigned int.这些促销规则(由N1570 6.5.2.2第6段规定)对此情况没有帮助printf("%f\n", 0).
zwo*_*wol 58
首先,正如其他几个答案所述,但在我看来,并没有明确说明:它在大多数情况下提供一个整数,其中库函数采用double或float参数.编译器将自动插入转换.例如,sqrt(0)定义明确并且行为完全sqrt((double)0)如此,对于那里使用的任何其他整数类型表达式也是如此.
printf是不同的.它是不同的,因为它需要可变数量的参数.它的功能原型是
extern int printf(const char *fmt, ...);
Run Code Online (Sandbox Code Playgroud)
因此,当你写
printf(message, 0);
Run Code Online (Sandbox Code Playgroud)
编译器不具有什么类型的任何信息,printf 预计这第二个参数是.它只有参数表达式的类型,也就是说int.因此,与大多数库函数不同,程序员可以确保参数列表与格式字符串的期望相匹配.
(现代编译器可以查看格式字符串并告诉您类型不匹配,但他们不会开始插入转换来实现您的意思,因为当您注意到时,您的代码现在应该更好地破解,多年后用一个不太有用的编译器重建.)
现在,问题的另一半是:给定(int)0和(float)0.0,在大多数现代系统中,都表示为32位,所有这些都是零,为什么它不能正常工作呢?C标准只是说"这不是必须工作的,你是独立的",但是让我说出为什么它不起作用的两个最常见的原因; 这可能会帮助你理解为什么它不是必需的.
首先,由于历史的原因,当你传递一个float通过可变参数列表,它被提拔到double,这在大多数现代系统,是64个位宽.因此printf("%f", 0),只有32个零位传递给被调用者,期望64个.
第二个同样重要的原因是浮点函数参数可以在不同于整数参数的位置传递.例如,大多数CPU具有用于整数和浮点值的单独寄存器文件,因此,如果它们是整数,则参数0到4可以进入寄存器r0到r4,如果它们是浮点,则可以是f0到f4.因此,printf("%f", 0)在寄存器f1中查找该零,但它根本不存在.
πάν*_*ῥεῖ 13
为什么使用整数文字而不是浮点文字导致此行为?
因为printf()除了const char* formatstring第一个之外没有类型参数.它使用了c风格的省略号(...).
它只是根据格式字符串中给出的格式类型决定如何解释传递给那里的值.
你会遇到与尝试时相同的未定义行为
int i = 0;
const double* pf = (const double*)(&i);
printf("%f\n",*pf); // dereferencing the pointer is UB
Run Code Online (Sandbox Code Playgroud)
Mar*_*som 13
通常当你调用一个期望a的函数double但是你提供的函数时,int编译器会自动转换为a double.这种情况不会发生printf,因为函数原型中没有指定参数的类型 - 编译器不知道应该应用转换.
wyr*_*yrm 10
这是从编译器警告中学习的好机会之一.
$ gcc -Wall -Wextra -pedantic fnord.c
fnord.c: In function ‘main’:
fnord.c:8:2: warning: format ‘%f’ expects argument of type ‘double’, but argument 2 has type ‘int’ [-Wformat=]
printf("%f\n",0);
^
Run Code Online (Sandbox Code Playgroud)
要么
$ clang -Weverything -pedantic fnord.c
fnord.c:8:16: warning: format specifies type 'double' but the argument has type 'int' [-Wformat]
printf("%f\n",0);
~~ ^
%d
1 warning generated.
Run Code Online (Sandbox Code Playgroud)
因此,printf产生未定义的行为是因为您传递的是不兼容的参数类型.
我不确定是什么令人困惑.
你的格式字符串需要一个double; 你代之以提供int.
这两种类型具有相同的位宽是完全不相关的,除了它可以帮助您避免从这样的破坏代码中获取硬内存冲突异常.
| 归档时间: |
|
| 查看次数: |
6931 次 |
| 最近记录: |