printf函数是否对参数的顺序敏感?

3
我想使用中的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。

这是为什么呢?


1
你知道time的正常分辨率是秒吗?所以,如果你测量的时间少于一秒(即使是0.99秒),结果仍将为零。 - Some programmer dude
当然,它对参数的顺序非常敏感,而且man页面也是这么说的。否则它将完全无用。 - user207421
@JoachimPileborg的意思是,如果我想要捕捉小于一秒的时间,我必须使用float类型吗?我尝试了这个方法但没有成功。 - Fadwa
1
@Misaki 不,你必须使用一些其他的函数来获得更高的分辨率,例如gettimeofdayclock(但请注意,在Windows中使用Visual Studio编译器时,clock函数的行为方式是非标准的,尽管它仍然可以使用)。 - Some programmer dude
4个回答

2

time返回一个time_t,它的大小不一定与int相同。在这种情况下,%d不是正确的输出格式。


好的,但是为什么这会影响outIndx的打印呢? - Fadwa
2
一旦您有了不正确的格式,结果在技术上是未定义的。实际上,一个更大的元素会导致下一个元素处于printf找不到的位置。 - Bo Persson
6
由于传递不符合格式说明符的参数,您会得到“未定义行为”(UB)。您观察到的奇怪行为通常是代码中UB的迹象。请注意,UB可能导致意外和无法预测的结果。 - juanchopanza
我可以用什么来打印这种情况下的时间呢?我尝试使用%lld来表示时间,它起作用了。但我不明白为什么 :D - Fadwa
1
@Misaki - 这有点复杂,取决于你的系统。如果你相信在你的系统中 time_t 可以适应一个 int,那么你可以执行 int t1 = time(NULL) - t0; 并打印 t1 - Bo Persson

1

原始问题:“printf对参数的顺序敏感吗?”

函数参数的顺序在标准中未定义,由编译器使用的调用约定决定。假设您正在使用 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()库函数返回。该类型是在标准头文件中定义的typedef。ISO C将time_t定义为算术类型,但没有指定任何特定的类型、范围、分辨率或编码方式。应用于时间值的算术运算的含义也未指定。
它是否以整数实现取决于底层架构。就您的示例而言,我认为它至少不是以“unsigned int”实现的(int t0),因此结果是实现定义的。

你在这里混淆了几个不同的主题,只有其中一个与问题相关。 - Paul R

1

一种C语言解决方案

通常,展示time_t的值的方法是使用gmtime()localtime()将其分解为struct tm的组件,并显示这些组件或使用strftime()按需转换它们,或使用ctime()直接从time_t转换为显示本地时间的字符串。

如果您想为某些目的查看原始值,C标准规定time_t是实数,这意味着它是整数或浮点数(C 2011 (N1570) 6.2.5 17)。因此,您应该使用difftime()将时间差转换为double:
#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"
}

演示


你认为使用 GetTickCount() 怎么样? - Fadwa
2
@Misaki 仅限于 Windows 的函数 ;) - YSC

1

printf(可变参数函数)传递给它的所有参数都在调用时推送到堆栈中。 printf例程根据其第一个参数中提供的%标识符顺序弹出参数。根据机器和编译器,堆栈上的参数顺序(自上而下或自下而上)可能会有所不同。

在您的情况下,当您首先传递(time(NULL) - t0)时,由于它的类型为time_t且其大小与printf看到的%d不同,这会导致问题。

现在为什么在将(time(NULL) - t0)作为第二个参数传递时它能正常工作:

再次应用相同的逻辑,假设您的timt_t大小为8字节,而int大小为4字节。对于每个%d,printf将从参数堆栈中消耗4个字节。因此,当您首先打印outIdx(int)时,printf从堆栈中获取正确的4个字节,对应于outIdx。当printf看到第二个%d时,它将使用接下来的4个字节,这是time_t对象的8个字节中的一部分。并且根据time_t的值,系统的字节序,print将从堆栈中打印4个字节(8个字节中的一部分),并将其视为int。

当提供错误的%标识符给打印函数时,可能会导致堆栈排列混乱,并可能得到不正确或未定义的行为。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接