C++无符号整数溢出探究
Posted 李迟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++无符号整数溢出探究相关的知识,希望对你有一定的参考价值。
本文主要探讨C/C++中无符号整数超过范围后的计算问题。
问题提出
nrf52832 的 SDK 中是没有时间戳获取的函数的,为了统计性能耗时,也为了向一些库提供时间戳(毫秒级别),需要自己利用定时器实现获取毫秒的接口。
nrf52832 是 32 位的,按毫秒计算,大概49天就会达到最大值,如何处理毫秒数值溢出后的情况,其实我是不懂的。看了些帖子,说在单独处理溢出反转的情形,但总觉得这样不太好,因此集中了一点时间,了解学习了无符号数的溢出(或说进位),并写了点代码测试。
设计思路
为了方便调试,本文用 32 位虚拟机 Linux 进行测试。用get_time
获取系统的秒数值,其值用g_ms
表示,开一线程time_handler
每秒累计一次时间数值,开另一线程myfunc_sleep
统计耗时。
需要注意的是,上面所述仅是模拟演示,旨在说明本质问题,并非实际使用的。
工程代码
首先简单测试无符号数的相加,函数如下:
// 测试无符号溢出后的差值 delta
void delta_test(int delta)
mytime_t start = 0xfffffffe;
/* 以ms为10为例,ent得到的结果为8
8 - start = 10
因此,即使溢出后,差值也是不变的
在延时函数中,即使时间戳溢出,也是无问题的。
*/
mytime_t end = start + delta;
mytime_t mydelta = end - start;
printf("end: %u start: %u delta: %u mydelta: %u\\n", end, start, delta, mydelta);
for (int i = 0; i < delta; i++)
printf("%u %d\\n", start, start);
start++;
测试代码:
delta_test(10);
其打印结果如下:
end: 8 start: 4294967294 delta: 10 mydelta: 10
4294967294 -2
4294967295 -1
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
起始数值为0xfffffffe
,即4294967294
,使用有符号打印,其值为-2
。当超过0xffffffff
则从0
开始计数。如果仅从结果看,可得到:8 - 4294967294 = 10
,与传递的参数一致。
下面给出多线程测试代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dlfcn.h>
#include <string.h>
#include <errno.h>
#include <thread>
typedef unsigned mytime_t;
static mytime_t g_start = 0xfffffff0; // == 4294967280
static mytime_t g_ms = g_start;
mytime_t t1 = 0;
mytime_t t2 = 0;
mytime_t get_time()
return g_ms;
void* time_handler(int a)
g_ms = g_start;
while (1)
g_ms++;
//printf("ms: %u\\n", g_ms);
sleep(1);
return NULL;
void* myfunc_sleep(int a)
int s = 2;
while (1)
t1 = get_time();
sleep(s);
t2 = get_time();
printf("after sleep %d s %u - %u = %d\\n", s, t2, t1, t2-t1);
return NULL;
int main(void)
#define TEST_THREAD_NUM 3
int threadnum = TEST_THREAD_NUM;
std::thread threads[TEST_THREAD_NUM];
int i = 0;
printf("test of unsigned overflow..\\n");
printf("sizeof: %d\\n", sizeof(mytime_t));
threads[i++] = std::thread(time_handler, 0);
threads[i++] = std::thread(myfunc_sleep, 0);
for (auto& t: threads)
if (t.joinable())
t.join();
return 0;
代码中定义的时间戳变量为mytime_t
,实际上是无符号类型unsigned
。另外也做了测试,在32位机器上使用uint8_t类型也可以得到正确值,但负数就无法打印出来了。
测试
当g_start
的值为0xfffffffe
时,测试结果如下:
test of unsigned overflow..
sizeof: 4
after sleep 3 s 1 - 4294967294 = 3
after sleep 3 s 4 - 1 = 3
after sleep 3 s 7 - 4 = 3
after sleep 3 s 10 - 7 = 3
after sleep 3 s 13 - 10 = 3
当g_start
的值为0xfffffff0
时,测试结果如下:
第一次测试:
test of unsigned overflow..
sizeof: 4
after sleep 2 s 4294967282 - 4294967281 = 1
after sleep 2 s 4294967284 - 4294967282 = 2
after sleep 2 s 4294967286 - 4294967284 = 2
after sleep 2 s 4294967288 - 4294967286 = 2
after sleep 2 s 4294967290 - 4294967288 = 2
after sleep 2 s 4294967292 - 4294967290 = 2
after sleep 2 s 4294967294 - 4294967292 = 2
after sleep 2 s 0 - 4294967294 = 2
after sleep 2 s 2 - 0 = 2
after sleep 2 s 4 - 2 = 2
after sleep 2 s 6 - 4 = 2
after sleep 2 s 8 - 6 = 2
第二次测试:
test of unsigned overflow..
sizeof: 4
after sleep 2 s 4294967282 - 4294967280 = 2
after sleep 2 s 4294967284 - 4294967282 = 2
after sleep 2 s 4294967286 - 4294967284 = 2
after sleep 2 s 4294967288 - 4294967286 = 2
after sleep 2 s 4294967290 - 4294967288 = 2
after sleep 2 s 4294967292 - 4294967290 = 2
after sleep 2 s 4294967294 - 4294967292 = 2
after sleep 2 s 0 - 4294967294 = 2
after sleep 2 s 2 - 0 = 2
after sleep 2 s 4 - 2 = 2
after sleep 2 s 6 - 4 = 2
after sleep 2 s 8 - 6 = 2
从结果上看,基本符合要求,即延时2秒,统计的耗时是2。——不管是否有溢出。
扩展知识
计算机中数值存储的是2的补码(2’s complement)。正数的补码是其本身,负数的补码是原码基础上取反码,末位加1。
mpu6050 芯片的陀螺仪和加速度数值,是16位有符号数值,就是用2的补码形式存储的。
小结
对于计时、延时类的函数,记录时间戳的变量为无符号数。类型为unsinged,不能加范围限制,这是指平台最大者,如32位系统,使用的是32位无符号数,64位的系统则是64位无符号数。当变量数值溢出后,其值归0,但计时函数是正常的,不需要额外处理溢出情况。
以上是关于C++无符号整数溢出探究的主要内容,如果未能解决你的问题,请参考以下文章