- 相關推薦
C++中時間與時間戳的轉換
C語言把括號、賦值、強制類型轉換等都作為運算符處理。從而使C語言的運算類型極其豐富,表達式類型多樣化。下面是小編分享的C++中時間與時間戳的轉換,一起來看一下吧。
C++ 中時間與時間戳的轉換實例
// 設置時間顯示格式:
NSString *timeStr = @"2011-01-26 17:40:50";
[formatter setDateFormat:@"YYYY-MM-dd HH:mm:ss"]; // ----------設置你想要的格式,hh與HH的區別:分別表示12小時制,24小時制。
//設置時區,這個對于時間的處理有時很重要。
//例如你在國內發布信息,用戶在國外的另一個時區,你想讓用戶看到正確的發布時間就得注意時區設置,時間的換算。
//例如你發布的時間為2010-01-26 17:40:50,那么在英國愛爾蘭那邊用戶看到的時間應該是多少呢?
//他們與我們有7個小時的時差,所以他們那還沒到這個時間呢...那就是把未來的事做了。
NSTimeZone* timeZone = [NSTimeZone timeZoneWithName:@"Asia/Shanghai"];
[formatter setTimeZone:timeZone];
NSDate *date = [formatter dateFromString:timeStr]; //------------將字符串按formatter轉成nsdate
NSLog(@"date = %@", date);
NSDate *datenow = [NSDate date];//現在時間,你可以輸出來看下是什么格式
NSLog(@"datenow = %@", datenow);
NSString *nowtimeStr = [formatter stringFromDate:datenow];//----------將nsdate按formatter格式轉成nsstring,nsstring會顯示與當前的時間吻合的串
NSLog(@"nowtimeStr = %@", nowtimeStr);
// 時間轉時間戳的方法:
NSString *timeSp = [NSString stringWithFormat:@"%ld", (long)[datenow timeIntervalSince1970]];
NSLog(@"timeSp:%@",timeSp); //時間戳的值
// 時間戳轉時間的方法
NSDate *confromTimesp = [NSDate dateWithTimeIntervalSince1970:1296035591];
NSLog(@"1296035591 = %@",confromTimesp);
NSString *confromTimespStr = [formatter stringFromDate:confromTimesp];
NSLog(@"confromTimespStr = %@",confromTimespStr);
// 時間戳轉時間的方法:
NSDateFormatter* formatter1 = [[NSDateFormatter alloc] init];
[formatter1 setDateStyle:NSDateFormatterMediumStyle];
[formatter1 setTimeStyle:NSDateFormatterShortStyle];
[formatter1 setDateFormat:@"yyyyMMddHHMMss"];
NSDate *date1 = [formatter1 dateFromString:@"1283376197"];
NSLog(@"date1:%@",date1);
當前時間是:14:41:57
C/C++內存管理
內存分配方式
簡介
在C++中,內存分成5個區,他們分別是堆、棧、自由存儲區、全局/靜態存儲區和常量存儲區。
棧:在執行函數時,函數內局部變量的存儲單元都可以在棧上創建,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置于處理器的指令集中,效率很高,但是分配的內存容量有限。
堆:就是那些由 new分配的內存塊,他們的釋放編譯器不去管,由我們的應用程序去控制,一般一個new就要對應一個 。如果程序員沒有釋放掉,那么在程序結束后,操作系統會自動回收。
自由存儲區:就是那些由malloc等分配的內存塊,他和堆是十分相似的,不過它是用free來結束自己的生命的。
全局/靜態存儲區:全局變量和靜態變量被分配到同一塊內存中,在以前的C語言中,全局變量又分為初始化的和未初始化的,在C++里面沒有這個區分了,他們共同占用同一塊內存區。
常量存儲區:這是一塊比較特殊的存儲區,他們里面存放的是常量,不允許修改。
常見的內存錯誤及其對策
發生內存錯誤是件非常麻煩的事情。編譯器不能自動發現這些錯誤,通常是在程序運行時才能捕捉到。而這些錯誤大多沒有明顯的癥狀,時隱時現,增加了改錯的難度。有時用戶怒氣沖沖地把你找來,程序卻沒有發生任何問題,你一走,錯誤又發作了。
常見的內存錯誤及其對策如下:
內存分配未成功,卻使用了它。編程新手常犯這種錯誤,因為他們沒有意識到內存分配會不成功。常用解決辦法是,在使用內存之前檢查指針是否為NULL。如果指針p是函數的參數,那么在函數的入口處用assert(p!=NULL)進行檢查。如果是用malloc或new來申請內存,應該用if(p==NULL) 或if(p!=NULL)進行防錯處理。
內存分配雖然成功,但是尚未初始化就引用它。犯這種錯誤主要有兩個起因:一是沒有初始化的觀念;二是誤以為內存的缺省初值全為零,導致引用初值錯誤(例如數組)。內存的缺省初值究竟是什么并沒有統一的標準,盡管有些時候為零值,我們寧可信其無不可信其有。所以無論用何種方式創建數組,都別忘了賦初值,即便是賦零值也不可省略,不要嫌麻煩。
內存分配成功并且已經初始化,但操作越過了內存的邊界。例如在使用數組時經常發生下標“多1”或者“少1”的操作。特別是在for循環語句中,循環次數很容易搞錯,導致數組操作越界。
忘記了釋放內存,造成內存泄露。含有這種錯誤的函數每被調用一次就丟失一塊內存。剛開始時系統的內存充足,你看不到錯誤。終有一次程序突然死掉,系統出現提示:內存耗盡。動態內存的申請與釋放必須配對,程序中malloc與free的使用次數一定要相同,否則肯定有錯誤(new/同理)。
釋放了內存卻繼續使用它。
有三種情況:
(1). 程序中的對象調用關系過于復雜,實在難以搞清楚某個對象究竟是否已經釋放了內存,此時應該重新設計數據結構,從根本上解決對象管理的混亂局面。
(2). 函數的return語句寫錯了,注意不要返回指向“棧內存”的“指針”或者“引用”,因為該內存在函數體結束時被自動銷毀。
(3). 使用free或釋放了內存后,沒有將指針設置為NULL。導致產生“野指針”。
那么如何避免產生野指針呢?這里列出了5條規則,平常寫程序時多注意一下,養成良好的習慣。
規則1:用malloc或new申請內存之后,應該立即檢查指針值是否為NULL。防止使用指針值為NULL的內存。
規則2:不要忘記為數組和動態內存賦初值。防止將未被初始化的內存作為右值使用。
規則3:避免數組或指針的下標越界,特別要當心發生“多1”或者“少1”操作。
規則4:動態內存的申請與釋放必須配對,防止內存泄漏。
規則5:用free或釋放了內存之后,立即將指針設置為NULL,防止產生“野指針”。
C/C++函數調用的方式
棧是一種先進后出的數據結構,棧有一個存儲區、一個棧頂指針。棧頂指針指向堆棧中第一個可用的數據項(被稱為棧頂)。用戶可以在棧頂上方向棧中加入數據,這個操作被稱為壓棧(Push),壓棧以后,棧頂自動變成新加入數據項的位置,棧頂指針也隨之修改。用戶也可以從堆棧中取走棧頂,稱為彈出棧(pop),彈出棧后,棧頂下的一個元素變成棧頂,棧頂指針隨之修改。函數調用時,調用者依次把參數壓棧,然后調用函數,函數被調用以后,在堆棧中取得數據,并進行計算。函數計算結束以后,或者調用者、或者函數本身修改堆棧,使堆;謴驮b。
在參數傳遞中,有兩個重要的問題必須要明確說明:
1. 當參數個數多于一個時,按照什么順序把參數壓入堆棧;
2. 函數調用后,由誰來把堆;謴驮瓲。
在高級語言中,就是通過函數的調用方式來說明這兩個問題的。常見的調用方式有:
stdcall
cdecl
fastcall
thiscall
thiscall
naked call
下面就分別介紹這幾種調用方式:
1.stdcall
stdcall調用方式又被稱為Pascal調用方式。在Microsoft C++系列的C/C++編譯器中,使用PASCAL宏,WINAPI宏和CALLBACK宏來指定函數的調用方式為stdcall。
stdcall調用方式的函數聲明為:
int stdcall function(int a, int b);
stdcall的調用方式意味著:
(1) 參數從右向左一次壓入堆棧
(2) 由被調用函數自己來恢復堆棧
(3) 函數名自動加前導下劃線,后面緊跟著一個@,其后緊跟著參數的尺寸
上面那個函數翻譯成匯編語言將變成:
push b 先壓入第二個參數
push a 再壓入第一個參數
call function 調用函數
在編譯時,此函數的名字被翻譯為function@8
2.cdecl
cdecl調用方式又稱為C調用方式,是C語言缺省的調用方式,它的語法為:
int function(int a, int b) // 不加修飾符就是C調用方式
int cdecl function(int a, int b) // 明確指定用C調用方式
cdecl的調用方式決定了:
(1) 參數從右向左依次壓入堆棧
(2) 由調用者恢復堆棧
(3) 函數名自動加前導下劃線
由于是由調用者來恢復堆棧,因此C調用方式允許函數的參數個數是不固定的,這是C語言的一大特色。
此方式的函數被翻譯為:
push b // 先壓入第二個參數
push a // 在壓入第一個參數
call funtion // 調用函數
add esp, 8 // 清理堆棧
在編譯時,此方式的函數被翻譯成:function
3.fastcall
fastcall 按照名字上理解就可以知道,它是一種快速調用方式。此方式的函數的第一個和第二個DWORD參數通過ecx和edx傳遞,
后面的參數從右向左的順序壓入棧。
被調用函數清理堆棧。
函數名修個規則同stdcall
其聲明語法為:
int fastcall function(int a, int b);
4.thiscall
thiscall 調用方式是唯一一種不能顯示指定的修飾符。它是c++類成員函數缺省的調用方式。由于成員函數調用還有一個this指針,因此必須用這種特殊的調用方式。
thiscall調用方式意味著:
參數從右向左壓入棧。
如果參數個數確定,this指針通過ecx傳遞給被調用者;如果參數個數不確定,this指針在所有參數壓入棧后被壓入棧。
參數個數不定的,由調用者清理堆棧,否則由函數自己清理堆棧。
可以看到,對于參數個數固定的情況,它類似于stdcall,不定時則類似于cdecl。
5.naked call
是一種比較少見的調用方式,一般高級程序設計語言中不常見。
函數的聲明調用方式和實際調用方式必須一致,必然編譯器會產生混亂。
函數名字修改規則:
1.C編譯時函數名修飾約定規則:
stdcall調用約定在輸出函數名前加上一個下劃線前綴,后面加上一個“@”符號和其參數的字節數,格式為function@8。
cdecl調用約定僅在輸出函數名前加上一個下劃線前綴,格式為function。
fastcall調用約定在輸出函數名前加上一個“@”符號,后面也是一個“@”符號和其參數的字節數,格式為@function@8。
它們均不改變輸出函數名中的字符大小寫,這和PASCAL調用約定不同,PASCAL約定輸出的函數名無任何修飾且全部大寫。
2.C++編譯時函數名修飾約定規則:
stdcall調用約定:
(1)以“?”標識函數名的開始,后跟函數名;
(2)函數名后面以“@@YG”標識參數表的開始,后跟參數表;
(3)參數表以代號表示:
X--void ,
D--char,
E--unsigned char,
F--short,
H--int,
I--unsigned int,
J--long,
K--unsigned long,
M--float,
N--double,
N--bool,
PA--表示指針,后面的代號表明指針類型,如果相同類型的指針連續出現,以“0”代替,一個“0”代
表一次重復;
(4)參數表的第一項為該函數的返回值類型,其后依次為參數的數據類型,指針標識在其所指數據類型前;
(5)參數表后以“@Z”標識整個名字的結束,如果該函數無參數,則以“Z”標識結束。
其格式為“?functionname@@YG*****@Z”或“?functionname@@YG*XZ”,例如
int Test1(char *var1,unsigned long)-----“?Test1@@YGHPADK@Z”
void Test2() -----“?Test2@@YGXXZ”
cdecl調用約定:
規則同上面的stdcall調用約定,只是參數表的開始標識由上面的“@@YG”變為“@@YA”。
fastcall調用約定:
規則同上面的stdcall調用約定,只是參數表的開始標識由上面的“@@YG”變為“@@YI”。
VC++對函數的省缺聲明是"cedcl",將只能被C/C++調用。
關于C/C++ 表達式求值順序
i = ++i + 1; // The behavior is unspecified
在介紹概念之前,我們先解釋一下它的結果。這個表達式( expression )包含3個子表達式( subexpression ):
e1 = ++i
e2 = e1 + 1
i = e2
這三個子表達式都沒有順序點( sequence point ),而 ++ i 和 i = e3 都是有副作用( side effect )的表達式。由于沒有順序點,語言不保證這兩個副作用的順序。
更加可怕的是,如果i 是一個內建類型,并在下一個順序點之前被改寫超過一次,那么結果是未定義(undefined)的!比如本例中如果有:
int i = 0x1000fffe;
i = ++i + 1; // The result is undefined!!
你也許會認為他的結果是加1 或者加2,其實更糟糕 —— 結果可能是 0x1001ffff 。他的高字節接受了一個副作用的內容,而低字節則接受了另一個副作用的內容! 如果i 是指針,那么將很容易造成程序崩潰。
為什么要這么做呢?因為對于編譯器提供商來說,未確定的順序對優化有相當重要的作用。比如,一個常見的優化策略是“減少寄存器占用和臨時對象”。編譯器可以重新組織表達式的求值,以便盡量不使用額外的寄存器以及臨時變量。 更加嚴格的說,即使是編譯器提供商也無法完全徹底序列化指令(比如無法嚴格規定讀和寫的順序),因為CPU本身有權利修改指令順序,以便達到更高的速度。
下面的術語以 ISO C99 和 C++03為準。譯名為參考并附帶原術語對照,如有解釋不當或者錯誤望指正。
表達式有兩種功能。每個表達式都產生一個值( value ),同時可能包含副作用( side effect ),比如:他可能修改某些值。
規則的核心在于 順序點( sequence point ) [ C99 6.5 Expressions 條款2 ] [ C++03 5 Expressions 概述 條款4 ]。 這是一個結算點,語言要求這一側的求值和副作用(除了臨時對象的銷毀以外)全部完成,才能進入下面的部分。 C/C++中大部分表達式都沒有順序點,只有下面五種表達式有:
1 函數。函數調用之前有一個求值順序點。
2 && || 和 ?: 這三個包含邏輯的表達式。其左側邏輯完成后有一個求值順序點。
3 逗號表達式。逗號左側有一個求值順序點。
注意,他們都只有一個求值順序點,2和3的右側運算結束后并沒有求值順序點。
在兩個順序點之間,子表達式求值和副作用的順序是不確定的。假如代碼的結果與求值和副作用發生順序相關,我們稱這樣的代碼有不確定的行為(unspecified behavior)。 而且,假如期間對一個內建類型執行一次以上的寫操作,則是未定義行為(undefined behavior)——我們知道,未定義行為帶來最好的后果是讓你的程序立即崩掉。
n = n++; // 兩個副作用,對于內建對象產生是未定義行為
幾乎所有表達式,求值順序都不確定。比如,下面的加法, f1 f2 f3的調用順序是任意的:
n = f1() + f2() + f3(); // f1 f2 f3 調用順序任意
而函數也只在實際調用前有一個求值順序點。所以,常見于早期 C 語言教材的這類題目,是錯題:
printf("%d",--a+b,--b+a); // --a + b 和 --b + a 這兩個子表達式,求值順序不確定
天啊,甚至可能出現未定義行為?那么堅決不寫與實現相關的代碼是最好的對策。即使是不確定行為(比如函數調用時) 只要沒有順序點編譯器怎么做方便就怎么做。 有些人認為函數調用參數求值與入棧順序相關,這是一種誤導。這個東西要解釋,無異于事后諸葛亮:
void f( int i1, int i2, int i3, int i4 ){
cout<< i1 << ' ' << i2 << ' ' << i3 << ' ' << i4 << endl;}
int main(){
int i = 0;
f( i++, i++, i++, i++ );}
這個有四個表達式求值,同時每個表達式都有負作用。這八個操作順序是任意的,那么結果如何?未定義。
請用 VC7.1 Debug和 Release 分別測試這同一份代碼,結果是不同的:
0 0 0 0 [release]
3 2 1 0 [debug]
事實上,鑒于前面的討論,如果換一些其他初始值,這里甚至會出現錯位而得到千奇百怪的詭異結果。
再看看C/C++標準中的其他經典例子:
[C99] 6.5.2.2 Function call
條款12 EXAMPLE 在下面的函數調用中:
(*pf[f1()]) ( f2(), f3() + f4() )
函數 f1 f2 f3 和f4 可能以任何順序被調用。 但是,所有副作用都必須在那個 pf[ f1() ] 返回的函數指針產生的調用前完成。
[C++03] 5 Expressions 概論4
i = v[i++]; // the behavior is unspecified
i = 7, i++, i++; // i becomes 9 ( 譯注: 賦值表達式比逗號表達式優先級高 )
i = ++i + 1; // the behavior is unspecified
i = i + 1; // the value of i is incremented
More Effective C++ 告誡我們, 千萬不要重載 &&, || 和, 操作符[ MEC ,條款7 ]。為什么?
以逗號操作符為例,每個逗號左側有一個求值順序點。假如ar是一個普通的對象,下面的做法是無歧義的:
ar[ i ], ++i ;
但是,如果ar[ i ] 返回一個 class A 對象或引用,而它重載了 operator, 那么結果不妙了。那么,上面的語句實際上是一個函數調用:
ar[ i ].operator, ( ++i );
C/C++ 中,函數只在調用前有一個求值順序點。所以 ar[i] 和 ++i 的求值、以及 ++i 副作用的順序是任意的。這會引起混亂。
更可怕的是,重載 && 和 || 。 大家已經習慣了其速死算法: 如果左側求值已經決定了最終結果,則右側不會被求值。而且大家很依賴這個行為,比如是C風格字符串拷貝常常這樣寫:
while( p && *p )
*pd++ = *p++;
假如p 為 0, 那么 *p 的行為是未定義的,可能令程序崩潰。 而 && 的求值順序避免了這一點。 但是,如果我們重載 && 就等于下面的做法:
exp1 .operator && ( exp2 )
現在不僅僅是求值混亂了。無論exp1是什么結果,exp2 必然會被求值。
C與C++的變量聲明
如何理解C和C++的復雜類型聲明,曾經碰到過讓你迷惑不解、類似于int * (* (*fp1) (int) );這樣的變量聲明嗎?本文將由易到難,一步一步教會你如何理解這種復雜的C/C++ 聲明,我們將從天天都能碰到的較簡單的聲明入手,然后逐步加入const修飾符和typedef,還有函數指針,最后介紹一個能夠讓你準確地理解任何C/C++ 聲明的“右左法則”,需要強調一下的是,復雜的C/C++ 聲明并不是好的編程風格;我這里僅僅是教你如何去理解這些聲明。注重:為了保證能夠在同一行上顯示代碼和相關注釋,本文最好在至少1024x768分辨率的顯示器上閱讀。
讓我們從一個非常簡單的例子開始,如下:
int n;
這個應該被理解為www.diannao114.cn “declare n as an int”(n是一個int型的變量)。接下去來看一下指針變量,如下:
int *p;
這個應該被理解為“declare p as an int *”(p是一個int *型的變量),或者說p是一個指向一個int型變量的指針。我想在這里展開討論一下:我覺得在聲明一個指針(或引用)類型的變量時,最好將*(或 &)寫在緊靠變量之前,而不是緊跟基本類型之后。這樣可以避免一些理解上的誤區,比如:
int* p;//不推薦
再來看一個指針的指針的例子:
char **argv;
理論上,對于指針的級數沒有限制,你可以定義一個浮點類型變量的指針的指針的指針的指針,再來看如下的聲明:
int RollNum[30][4];
int (*p)[4]=RollNum;
int *q[5];
這里,p被聲明為一個指向一個4元素(int類型)數組的指針,而q被聲明為一個包含5個元素(int類型的指針)的數組。另外,我們還可以在同一個聲明中混合實用*和&,如下:
int **p1;// p1 is a pointer to a pointer to an int.
int *&p2;// p2 is a reference to a pointer to an int.
int &*p3;// ERROR: Pointer to a reference is illegal.
int &&p4;// ERROR: Reference to a reference is illegal.
注:p1是一個int類型的指針的指針;p2是一個int類型的指針的引用;p3是一個int類型引用的指針(不合法!);p4是一個int類型引用的引用(不合法。。
const 修飾符
當你想阻止一個變量被改變,可能會用到const要害字。在你給一個變量加上const修飾符的同時,通常需要對它進行初始化,因為以后的任何時候你將沒有機會再去改變它。例如:
const int n=5;
int const m=10;
上述兩個變量n和m其實是同一種類型的變量,都是const int(整形恒量)。因為C 標準規定,const要害字放在類型或變量名之前等價的。我個人更喜歡第一種聲明方式,因為它更突出了const修飾符的作用。當const與指針一起使用時,輕易讓人感到迷惑。例如,我們來看一下下面的p和q的聲明:
const int *p;
int const *q;
他們當中哪一個代表const int類型的指針(const直接修飾int),哪一個代表int類型的const指針(const直接修飾指針)?實際上,p和q都被聲明為const int類型的指針。而int類型的const指針應該這樣聲明:
int * const r= &n;// n has been declared as an int
這里,p和q都是指向const int類型的指針,也就是說,你在以后的程序里不能改變*p的值。而r是一個const指針,它在聲明的時候被初始化指向變量n(即r=&n;)之后,r的值將不再答應被改變(但*r的值可以改變)。
組合上述兩種const修飾的情況,我們來聲明一個指向 const int類型的const指針,如下:
const int * const p=&n// n has been declared as const int
下面給出的一些關于const的聲明,將幫助你徹底理清const的用法。不過請注重,下面的一些聲明是不能被編譯通過的,因為他們需要在聲明的同時進行初始化。為了簡潔起見,我忽略了初始化部分;因為加入初始化代碼的話,下面每個聲明都將增加兩行代碼。
char **p1;// pointer to pointer to char
const char **p2;// pointer to pointer to const char
char * const * p3;// pointer to const pointer to char
const char * const * p4;// pointer to const pointer to const char
char ** const p5;// const pointer to pointer to char
const char ** const p6;// const pointer to pointer to const char
char * const * const p7;// const pointer to const pointer to char
const char * const * const p8;// const pointer to const pointer to const char
注: p1是指向char類型的指針的指針;p2是指向const char類型的指針的指針;p3是指向char類型的const指針;p4是指向const char類型的const指針;p5是指向char類型的指針的const指針;p6是指向const char類型的指針的const指針;p7是指向char類型const指針的const指針;p8是指向const char類型的const指針的const指針。
【C++中時間與時間戳的轉換】相關文章:
PHP時間轉換Unix時間戳代碼08-19
如何利用PHP時間轉換Unix時間戳代碼10-21
php日期轉時間戳 指定日期轉換成時間戳09-26
C++類的轉換10-17
2017中考備戰時間表07-23
2024年中考考試時間06-08
2015中西醫執業醫師報名時間確定09-01
時間介詞短語07-30
認識時間教案08-19
2017中級經濟師報名時間及資格審核09-26