什么是2038問題
不知道你有沒有聽過2038問題?無論你是否聽過,本文將帶你認識什么是2038問題。
Unix時間戳
定義為從格林威治時間1970年01月01日00時00分00秒(北京時間1970年01月01日08時00分00秒)起至現在的總秒數。
而在C語言中,常用time_t來表示。舉個例子:
#include
#include?
int?main?(void)
{
time_t?rawtime=10;//time(NULL)獲取當前時間戳
struct?tm?info;
//轉為tm結構
localtime_r(?&rawtime,&info);
//轉為字符串
printf("時間為:?%s\n",?asctime(&info));
return?0;
}
運行結果:
時間為:?Thu?Jan??1?08:00:10?1970
在這里,我給rawtime設置為10,從打印結果來看也知道是正確的了。(注意,由于我們的時區為東八區,所以得到的時間是八點。)
當然這里的內容暫時不展開,主要關注time_t。
然而實際上,time_t到底是什么?
通常,time_t直接或者間接被定義為下面這樣:
typedef?long?time_t
我們知道,在32位程序下面,long占用四個字節空間:
#include
#include?
int?main(void)
{
printf("long?size:%zd\n",sizeof(long));
printf("long?max:%ld\n",LONG_MAX);
return?0;
}
編譯運行:
$?gcc?-m32?-o?main?main.c
$?./main
4
2147483647
可以看到,對于32位程序而言,long的最大值為2147483647。
溢出引發的問題
也就是說,一旦時間戳的值大于四字節的LONG_MAX,time_t將會無法正確存儲這個時間戳。
舉例來說,最開始的程序編譯為32位程序,修改rawtime的值為2147483648,運行結果為(注意,溢出的結果是未定義的):
#include
#include?
int?main?(void)
{
time_t?rawtime=2147483648;//time(NULL)獲取當前時間戳
struct?tm?info;
//轉為tm結構
localtime_r(?&rawtime,&info);
//轉為字符串
printf("時間為:?%s",?asctime(&info));
return(0);
}
然后我們編譯運行:
$?gcc?-m32?-o?main?main.c
warning:?this?decimal?constant?is?unsigned?only?in?ISO?C90?[enabled?by?default]
$?./main
間為:?Sat?Dec?14?04:45:52?1901
首先編譯的時候也有警告,原因在于2147483649無法使用time_t來表示,我們運行之后,也發現結果出乎我們的意料,它竟然是一個1901年的時間!
2038問題
那這和2038有什么關系呢?
編譯為64位程序我們再次運行就會發現:
間為:?Tue?Jan?19?11:14:08?2038
這個4字節整型表示的時間戳值只能表示到北京時間2038年1月19日11時14分07秒,一旦到了這時間之后,這些32位程序就可能運行異常,因為它們無法將此時間正確的識別為2038年,而可能會依個別實現而跳回1970年或1901年。
總結
到此,想必你已經很清楚了。由于在32位程序中,time_t最大值為2147483647,即最多表示到北京時間2038年1月19日11時14分07秒,因此在此之后就會出現異常。
而如果使用64位整型,則可以記錄至約2900億年后的292,277,026,596年12月4日15:30:08,星期日(UTC)。
當然,如果采用無符號整型,這個錯誤會被延后到 2106 年。到那時,還會有32位的程序在運行嗎?
2038問題只是一個引子,實際上在程序中有很多現在不會溢出而將來可能溢出的問題,你會關注嗎?
【編輯推薦】
【責任編輯:趙寧寧 TEL:(010)68476606】
點贊 0