printf 对参数的顺序敏感吗?
Is printf sensitive to the order of arguments?
我想在 c++
中使用 printf 打印一行
int t0 = time(NULL);
int outIndx = 99736;
printf ("time using recurision = %d secs, the result is = %d\n",(time(NULL) - t0), outIndx);
在这种情况下,printf 的输出是:
使用递归的时间 = 0 秒,结果为 = 0
但是当颠倒outIndx和(time(NULL) - t0)的顺序时:
printf ("time using recurision = %d secs, the result is = %d\n", outIndx,(time(NULL) - t0));
printf 的输出被正确打印:
使用递归的时间 = 99736 秒,结果为 = 0
为什么会这样?
time
returns time_t
不必与 int
大小相同。在这种情况下,%d
不是正确的输出格式。
原问题:"Is printf sensitive to the order of arguments?"
函数参数的顺序在标准中没有定义,而是由编译器使用的调用约定决定的。假设您正在使用 cdecl 调用约定(许多 C 编译器将其用于 x86 体系结构),其中函数中的参数从 从右到左计算。
举个例子
int i=5;
printf("%d%d%d%d%d%d",i++,i--,++i,--i,i);
Output :45545
这是因为参数是从右到左求值的。
但是,在您的示例中,您尝试打印类型为 "time_t" 的时间,如下所示:
typedef __time_t time_t;
time_t 数据类型是 ISO C 库中定义用于存储系统时间值的数据类型。这些值从标准 time() 库函数返回。此类型是标准 header 中定义的 typedef。 ISO C 将 time_t 定义为算术类型,但未为其指定任何特定类型、范围、分辨率或编码。同样未指定的是应用于时间值的算术运算的含义。
是否以整数形式实现取决于底层架构。就你的例子而言,我相信它至少没有实现为 "unsigned int " (int t0),所以结果是 implementation-defined.
A C 溶液
一般来说,显示time_t
is to break down its components to a struct tm
using gmtime()
or localtime()
and display those or convert them as desired with strftime()
, or ctime()
的值的方式是直接从time_t
到显示本地时间的字符串。
如果出于某种目的想要查看原始值,C 标准规定 time_t
是实数,这意味着它是整数或浮点数 (C 2011 (N1570) 6.2.5 17) .因此,您应该使用 difftime()
将时差转换为双倍:
#include <ctime>
#include <cstdio>
void process()
{
static unsigned dummy = 0;
for (size_t i = 0 ; i < 1000000000 ; ++i)
{
dummy += (dummy + i) / (dummy * i + 1);
}
}
int main()
{
const time_t t0 = time(NULL);
process(); // let say it can take up to a few hours
const time_t t_end = time(NULL);
struct tm breakdown_time = { 0 };
breakdown_time.tm_sec = difftime(t_end, t0);
(void) mktime(&breakdown_time);
printf("duration: %.2d:%.2d:%.2d\n", breakdown_time.tm_hour, breakdown_time.tm_min, breakdown_time.tm_sec);
// Output: "duration: 00:00:08"
}
C++ 解决方案
但是您不是在使用 C++ 吗? std::clock()
提供了一种计算 CPU 持续时间的方法(CPU 花费的时间,不包括它在其他线程上花费的时间):
#include <iostream>
#include <iomanip>
void process()
{
static unsigned dummy = 0;
for (size_t i = 0 ; i < 1000000000 ; ++i)
{
dummy += (dummy + i) / (dummy * i + 1);
}
}
int main() {
const std::clock_t t0 = std::clock();
process(); // business code that take time to run
const std::clock_t t_end = std::clock();
const double duration = static_cast<double>(t_end - t0) / CLOCKS_PER_SEC;
std::cout << "duration: " << std::setprecision(3) << duration << " s" << std::endl;
// Output: "duration: 8.92 s"
}
C++14 解决方案
我偶然发现了一种计算实际持续时间的优雅、通用的方法,它在 C++14 中,并使用了 std::chrono
中的所有高级工具:
#include <iostream>
#include <iomanip>
#include <chrono>
void process()
{
static unsigned dummy = 0;
for (size_t i = 0 ; i < 100000000 ; ++i)
{
dummy += (dummy + i) / (dummy * i + 1);
}
}
template<typename TimeT = std::chrono::milliseconds>
struct measure
{
template<typename F, typename ...Args>
static typename TimeT::rep execution(F&& func, Args&&... args)
{
auto start = std::chrono::system_clock::now();
std::forward<decltype(func)>(func)(std::forward<Args>(args)...);
auto duration = std::chrono::duration_cast< TimeT>
(std::chrono::system_clock::now() - start);
return duration.count();
}
};
int main() {
std::cout << "duration: " << measure<>::execution(process) << " ms" << std::endl;
// Output: "duration: 707 ms"
}
传递给 printf(可变参数函数)的所有参数在调用时被压入堆栈。 printf 例程根据第一个参数中连续提供的 %
标识符弹出参数。取决于机器和编译器,参数在堆栈上的顺序(从上到下或从下到上)可能会有所不同。
在你的例子中,当你首先传递 (time(NULL) - t0)
时,因为它是 time_t 类型并且它的大小与 printf 通过查看 %d
解释的不同。它会产生问题。
现在为什么当您将 (time(NULL) - t0
) 作为第二个参数传递时它会起作用:
同样的逻辑,假设 timt_t 在你的情况下是 8 个字节,而 int 是 4 个字节。对于每个 %d
printf 将从参数堆栈中消耗 4 个字节。因此,当您首先打印 outIdx(int)
时,printf 从对应于 outIdx 的堆栈中获取正确的 4 个字节。当 printf 看到第二个 %d
时,它将使用 4 个下一个字节,这是 time_t 对象的 8 个字节的一部分。并且根据 time_t 的值、系统的字节顺序,print 将从堆栈中打印 4 个字节(共 8 个字节),假设它是 int。
当 print 提供了错误的 %
标识符时,您最终可能会弄乱其堆栈排列,并可能会出现不正确或未定义的行为。
我想在 c++
中使用 printf 打印一行 int t0 = time(NULL);
int outIndx = 99736;
printf ("time using recurision = %d secs, the result is = %d\n",(time(NULL) - t0), outIndx);
在这种情况下,printf 的输出是:
使用递归的时间 = 0 秒,结果为 = 0
但是当颠倒outIndx和(time(NULL) - t0)的顺序时:
printf ("time using recurision = %d secs, the result is = %d\n", outIndx,(time(NULL) - t0));
printf 的输出被正确打印:
使用递归的时间 = 99736 秒,结果为 = 0
为什么会这样?
time
returns time_t
不必与 int
大小相同。在这种情况下,%d
不是正确的输出格式。
原问题:"Is printf sensitive to the order of arguments?"
函数参数的顺序在标准中没有定义,而是由编译器使用的调用约定决定的。假设您正在使用 cdecl 调用约定(许多 C 编译器将其用于 x86 体系结构),其中函数中的参数从 从右到左计算。
举个例子
int i=5;
printf("%d%d%d%d%d%d",i++,i--,++i,--i,i);
Output :45545
这是因为参数是从右到左求值的。
但是,在您的示例中,您尝试打印类型为 "time_t" 的时间,如下所示:
typedef __time_t time_t;
time_t 数据类型是 ISO C 库中定义用于存储系统时间值的数据类型。这些值从标准 time() 库函数返回。此类型是标准 header 中定义的 typedef。 ISO C 将 time_t 定义为算术类型,但未为其指定任何特定类型、范围、分辨率或编码。同样未指定的是应用于时间值的算术运算的含义。
是否以整数形式实现取决于底层架构。就你的例子而言,我相信它至少没有实现为 "unsigned int " (int t0),所以结果是 implementation-defined.
A C 溶液
一般来说,显示time_t
is to break down its components to a struct tm
using gmtime()
or localtime()
and display those or convert them as desired with strftime()
, or ctime()
的值的方式是直接从time_t
到显示本地时间的字符串。
如果出于某种目的想要查看原始值,C 标准规定 time_t
是实数,这意味着它是整数或浮点数 (C 2011 (N1570) 6.2.5 17) .因此,您应该使用 difftime()
将时差转换为双倍:
#include <ctime>
#include <cstdio>
void process()
{
static unsigned dummy = 0;
for (size_t i = 0 ; i < 1000000000 ; ++i)
{
dummy += (dummy + i) / (dummy * i + 1);
}
}
int main()
{
const time_t t0 = time(NULL);
process(); // let say it can take up to a few hours
const time_t t_end = time(NULL);
struct tm breakdown_time = { 0 };
breakdown_time.tm_sec = difftime(t_end, t0);
(void) mktime(&breakdown_time);
printf("duration: %.2d:%.2d:%.2d\n", breakdown_time.tm_hour, breakdown_time.tm_min, breakdown_time.tm_sec);
// Output: "duration: 00:00:08"
}
C++ 解决方案
但是您不是在使用 C++ 吗? std::clock()
提供了一种计算 CPU 持续时间的方法(CPU 花费的时间,不包括它在其他线程上花费的时间):
#include <iostream>
#include <iomanip>
void process()
{
static unsigned dummy = 0;
for (size_t i = 0 ; i < 1000000000 ; ++i)
{
dummy += (dummy + i) / (dummy * i + 1);
}
}
int main() {
const std::clock_t t0 = std::clock();
process(); // business code that take time to run
const std::clock_t t_end = std::clock();
const double duration = static_cast<double>(t_end - t0) / CLOCKS_PER_SEC;
std::cout << "duration: " << std::setprecision(3) << duration << " s" << std::endl;
// Output: "duration: 8.92 s"
}
C++14 解决方案
我偶然发现了一种计算实际持续时间的优雅、通用的方法,它在 C++14 中,并使用了 std::chrono
中的所有高级工具:
#include <iostream>
#include <iomanip>
#include <chrono>
void process()
{
static unsigned dummy = 0;
for (size_t i = 0 ; i < 100000000 ; ++i)
{
dummy += (dummy + i) / (dummy * i + 1);
}
}
template<typename TimeT = std::chrono::milliseconds>
struct measure
{
template<typename F, typename ...Args>
static typename TimeT::rep execution(F&& func, Args&&... args)
{
auto start = std::chrono::system_clock::now();
std::forward<decltype(func)>(func)(std::forward<Args>(args)...);
auto duration = std::chrono::duration_cast< TimeT>
(std::chrono::system_clock::now() - start);
return duration.count();
}
};
int main() {
std::cout << "duration: " << measure<>::execution(process) << " ms" << std::endl;
// Output: "duration: 707 ms"
}
传递给 printf(可变参数函数)的所有参数在调用时被压入堆栈。 printf 例程根据第一个参数中连续提供的 %
标识符弹出参数。取决于机器和编译器,参数在堆栈上的顺序(从上到下或从下到上)可能会有所不同。
在你的例子中,当你首先传递 (time(NULL) - t0)
时,因为它是 time_t 类型并且它的大小与 printf 通过查看 %d
解释的不同。它会产生问题。
现在为什么当您将 (time(NULL) - t0
) 作为第二个参数传递时它会起作用:
同样的逻辑,假设 timt_t 在你的情况下是 8 个字节,而 int 是 4 个字节。对于每个 %d
printf 将从参数堆栈中消耗 4 个字节。因此,当您首先打印 outIdx(int)
时,printf 从对应于 outIdx 的堆栈中获取正确的 4 个字节。当 printf 看到第二个 %d
时,它将使用 4 个下一个字节,这是 time_t 对象的 8 个字节的一部分。并且根据 time_t 的值、系统的字节顺序,print 将从堆栈中打印 4 个字节(共 8 个字节),假设它是 int。
当 print 提供了错误的 %
标识符时,您最终可能会弄乱其堆栈排列,并可能会出现不正确或未定义的行为。