timeGetTime在C语言中如何精确计时?

99ANYc3cd6
预计阅读时长 15 分钟
位置: 首页 C语言 正文

这是一个在 Windows 平台下非常常用的多媒体定时器函数。

c语言 timegettime
(图片来源网络,侵删)

timeGetTime 是什么?

timeGetTime 是 Windows API (Application Programming Interface) 中的一个函数,它属于 Winmm.dll (Windows Multimedia Library),它的主要作用是获取系统运行以来经过的毫秒数。

函数原型:

DWORD timeGetTime(void);

返回值:

  • 返回一个 DWORD 类型的值,表示从系统启动到当前时刻所经过的毫秒数
  • DWORD 是一个 32 位无符号整数,最大值可以表示到 4,294,967,295 毫秒。

核心特点与局限性

理解 timeGetTime 的关键在于它的几个重要特性:

c语言 timegettime
(图片来源网络,侵删)

a. 高精度 (相对于 GetTickCount)

timeGetTime 通常比标准的 GetTickCount 具有更高的分辨率(虽然在高精度定时器可用的现代系统上,GetTickCount64QueryPerformanceCounter 更优)。

b. 系统重启后归零

它的计时起点是系统启动的时间,每次电脑重启后,这个计数都会从零重新开始,这意味着它不适合用来测量跨天、跨周或跨月的绝对时间。

c. 32 位回绕问题

这是 timeGetTime重要的局限性,由于它返回的是一个 32 位无符号整数,它的最大值是 2³² - 1 = 4,294,967,295 毫秒。

我们来计算一下这个值代表多少时间: 4,294,967,295 毫秒 ≈ 4,294,967 秒 ≈ 71,581 分钟 ≈ 1,193 小时 ≈ 7 天

c语言 timegettime
(图片来源网络,侵删)

这意味着,如果你的电脑连续运行超过 49.7 天不重启timeGetTime 的返回值就会从 0xFFFFFFFF 突然回绕到 0x00000000,如果你在回绕前后分别调用它来计算时间差,就会得到一个完全错误的巨大负数(在无符号整数运算下会变成一个巨大的正数)。

d. 不受系统时间影响

GetSystemTimeGetLocalTime 不同,timeGetTime 的值不会因为用户手动修改了系统时间(比如从 10:00 改成 12:00)而受到影响,它只关心系统启动后的真实流逝时间。


如何正确使用 timeGetTime

尽管有回绕问题,timeGetTime 在测量短时间间隔(比如游戏帧率、代码执行时间)时仍然非常方便。

基本用法示例:测量代码执行时间

#include <stdio.h>
#include <windows.h> // 必须包含此头文件
int main() {
    // 1. 记录开始时间
    DWORD startTime = timeGetTime();
    // 2. 模拟一段耗时操作
    // 一个简单的循环
    volatile int sum = 0;
    for (int i = 0; i < 100000000; ++i) {
        sum += i;
    }
    // 3. 记录结束时间
    DWORD endTime = timeGetTime();
    // 4. 计算耗时
    DWORD elapsedTime = endTime - startTime;
    printf("代码执行耗时: %d 毫秒\n", elapsedTime);
    return 0;
}

编译说明: 在使用 Visual Studio 编译时,通常不需要特殊设置。 如果使用 MinGW (GCC) 在命令行编译,需要链接 winmm 库:

gcc your_program.c -o your_program.exe -lwinmm

如何安全地处理回绕问题?

当测量时间可能超过 49 天时,必须处理回绕,一个常用的技巧是使用 if 语句判断差值是否合理。

#include <stdio.h>
#include <windows.h>
// 一个安全的函数,用于计算两个 timeGetTime 之间的时间差
DWORD getSafeTimeDifference(DWORD start, DWORD end) {
    // end >= start,说明没有发生回绕,直接相减即可
    if (end >= start) {
        return end - start;
    } 
    // end < start,说明发生了回绕(end 已经回绕到 0 了)
    // 总时间 = (最大可能值 - start) + end
    else {
        return (0xFFFFFFFF - start) + end + 1; // +1 是因为从 start 到 0xFFFFFFFF 是 (0xFFFFFFFF - start + 1) 个单位
    }
}
int main() {
    DWORD t1 = timeGetTime();
    Sleep(500); // 暂停 500 毫秒
    DWORD t2 = timeGetTime();
    // 错误的方式(在正常情况下没问题)
    // DWORD diff = t2 - t1; 
    // 正确且安全的方式
    DWORD safeDiff = getSafeTimeDifference(t1, t2);
    printf("经过时间 (不安全计算): %u 毫秒\n", t2 - t1);
    printf("经过时间 (安全计算): %u 毫秒\n", safeDiff);
    return 0;
}

更简洁的写法是利用 DWORD 是无符号整数的特性,直接相减,在无符号整数运算中,a - b (当 a < b 时) 会自动计算出正确的回绕后的差值。 上面的 getSafeTimeDifference 函数可以简化为:

DWORD getSafeTimeDifference(DWORD start, DWORD end) {
    // 对于无符号整数,当 end < start 时,end - start 会自动回绕并计算出正确值
    return end - start; 
}

注意:这个简化版本只在你确定差值不会超过 DWORD 的最大值(即不会超过 49.7 天)时才完全正确,如果差值本身就可能超过 49.7 天,那么任何基于 timeGetTime 的计算都是不安全的,必须换用其他方法。


timeGetTime 的替代方案

对于现代 Windows 应用程序,通常有更好的选择:

函数 头文件 返回值 优点 缺点
timeGetTime Mmsystem.h DWORD (毫秒) 简单,兼容性好 32位回绕 (~49天),精度有限
GetTickCount Windows.h DWORD (毫秒) 非常快,系统原生 32位回绕 (~49天),精度与 timeGetTime 相当或更低
GetTickCount64 Windows.h ULONGLONG (毫秒) 无回绕问题,原生支持 需要 Windows Vista 或更高版本
QueryPerformanceCounter Windows.h LARGE_INTEGER (高精度计数器) 极高精度,无回绕问题 速度稍慢,需要配合 QueryPerformanceFrequency 使用,频率因硬件而异

推荐方案:

  1. 对于绝大多数现代 Windows 应用程序

    • 首选 GetTickCount64,它解决了 timeGetTime 最致命的 32 位回绕问题,用法几乎一样,且是系统原生 API,效率高。
    • 次选 QueryPerformanceCounter,当你需要微秒(µs)甚至更高精度的计时,或者需要测量非常短的代码片段时(例如性能分析),这是最佳选择。
  2. 对于需要兼容旧版 Windows (如 XP) 的应用程序

    • 如果确定运行时间不会超过 49 天,timeGetTimeGetTickCount 仍然可用。
    • 如果需要运行时间超过 49 天,那么必须自己实现一个处理 GetTickCount 回绕的逻辑,或者使用 QueryPerformanceCounter
特性 描述
功能 获取系统启动后的毫秒数。
优点 使用简单,API 直观,兼容性好。
最大缺点 32位回绕,在系统连续运行约49.7天后会归零。
适用场景 短时间间隔测量(如游戏逻辑、简单性能测试),且确定运行时间不会超过回绕周期。
现代替代品 GetTickCount64 (解决回绕) 和 QueryPerformanceCounter (高精度)。

timeGetTime 是一个有用的老函数,但在编写新代码时,你应该优先考虑 GetTickCount64QueryPerformanceCounter 来获得更好的健壮性和精度。

-- 展开阅读全文 --
头像
织梦uploads权限如何正确配置?
« 上一篇 01-03
dede采集列表标题为图片连接
下一篇 » 01-03

相关文章

取消
微信二维码
支付宝二维码

目录[+]