GetTickCount() 是一个在 Windows 平台上广泛使用的 API 函数,它返回自 Windows 操作系统启动以来经过的毫秒数,它常用于程序中测量代码执行时间、实现简单的定时器或延时功能。

函数原型
GetTickCount() 的函数原型非常简单,定义在 <Windows.h> 头文件中:
DWORD GetTickCount(void);
- 返回值类型:
DWORD,这是一个 32 位无符号整数(unsigned long)。 - 返回值: 自系统启动后经过的毫秒数。
工作原理与精度
GetTickCount() 的值是系统启动后不断累加的一个计数器,其计数频率与系统的定时器中断频率有关,通常是 10 毫秒或 15.6 毫秒更新一次,它的精度并不高,通常在 10-16 毫秒左右。
重要问题:32位整数溢出
DWORD 是一个 32 位无符号整数,它的最大值是 2^32 - 1,即 4,294,967,295。

- 如果系统以 15.625 毫秒/次的速度更新,那么大约
4,294,967,295 / 1000 / 60 / 60 / 24 ≈ 49.7天后会溢出。 - 如果系统以 10 毫秒/次的速度更新,那么大约
4,294,967,295 / 1000 / 60 / 60 / 24 ≈ 49.7天后也会溢出。
溢出后会发生什么?
溢出后,GetTickCount() 的值会从 0xFFFFFFFF(约 49.7 天)回绕到 0,如果你的程序长时间运行(超过 49.7 天),或者你用它来测量一个跨越了溢出点的时长,直接进行减法运算会导致结果错误。
示例:错误的计算
// 假设系统已经运行了 50 天,GetTickCount() 已经回绕到 1000 DWORD start = 1000; // ... 程序运行了 10 分钟 (600,000 毫秒) // 实际的TickCount应该是 1000 + 600000 = 601000 // 但由于溢出,实际返回的值可能是一个很小的数,500000 DWORD end = 500000; // 假设溢出后是这个值 // 错误的计算 DWORD elapsed = end - start; // 500000 - 1000 = 499000 (结果完全错误)
如何正确使用(避免溢出问题)
为了正确计算时间差,即使发生了溢出,我们需要一个特殊的技巧:将 DWORD 当作一个环形计数器来处理。

当 end 小于 start 时,意味着发生了溢出,正确的计算方法是:
elapsed = (MAX_DWORD - start) + end + 1
或者,利用 C 语言中无符号整数溢出会自动回绕的特性,可以直接用减法,结果也是正确的:
elapsed = end - start; (当 end 和 start 都是 DWORD 类型时)
正确计算时间差的函数示例:
#include <stdio.h>
#include <Windows.h>
// 计算两个 GetTickCount() 值之间经过的毫秒数(正确处理溢出)
DWORD GetElapsedMillis(DWORD start, DWORD end) {
// 因为 DWORD 是无符号整数,当 end < start 时,end - start 会自动计算
// 出 (MAX_DWORD - start) + end 的正确结果,无需手动判断。
return end - start;
}
int main() {
DWORD start, end, elapsed;
start = GetTickCount();
// 模拟一段耗时操作
Sleep(500); // 暂停 500 毫秒
end = GetTickCount();
elapsed = GetElapsedMillis(start, end);
printf("开始时间戳: %u\n", start);
printf("结束时间戳: %u\n", end);
printf("经过时间: %u 毫秒\n", elapsed);
return 0;
}
优点与缺点
优点:
- 简单易用: 只需调用一个函数即可,非常方便。
- 开销小: 它是一个轻量级的系统调用,性能开销非常低。
- 兼容性好: 从古老的 Windows 95 到最新的 Windows 11 都支持。
缺点:
- 精度较低: 精度通常在 10-16 毫秒,对于需要高精度计时的场景(如游戏、物理模拟)不适用。
- 存在溢出问题: 如上所述,需要特殊处理才能跨越溢出点正确计算时间。
替代方案
如果你需要更高精度或希望避免 GetTickCount() 的溢出问题,可以使用以下更现代的 Windows API:
QueryPerformanceCounter (高精度计时器)
这是在 Windows 上进行性能计时的黄金标准。
QueryPerformanceCounter(): 获取一个高分辨率的性能计数器的值,这个值本身没有特定的时间单位,只是一个数字。QueryPerformanceFrequency(): 获取性能计数器的频率,即每秒多少次,这个频率是固定的,通常在 1MHz 到 50MHz 之间,非常高。
使用方法:
- 调用
QueryPerformanceFrequency()获取频率freq。 - 在计时开始时调用
QueryPerformanceCounter()获取起始计数start_count。 - 在计时结束时调用
QueryPerformanceCounter()获取结束计数end_count。 - 计算经过的秒数:
elapsed_seconds = (double)(end_count - start_count) / (double)freq。
优点:
- 精度极高,可以达到微秒(μs)甚至纳秒级别。
- 不会溢出:
LARGE_INTEGER类型(通常是 64 位)非常大,溢出需要极长的时间(数千年),可以忽略不计。
缺点:
- 相对
GetTickCount()稍微复杂一些。
GetTickCount64() (64位版本)
这是 GetTickCount() 的 64 位版本,直接解决了溢出问题。
- 函数原型:
ULONGLONG GetTickCount64(void); - 返回值: 一个 64 位无符号整数,表示自系统启动以来的毫秒数。
优点:
- 解决了溢出问题:64 位的数值极大,溢出需要数万年,对于任何应用程序来说都足够了。
- 使用方式与
GetTickCount()完全相同,非常简单。
缺点:
- 仅在 Windows Vista 及更高版本 中可用。
总结与建议
| 特性 | GetTickCount() |
GetTickCount64() |
QueryPerformanceCounter() |
|---|---|---|---|
| 精度 | 低 (~15ms) | 低 (~15ms) | 极高 (μs级) |
| 返回值类型 | DWORD (32位) |
ULONGLONG (64位) |
LARGE_INTEGER (64位) |
| 溢出问题 | 有 (约49.7天) | 无 (数万年) | 无 (数千年) |
| 系统要求 | 所有Windows版本 | Windows Vista+ | 所有Windows版本 |
| 使用复杂度 | 非常简单 | 非常简单 | 较复杂 |
如何选择?
- 追求极致简单,且程序运行时间远小于49.7天:可以使用
GetTickCount(),但必须记得使用无符号整数减法来处理时间差。 - 使用 Windows Vista 或更高版本,且希望简单避免溢出问题:强烈推荐使用
GetTickCount64(),它是GetTickCount()的完美现代替代品。 - 需要高精度计时(游戏、基准测试等):必须使用
QueryPerformanceCounter(),它是性能测量的行业标准。 - 编写需要跨平台(如Linux/macOS)的代码:
GetTickCount()是 Windows 特有的,在 Linux 上,你可以使用clock_gettime();在 macOS 上,可以使用clock_gettime()或mach_absolute_time()。QueryPerformanceCounter()也是 Windows 特有的。
