程序員為什么要學(xué)數(shù)據(jù)結(jié)構(gòu)?(程序員為什么要學(xué)數(shù)據(jù)結(jié)構(gòu))
參與文末話題討論,每日贈(zèng)送異步圖書

——異步小編
作為軟件開發(fā)人員,在面對(duì)全新的任務(wù)和挑戰(zhàn)時(shí),我們常常會(huì)將這些問(wèn)題分解為自己所熟知的各類解決方案和代碼片段,并根據(jù)客戶需求和任務(wù)截止日期,制定最快的方案進(jìn)行開發(fā)。但是,這樣做只是單純地完成了工作要求,有時(shí)對(duì)于學(xué)到更多的開發(fā)技巧和理念從而成為一名更優(yōu)秀、更高效的開發(fā)者的幫助并沒(méi)有想象中的那么大。
為什么要學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)?在計(jì)算機(jī)發(fā)展的初期,人們使用計(jì)算機(jī)的主要目的是處理數(shù)值計(jì)算問(wèn)題。使用計(jì)算機(jī)解決具體問(wèn)題一般需要經(jīng)過(guò)以下幾個(gè)步驟:首先從具體問(wèn)題抽象出適當(dāng)?shù)臄?shù)學(xué)模型,然后設(shè)計(jì)或選擇解此數(shù)學(xué)模型的算法,接著編寫程序并進(jìn)行調(diào)試、測(cè)試,直至得到最終的解答。
由于最初涉及的運(yùn)算對(duì)象是簡(jiǎn)單的整型、實(shí)型或布爾型數(shù)據(jù),所以程序設(shè)計(jì)者的主要精力集中于程序設(shè)計(jì)的技巧上,而無(wú)需重視數(shù)據(jù)結(jié)構(gòu)。隨著計(jì)算機(jī)應(yīng)用領(lǐng)域的擴(kuò)大和軟硬件的發(fā)展,非數(shù)值計(jì)算問(wèn)題顯得越來(lái)越重要。據(jù)統(tǒng)計(jì),當(dāng)今處理非數(shù)值計(jì)算問(wèn)題占用了90%以上的機(jī)器時(shí)間。這類問(wèn)題涉及的數(shù)據(jù)結(jié)構(gòu)更為復(fù)雜,數(shù)據(jù)元素之間的相互關(guān)系一般無(wú)法用數(shù)學(xué)方程式加以描述。因此,解決這類問(wèn)題的關(guān)鍵不再是數(shù)學(xué)分析和計(jì)算方法,而是要設(shè)計(jì)出合適的數(shù)據(jù)結(jié)構(gòu)。
著名的瑞士計(jì)算機(jī)科學(xué)家沃思(N.Wirth)教授曾提出:
算法 + 數(shù)據(jù)結(jié)構(gòu)=程序
程序設(shè)計(jì)的實(shí)質(zhì)是對(duì)實(shí)際問(wèn)題設(shè)計(jì)/選擇好的數(shù)據(jù)結(jié)構(gòu)和好的算法,而好的算法在很大程度上取決于描述實(shí)際問(wèn)題的數(shù)據(jù)結(jié)構(gòu)。
數(shù)據(jù)類型:基本的數(shù)據(jù)結(jié)構(gòu)
將數(shù)據(jù)類型稱作基本的數(shù)據(jù)結(jié)構(gòu)可能有些用詞不當(dāng),但開發(fā)人員往往使用這些數(shù)據(jù)類型來(lái)構(gòu)建他們自己的類和數(shù)據(jù)集,因此從他們的角度思考的話,這樣的稱呼也未嘗不可。所以,在我們進(jìn)一步學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)之前,最好先快速地回顧一下數(shù)據(jù)類型。
1.1 數(shù)值數(shù)據(jù)類型
C#、Java、Objective-C和Swift這4種語(yǔ)言中全部數(shù)值數(shù)據(jù)類型的詳細(xì)說(shuō)明都可以再寫一本書了。這里,我們只回顧每種語(yǔ)言中最常用的數(shù)值數(shù)據(jù)類型標(biāo)識(shí)符。評(píng)價(jià)這些數(shù)據(jù)類型最簡(jiǎn)單的方法是基于其實(shí)際數(shù)據(jù)大小用每個(gè)語(yǔ)言分別舉例,并在同一個(gè)框架內(nèi)來(lái)分析討論。
看起來(lái)一樣,實(shí)際卻不一樣!
當(dāng)在多個(gè)移動(dòng)平臺(tái)上開發(fā)應(yīng)用時(shí),應(yīng)當(dāng)注意到不同的語(yǔ)言可能會(huì)共用同一個(gè)/套數(shù)據(jù)類型標(biāo)識(shí)符或關(guān)鍵字,但從底層來(lái)看,這些標(biāo)識(shí)符并不一定等價(jià)。同樣地,同一種數(shù)據(jù)類型在不同的語(yǔ)言中也可能會(huì)有不同的標(biāo)識(shí)符。以16位無(wú)符號(hào)整型(16-bit unsigned integer)為例,在Objective-C中它被稱作unsigned short類型。但在C#或Swift里,卻分別用ushort類型和UInt16類型來(lái)表示。Java規(guī)定16位無(wú)符號(hào)整型只能用作char類型,盡管這個(gè)類型通常不用于數(shù)值型數(shù)據(jù)。以上每一種數(shù)據(jù)類型都表示一個(gè)16位無(wú)符號(hào)整型,只是名稱有所不同。這貌似問(wèn)題不大,但當(dāng)你分別使用每個(gè)平臺(tái)的原生語(yǔ)言為多個(gè)設(shè)備進(jìn)行應(yīng)用開發(fā)時(shí),為保證一致性,需注意到這種差別。否則,將會(huì)帶來(lái)不同平臺(tái)上出現(xiàn)特定錯(cuò)誤/漏洞的風(fēng)險(xiǎn),這將是非常難以檢測(cè)和判斷的。
1.1.1 整型
整型數(shù)據(jù)類型的定義為表示有符號(hào)(負(fù)值、零或正值)或無(wú)符號(hào)(零或正值)的整數(shù)。對(duì)于整型,每種語(yǔ)言都有其特定的標(biāo)識(shí)符和關(guān)鍵字,因此按照存儲(chǔ)長(zhǎng)度來(lái)思考最為簡(jiǎn)便。為達(dá)到我們的目的,我們只討論表示8、16、32和64位存儲(chǔ)對(duì)象的整型。
8位數(shù)據(jù)類型,或統(tǒng)稱為字節(jié)(byte),是我們探討的最小的數(shù)據(jù)類型。如果你復(fù)習(xí)過(guò)二進(jìn)制數(shù)學(xué),你會(huì)知道1個(gè)8位的內(nèi)存塊可表示28或256個(gè)值。有符號(hào)字節(jié)的可取值范圍為?128~127或?27~27?1。無(wú)符號(hào)字節(jié)的可取值范圍為0~255或0~28?1。
除了一些特殊情況,16位數(shù)據(jù)類型通常稱為短整型(short)。這種數(shù)據(jù)類型可表示216個(gè)值。有符號(hào)短整型的可取值范圍為?215~215?1。無(wú)符號(hào)短整型的可取值范圍為0~216?1。
32位數(shù)據(jù)類型一般被認(rèn)為是整型,有些時(shí)候也會(huì)被認(rèn)為是長(zhǎng)整型(long)。整型可表示232個(gè)值。有符號(hào)整型的可取值范圍為?231~231?1。無(wú)符號(hào)整型的可取值范圍為0~232?1。
最后,64位數(shù)據(jù)類型一般被認(rèn)為是長(zhǎng)整型,而Objective-C中規(guī)定其為雙長(zhǎng)整型(long long)。長(zhǎng)整型可以表示264個(gè)值。有符號(hào)長(zhǎng)整型的可取值范圍為?263~263?1。無(wú)符號(hào)長(zhǎng)整型的可取值范圍為0~264?1。
需注意的是,以上所提到的取值在我們所使用的4種語(yǔ)言中是一致的,但在其他的語(yǔ)言中可能會(huì)有細(xì)微的變化。熟悉所用語(yǔ)言數(shù)值標(biāo)識(shí)符的詳細(xì)細(xì)節(jié)總是一個(gè)好主意,尤其是當(dāng)你需要用到標(biāo)識(shí)符規(guī)定極值的情況下。
C#用整型來(lái)表示整數(shù)類型。它提供byte和sbyte兩種機(jī)制來(lái)生成8位整型。這兩種整型都能表示256個(gè)值,無(wú)符號(hào)的字節(jié)可取值范圍為0~255。有符號(hào)的字節(jié)對(duì)負(fù)值提供支持,因此取值范圍為?128~127,具體代碼如下。
//?C#
sbyte?minSbyte?=?-128;
byte?maxByte?=?255;
Console.WriteLine("minSbyte:?{0}",?minSbyte);
Console.WriteLine("maxByte:?{0}",?maxByte);
/*
輸出結(jié)果
minSbyte:?-128
maxByte:?255
*/
有趣的是,對(duì)于更長(zhǎng)位的標(biāo)識(shí)符C#改變了其命名模式。它用u作無(wú)符號(hào)(unsigned)的前綴,而不是使用sbyte中s作為有符號(hào)(signed)的前綴。因此C#分別使用short,ushort;int,uint;long,ulong作為16位、32位以及64位的整型標(biāo)識(shí)符,其代碼實(shí)現(xiàn)如下。
short?minShort?=?-32768;
ushort?maxUShort?=?65535;
Console.WriteLine("minShort:?{0}",?minShort);
Console.WriteLine("maxUShort:?{0}",?maxUShort);
int?minInt?=?-2147483648;
uint?maxUint?=?4294967295;
Console.WriteLine("minInt:?{0}",?minInt);
Console.WriteLine("maxUint:?{0}",?maxUint);
long?minLong?=?-9223372036854775808;
ulong?maxUlong?=?18446744073709551615;
Console.WriteLine("minLong:?{0}",?minLong);
Console.WriteLine("maxUlong:?{0}",?maxUlong);
/*
輸出結(jié)果
minShort:?-32768
maxUShort:?65535
minInt:?-2147483648
maxUint:?4294967295
minLong:?-9223372036854775808
maxUlong:?18446744073709551615
*/
Java將整型作為其原始數(shù)據(jù)類型的一部分。Java只提供一種建立8位類型的方式,即byte。這是一個(gè)有符號(hào)的數(shù)據(jù)類型,因此可表示?127~128的取值。Java還提供了名為Byte的包裝類,其不僅包裝了原始值,并對(duì)像“42”這些能夠轉(zhuǎn)換為數(shù)值的可解析字符串或文本提供了額外的構(gòu)造函數(shù)支持。這種模式重復(fù)體現(xiàn)在16位、32位和64位類型中。
//Java
byte?myByte?=?-128;
byte?bigByte?=?127;
Byte?minByte?=?new?Byte(myByte);
Byte?maxByte?=?new?Byte("128");
System.out.println(minByte);
System.out.println(bigByte);
System.out.println(maxByte);
/*
輸出結(jié)果
-128
127
127
*/
Java和C#共用了所有整型的標(biāo)識(shí)符,這意味著Java對(duì)8位、16位、32位和64位類型也提供了byte、short、int及l(fā)ong標(biāo)識(shí)符。Java中只有一個(gè)例外,即提供了16位無(wú)符號(hào)數(shù)據(jù)類型的標(biāo)識(shí)符char。值得注意的是,char類型通常只用作分配ASCII字符,而不是實(shí)際的整數(shù)數(shù)值。
//Short?Class
Short?minShort?=?new?Short(myShort);
Short?maxShort?=?new?Short("32767");
System.out.println(minShort);
System.out.println(bigShort);
System.out.println(maxShort);
int?myInt?=?-2147483648;
int?bigInt?=?2147483647;
//Integer?class
Integer?minInt?=?new?Integer(myInt);
Integer?maxInt?=?new?Integer("2147483647");
System.out.println(minInt);
System.out.println(bigInt);
System.out.println(maxInt);
long?myLong?=?-9223372036854775808L;
long?bigLong?=?9223372036854775807L;
//Long?class
Long?minLong?=?new?Long(myLong);
Long?maxLong?=?new?Long("9223372036854775807");
System.out.println(minLong);
System.out.println(bigLong);
System.out.println(maxLong);
/*
輸出結(jié)果
-32768
32767
32767
-2147483648
2147483647
2147483647
-9223372036854775808
9223372036854775807
9223372036854775807
*/
在以上的代碼中,須注意int類型和Integer類。不同于其他原始包裝類,Integer并不和其支持的標(biāo)識(shí)符共用名稱。
此外,注意long類型和其分配的數(shù)值。在每個(gè)例子中,這些值都有后綴L。這是Java對(duì)long類型的要求,因?yàn)榫幾g器將所有的數(shù)值文字默認(rèn)翻譯為32位整數(shù)。當(dāng)需要明確說(shuō)明字面數(shù)值是長(zhǎng)于32位時(shí),必須為其加上后綴L。不然的話,編譯器可能會(huì)報(bào)錯(cuò)。然而,當(dāng)給Long類型構(gòu)造函數(shù)傳遞字符串值時(shí),則不受這種限制:
Long?maxLong?=?new?Long("9223372036854775807");
對(duì)于8位數(shù)據(jù),Objective-C提供了有符號(hào)和無(wú)符號(hào)兩種格式的char類型。與其他語(yǔ)言相同,有符號(hào)的數(shù)據(jù)類型取值為?127~128,而無(wú)符號(hào)的類型取值為0~255。開發(fā)人員還可以選擇使用Objective-C的定寬整型int8_t和uint8_t。這種模式重復(fù)體現(xiàn)在16位、32位和64位類型中。最后,Objective-C還以NSNumber類的形式對(duì)每種整型提供了面向?qū)ο蟮陌b類。
char或其他整型和其定寬整型有非常顯著的區(qū)別。除了char類型總是為1字節(jié)長(zhǎng)度以外,其他Objective-C中的整型長(zhǎng)度會(huì)根據(jù)實(shí)現(xiàn)方式和底層架構(gòu)的不同而改變。這是因?yàn)镺bjective-C是基于C語(yǔ)言的,而C語(yǔ)言被設(shè)計(jì)成能夠在不同種類的底層架構(gòu)上高效工作。盡管可以在運(yùn)行和編譯時(shí)就確定整型數(shù)據(jù)結(jié)構(gòu)的確切長(zhǎng)度,但在一般情況下,你只能確定的是short <= int <= long <= long long。
這時(shí)定寬整型就派上用場(chǎng)了。在需要嚴(yán)格控制字節(jié)長(zhǎng)度的情況下,(u)int
//Objective-C
char?number?=?-127;
unsigned?char?uNumber?=?255;
NSLog(@"Signed?char?number:?%hhd",?number);
NSLog(@"Unsigned?char?uNumber:?%hhu",?uNumber);
//固定寬度
int8_t?fixedNumber8?=?-127;
uint8_t?fixedUNumber8?=?255;
NSLog(@"fixedNumber8:?%hhd",?fixedNumber8);
NSLog(@"fixedUNumber8:?%hhu",?fixedUNumber8);
NSNumber?*charNumber?=?[NSNumber?numberWithChar:number];
NSLog(@"Char?charNumber:?%@",?[charNumber?stringValue]);
/*
輸出結(jié)果
Signed?char?number:?-127
Unsigned?char?uNumber:?255
fixedNumber8:?-127
fixedUNumber8:?255
Char?charNumber:?-127
*/
從上面的例子可以看出,當(dāng)在代碼中使用char類型時(shí),必須指定標(biāo)識(shí)符unsigned,例如unsigned char。signed是char類型的默認(rèn)標(biāo)識(shí)符,可以省略,這也意味著char類型與signed char等價(jià)。Objective-C中的其他整型也遵循這種模式。
Objective-C中更大的整型包括用于16位的short類型,用于32位的int類型以及用于64位的long long類型。以上每種整型都依照(u)int
//更大的Objective-C整型
short?aShort?=?-32768;
unsigned?short?anUnsignedShort?=?65535;
NSLog(@"Signed?short?aShort:?%hd",?aShort);
NSLog(@"Unsigned?short?anUnsignedShort:?%hu",?anUnsignedShort);
int16_t?fixedNumber16?=?-32768;
uint16_t?fixedUNumber16?=?65535;
NSLog(@"fixedNumber16:?%hd",?fixedNumber16);
NSLog(@"fixedUNumber16:?%hu",?fixedUNumber16);
NSNumber?*shortNumber?=?[NSNumber?numberWithShort:aShort];
NSLog(@"Short?shortNumber:?%@",?[shortNumber?stringValue]);
int?anInt?=?-2147483648;
unsigned?int?anUnsignedInt?=?4294967295;
NSLog(@"Signed?Int?anInt:?%d",?anInt);
NSLog(@"Unsigned?Int?anUnsignedInt:?%u",?anUnsignedInt);
int32_t?fixedNumber32?=?-2147483648;
uint32_t?fixedUNumber32?=?4294967295;
NSLog(@"fixedNumber32:?%d",?fixedNumber32);
NSLog(@"fixedUNumber32:?%u",?fixedUNumber32);
NSNumber?*intNumber?=?[NSNumber?numberWithInt:anInt];
NSLog(@"Int?intNumber:?%@",?[intNumber?stringValue]);
long?long?aLongLong?=?-9223372036854775808;
unsigned?long?long?anUnsignedLongLong?=?18446744073709551615;
NSLog(@"Signed?long?long?aLongLong:?%lld",?aLongLong);
NSLog(@"Unsigned?long?long?anUnsignedLongLong:?%llu",
anUnsignedLongLong);
int64_t?fixedNumber64?=?-9223372036854775808;
uint64_t?fixedUNumber64?=?18446744073709551615;
NSLog(@"fixedNumber64:?%lld",?fixedNumber64);
NSLog(@"fixedUNumber64:?%llu",?fixedUNumber64);
NSNumber?*longlongNumber?=?[NSNumber?numberWithLongLong:aLongLong];
NSLog(@"Long?long?longlongNumber:?%@",?[longlongNumber?stringValue]);
/*
輸出結(jié)果
Signed?short?aShort:?-32768
Unsigned?short?anUnsignedShort:?65535
fixedNumber16:?-32768
fixedUNumber16:?65535
Short?shortNumber:?-32768
Signed?Int?anInt:?-2147483648
Unsigned?Int?anUnsignedInt:?4294967295
fixedNumber32:?-2147483648
fixedUNumber32:?4294967295
Int?intNumber:?-2147483648
Signed?long?long?aLongLong:?-9223372036854775808
Unsigned?long?long?anUnsignedLongLong:?18446744073709551615
fixedNumber64:?-9223372036854775808
fixedUNumber64:?18446744073709551615
Long?long?longlongNumber:?-9223372036854775808
*/
Swift語(yǔ)言和其他語(yǔ)言類似,對(duì)于有符號(hào)和無(wú)符號(hào)的整數(shù)提供了各自的標(biāo)識(shí)符,如Int8和UInt8。依據(jù)標(biāo)識(shí)符名稱來(lái)確定可應(yīng)用的數(shù)據(jù)類型,這樣的方式適用于Swift的每種整型,也使得Swift也許會(huì)成為最簡(jiǎn)單的語(yǔ)言。
//Swift
var?int8?:?Int8?=?-127
var?uint8?:?UInt8?=?255
print("int8:?\(int8)")
print("uint8:?\(uint8)")
/*
輸出結(jié)果
int8:?-127
uint8:?255
*/
為清晰地展示聲明過(guò)程,上面的例子使用:Int8和:UInt8標(biāo)識(shí)符明確地聲明了數(shù)據(jù)類型。在Swift里,還可以不加這些標(biāo)識(shí)符,讓Swift在運(yùn)行時(shí)動(dòng)態(tài)地推斷出數(shù)據(jù)類型。
//更大的Swift整型
var?int16?:?Int16?=?-32768
var?uint16?:?UInt16?=?65535
print("int16:?\(int16)")
print("uint16:?\(uint16)")
var?int32?:?Int32?=?-2147483648
var?uint32?:?UInt32?=?4294967295
print("int32:?\(int32)")
print("uint32:?\(uint32)")
var?int64?:?Int64?=?-9223372036854775808
var?uint64?:?UInt64?=?18446744073709551615
print("int64:?\(int64)")
print("uint64:?\(uint64)")
/*
輸出結(jié)果
int16:?-32768
uint16:?65535
int32:?-2147483648
uint32:?4294967295
int64:?-9223372036854775808
uint64:?18446744073709551615
*/
我為什么需要知道這些?你可能會(huì)問(wèn),我為什么需要知道數(shù)據(jù)類型的這些復(fù)雜細(xì)節(jié)?我難道不能只聲明一個(gè)int對(duì)象或其他類似的東西后去寫一些有趣的代碼?現(xiàn)代計(jì)算機(jī)甚至是移動(dòng)設(shè)備都能夠提供近乎無(wú)窮的資源,所以這沒(méi)什么大不了的,對(duì)吧?
事實(shí)并非如此。在你日常編程的經(jīng)歷中,大多數(shù)情況下隨便使用哪一個(gè)數(shù)據(jù)類型可能都行。比如,遍歷出任意一天西佛吉尼亞州全州車管部門簽發(fā)的牌照列表,結(jié)果可能從幾十個(gè)到上百個(gè)。你可以使用一個(gè)短整型變量或一個(gè)雙長(zhǎng)整型變量來(lái)控制for循環(huán)迭代。無(wú)論選用何種方式,這個(gè)循環(huán)為你的系統(tǒng)性能所帶來(lái)的影響幾乎可以忽略。
假設(shè)你在處理一組數(shù)據(jù),這組數(shù)據(jù)中的每個(gè)離散結(jié)果都與16位類型匹配,而你習(xí)慣性地使用32位類型來(lái)處理,這會(huì)導(dǎo)致什么結(jié)果呢?這樣做的結(jié)果會(huì)使處理這個(gè)數(shù)據(jù)集所需的內(nèi)存空間翻倍。當(dāng)離散結(jié)果只有100個(gè)或100 000個(gè)的時(shí)候,這樣做可能并沒(méi)什么不妥。但如果要處理的數(shù)據(jù)集很大,有百萬(wàn)個(gè)以及更多的離散結(jié)果的時(shí)候,這么做肯定會(huì)給系統(tǒng)性能帶來(lái)非常大的影響。
1.1.2 單精度浮點(diǎn)類型
單精度浮點(diǎn)(single precision floating point)類型通常稱為單精度類型(float),用32位浮點(diǎn)容器能夠存儲(chǔ)比整型更高精度的數(shù)值,通常有6~7位有效數(shù)字。多種語(yǔ)言使用float關(guān)鍵字/標(biāo)識(shí)符來(lái)標(biāo)記單精度浮點(diǎn)數(shù)值,本書所討論的4種語(yǔ)言也是如此。
需要注意的是,由于浮點(diǎn)數(shù)值不能精確地表示以10為基的數(shù)字,因此其精度受限于歸零誤差。浮點(diǎn)類型的數(shù)值算法非常復(fù)雜,無(wú)論何時(shí)其中的細(xì)節(jié)都與大部分開發(fā)人員不太相關(guān)。然而,學(xué)習(xí)浮點(diǎn)類型可以加深對(duì)底層技術(shù)及每種語(yǔ)言實(shí)現(xiàn)細(xì)節(jié)的了解。
由于我并不是這方面的專家,因此只簡(jiǎn)單了解一下這些類型背后的科學(xué)原理,并不涉及具體的數(shù)學(xué)算法。我在本章末尾的附加資料中列出了這個(gè)領(lǐng)域?qū)<覀兊囊恍┭芯砍晒瑥?qiáng)烈建議你們學(xué)習(xí)。
C#使用float關(guān)鍵字標(biāo)識(shí)32位浮點(diǎn)值。C#中float類型精度為6位有效數(shù)字,近似取值范圍從?3.4×1038~+3.4×1038:
//C#
float?piFloat?=?3.14159265358979323846264338327f;
Console.WriteLine("piFloat:?{0}",?piFloat);
/*
輸出結(jié)果
piFloat:?3.141593
*/
從上面的代碼可以看出,使用float在賦值時(shí)有f作為后綴。這是因?yàn)镃#和其他基于C的語(yǔ)言一樣,在處理賦值語(yǔ)句右邊的小數(shù)數(shù)字時(shí),默認(rèn)其為雙精度型(double,稍后討論)。如果在賦值時(shí)不用f或F后綴,而直接將一個(gè)雙精度浮點(diǎn)的數(shù)值賦給單精度浮點(diǎn)類型,則會(huì)產(chǎn)生編譯錯(cuò)誤。
此外,注意到最后一位的歸零誤差。我們將30位有效數(shù)字的圓周率賦值給piFloat。但由于float只能保留6位有效數(shù)字,其后數(shù)字都會(huì)被約去。若直接對(duì)圓周率值保留6位有效數(shù)字,我們得到3.141592,但由于歸零誤差,浮點(diǎn)數(shù)的實(shí)際值為3.141593。
與C#相同,Java使用float標(biāo)識(shí)符確定浮點(diǎn)值。Java中float類型精度為6或7位有效數(shù)字,近似取值范圍為?3.4×1038~+3.4×1038:
//Java
float?piFloat?=?3.141592653589793238462643383279f;
System.out.println(piFloat);
/*
輸出結(jié)果
3.1415927
*/
從上面的代碼可以看出,浮點(diǎn)賦值操作有f后綴。這是因?yàn)镴ava和其他基于C的語(yǔ)言一樣,在處理賦值語(yǔ)句右邊的小數(shù)數(shù)字時(shí),默認(rèn)其為雙精度型。如果在賦值時(shí)不加入f或F后綴,而直接將一個(gè)雙精度浮點(diǎn)的數(shù)值賦給單精度浮點(diǎn)類型,則會(huì)產(chǎn)生編譯錯(cuò)誤。
Objective-C使用float標(biāo)識(shí)符確定浮點(diǎn)值。在Objective-C中,float類型精度為6位有效數(shù)字,近似取值范圍從?3.4×1038~+3.4×1038:
//Objective-C
float?piFloat?=?3.14159265358979323846264338327f;
NSLog(@"piFloat:?%f",?piFloat);
NSNumber?*floatNumber?=?[NSNumber?numberWithFloat:piFloat];
NSLog(@"floatNumber:?%@",?[floatNumber?stringValue]);
/*
輸出結(jié)果
piFloat:?3.141593
floatNumber:?3.141593
*/
從上面的代碼可以看出,浮點(diǎn)賦值操作有f后綴。這是因?yàn)镺bjective-C和其他基于C的語(yǔ)言一樣,在處理賦值語(yǔ)句右邊的小數(shù)數(shù)字時(shí),默認(rèn)其為雙精度型。如果在賦值時(shí)不加入f或F后綴,而直接將一個(gè)雙精度浮點(diǎn)的數(shù)值賦給單精度浮點(diǎn)類型,則會(huì)產(chǎn)生編譯錯(cuò)誤。
此外,注意到最后一位的歸零誤差。我們將30位有效數(shù)字的圓周率賦值給piFloat。但由于float只能保留6位有效數(shù)字,其后數(shù)字都會(huì)被約去。若直接對(duì)圓周率值保留6位有效數(shù)字,我們得到3.141592,但由于歸零誤差,浮點(diǎn)數(shù)的實(shí)際值為3.141593。
Swift使用float標(biāo)識(shí)符確定浮點(diǎn)值。在Swift中,float類型精度為6位有效數(shù)字,近似取值范圍從?3.4×1038~+3.4×1038:
//Swift
var?floatValue?:?Float?=?3.141592653589793238462643383279
print("floatValue:?\(floatValue)")
/*
輸出結(jié)果
floatValue:?3.141593
*/
從上面的代碼可以看出,浮點(diǎn)賦值操作有f后綴。這是因?yàn)镾wift和其他基于C的語(yǔ)言一樣,在處理賦值語(yǔ)句右邊的實(shí)數(shù)數(shù)字時(shí),默認(rèn)其為雙精度型。如果在賦值時(shí)不加入f或F后綴,而直接將一個(gè)雙精度浮點(diǎn)的數(shù)值賦給單精度浮點(diǎn)類型,則會(huì)產(chǎn)生編譯錯(cuò)誤。
此外,注意到最后一位的歸零誤差。我們將30位有效數(shù)字的圓周率賦值給floatValue。但由于float只能保留6位有效數(shù)字,其后數(shù)字都會(huì)被約去。若直接對(duì)圓周率值保留6位有效數(shù)字,我們得到3.141 592,但由于歸零誤差,浮點(diǎn)數(shù)的實(shí)際值為3.141 593。
1.1.3 雙精度浮點(diǎn)類型
雙精度浮點(diǎn)(double precision floating point)類型通常稱為雙精度型(double)。用64位浮點(diǎn)容器能夠存儲(chǔ)比整型更高精度的數(shù)值,該類型通常有15位有效數(shù)字。多種語(yǔ)言使用double關(guān)鍵字/標(biāo)識(shí)符來(lái)標(biāo)記雙精度浮點(diǎn)數(shù)值,我們所討論的4種語(yǔ)言也是如此。
在大多數(shù)情況下,無(wú)論選用float還是double都無(wú)關(guān)緊要,除非內(nèi)存空間較為緊張,這時(shí)應(yīng)該盡可能選擇float。很多人認(rèn)為在多數(shù)情況下float比double更高效,一般來(lái)說(shuō),也確實(shí)是這樣。但在一些情況下,double會(huì)比f(wàn)loat更高效。事實(shí)上,由于存在太多無(wú)法在這里詳述的標(biāo)準(zhǔn),每種類型的效率會(huì)因情況而異。因此,如果需要在特定應(yīng)用中達(dá)到最高的效率,你需要仔細(xì)研究各種影響因素來(lái)選用最合適的類型。如果對(duì)效率并不是那么在意,那就任選一個(gè)合適的類型,接著干活。
C#使用double關(guān)鍵字標(biāo)識(shí)64位浮點(diǎn)數(shù)值。在C#中,double類型的精度為14或15位有效數(shù)字,近似取值范圍從±5.0×10?324~±1.7×10308:
//C#
double?piDouble?=?3.14159265358979323846264338327;
double?wholeDouble?=?3d;
Console.WriteLine("piDouble:?{0}",?piDouble);
Console.WriteLine("wholeDouble:?{0}",?wholeDouble);
/*
輸出結(jié)果
piDouble:?3.14159265358979
wholeDouble:?3
*/
從上面的代碼可以看出,wholeDouble變量的賦值操作加了d后綴。這是因?yàn)镃#和其他基于C的語(yǔ)言一樣,在處理賦值語(yǔ)句右邊的整數(shù)數(shù)字時(shí),默認(rèn)其為整型。如果在賦值時(shí)不加入d或D后綴,而試圖直接將一個(gè)整型數(shù)值賦給雙精度型,則會(huì)收到編譯錯(cuò)誤。
此外,注意到最后一位的歸零誤差。我們將30位有效數(shù)字的圓周率賦值給piDouble。但double只能保留14位有效數(shù)字,其后數(shù)字都會(huì)被約去。若直接對(duì)圓周率值保留15位有效數(shù)字,我們得到3.141 592 653 589 793,但由于歸零誤差,浮點(diǎn)數(shù)的實(shí)際值為3.141 592 653 589 79。
Java使用double關(guān)鍵字標(biāo)識(shí)64位浮點(diǎn)數(shù)值。在Java中,double類型的精度為15或16位有效數(shù)字,近似取值范圍為±4.9×10?324~±1.8×10308:
double?piDouble?=?3.141592653589793238462643383279;
System.out.println(piDouble);
/*
輸出結(jié)果
3.141592653589793
*/
查看上面的代碼,注意到最后一位的歸零誤差。我們將30位有效數(shù)字的圓周率賦值給piDouble。但double只能保留15位有效數(shù)字,其后數(shù)字都會(huì)被約去。若直接對(duì)圓周率值保留15位有效數(shù)字,我們得到3.141 592 653 589 793 2,但由于歸零誤差,浮點(diǎn)數(shù)的實(shí)際值為3.141 592 653 589 793。
Objective-C也使用double標(biāo)識(shí)符來(lái)確定64位浮點(diǎn)數(shù)值。在Objective-C中,double類型的精度為15位有效數(shù)字,近似取值范圍從2.3×10?308到1.7×10308。為進(jìn)一步提高精確性,Objective-C還提供了一個(gè)更高精度版本的double類型,即長(zhǎng)雙精度型(long double)。long double類型能夠存儲(chǔ)80位浮點(diǎn)數(shù)值,精度為19位有效數(shù)字,近似取值范圍從3.4×10?4932~1.1×104932:
//Objective-C
double?piDouble?=?3.14159265358979323846264338327;
NSLog(@"piDouble:?%.15f",?piDouble);
NSNumber?*doubleNumber?=?[NSNumber?numberWithDouble:piDouble];
NSLog(@"doubleNumber:?%@",?[doubleNumber?stringValue]);
/*
輸出結(jié)果
piDouble:?3.141592653589793
doubleNumber:?3.141592653589793
*/
查看上面的代碼,注意到最后一位的歸零誤差。我們將30位有效數(shù)字的圓周率賦值給piDouble。但double只能保留15位有效數(shù)字,其后數(shù)字都會(huì)被約去。若直接對(duì)圓周率值保留15位有效數(shù)字,我們得到3.141 592 653 589 793 2,但由于歸零誤差,浮點(diǎn)數(shù)的實(shí)際值為3.141 592 653 589 793。
Swift使用double標(biāo)識(shí)符來(lái)確定64位浮點(diǎn)數(shù)值。在Swift中,double類型的精度為15位有效數(shù)字,近似取值范圍從2.3×10?308~1.7×10308。需注意的是,根據(jù)Apple公司的Swift文檔,當(dāng)float和double類型均能滿足需求時(shí),推薦使用double類型:
//Swift
var?doubleValue?:?Double?=?3.141592653589793238462643383279
print("doubleValue:?\(doubleValue)")
/*
輸出結(jié)果
doubleValue:?3.14159265358979
*/
查看上面的代碼,注意最后一位的歸零誤差。我們將30位有效數(shù)字的圓周率賦值給doubleValue。但由于double只能保留15位有效數(shù)字,其后數(shù)字都會(huì)被約去。若直接對(duì)圓周率值保留15位有效數(shù)字,我們得到3.141 592 653 589 793,但由于歸零誤差,浮點(diǎn)數(shù)的實(shí)際值為3.141 592 653 589 79。
1.1.4 貨幣類型
由于浮點(diǎn)運(yùn)算事實(shí)上是基于二進(jìn)制數(shù)學(xué)的,有著固有的不準(zhǔn)確性,因此單精度和雙精度浮點(diǎn)類型無(wú)法精確地表示我們使用的十進(jìn)制貨幣。乍一看,將貨幣用單精度或雙精度浮點(diǎn)類型表示或許是個(gè)好主意,因?yàn)樗軌蚣s去運(yùn)算過(guò)程帶來(lái)的細(xì)微誤差。但是,當(dāng)把這些本來(lái)就不怎么精確的結(jié)果再進(jìn)行大量、復(fù)雜的運(yùn)算后,誤差會(huì)不斷累積,造成嚴(yán)重的偏差和難以跟蹤的錯(cuò)誤。這使得單精度和雙精度浮點(diǎn)類型無(wú)法用于對(duì)精確度要求近乎完美的十進(jìn)制貨幣。幸運(yùn)的是,對(duì)于貨幣和其他需要進(jìn)行高精度十進(jìn)制運(yùn)算的數(shù)學(xué)問(wèn)題,我們所討論的這4種語(yǔ)言都提供了相應(yīng)機(jī)制。
C#使用decimal關(guān)鍵字來(lái)標(biāo)識(shí)精確浮點(diǎn)值。在C#中,decimal的精度為28或29位有效數(shù)字,取值范圍為±1.0×10?28~±7.9×1028:
var?decimalValue?=
NSDecimalNumber.init(string:"3.141592653589793238462643383279")
print("decimalValue?\(decimalValue)")
/*
輸出結(jié)果
piDecimal:?3.1415926535897932384626433833
*/
上述代碼中,我們將30位有效數(shù)字的圓周率賦值給decimal Value,但它只保留了28位有效數(shù)字。
Java以BigDecimal類的形式對(duì)貨幣類問(wèn)題提供了一種面向?qū)ο蟮姆桨福?/p>
BigDecimal?piDecimal?=?new
BigDecimal("3.141592653589793238462643383279");
System.out.println(piDecimal);
/*
輸出結(jié)果
3.141592653589793238462643383279
*/
在上述代碼中,我們把十進(jìn)制值以文本形式作為構(gòu)造函數(shù)的參數(shù)來(lái)初始化BigDecimal類。程序運(yùn)行結(jié)果表明BigDecimal類返回了30位有效數(shù)字,沒(méi)有精度損失。
Objective-C以NSDecimalNumber類的形式對(duì)貨幣類問(wèn)題提供了一種面向?qū)ο蟮姆桨福?/p>
//Objective-C
NSDecimalNumber?*piDecimalNumber?=?[[NSDecimalNumber?alloc]
initWithDouble:3.14159265358979323846264338327];
NSLog(@"piDecimalNumber:?%@",?[piDecimalNumber?stringValue]);
/*
輸出結(jié)果
piDecimalNumber:?3.141592653589793792
*/
Swift用與Objective-C中同名的類NSDecimalNumber對(duì)貨幣類問(wèn)題提供了一種面向?qū)ο蟮姆桨浮_@個(gè)類在Swift和Objective-C中的初始化操作有些區(qū)別,但功能并無(wú)二致。
var?decimalValue?=
NSDecimalNumber.init(string:"3.141592653589793238462643383279")
print("decimalValue?\(decimalValue)")
/*
輸出結(jié)果
decimalValue?3.141592653589793238462643383279
*/
注意,Objective-C和Swift兩例的輸出結(jié)果都有30位有效數(shù)字,這說(shuō)明NSDecimal ?Number類適用于處理貨幣及其他十進(jìn)制數(shù)值。
透露一下,對(duì)于這些定制類型,還有一種簡(jiǎn)單的、可以說(shuō)是更為優(yōu)雅的替代方法。可使用int或long類型來(lái)進(jìn)行貨幣計(jì)算,用分代替元來(lái)計(jì)數(shù):
//C#?long?total?=?316;
//$3.16
1.1.5 類型轉(zhuǎn)換
在計(jì)算機(jī)科學(xué)領(lǐng)域中,類型轉(zhuǎn)換(type conversion或typecasting)是指將對(duì)象或數(shù)據(jù)從一種類型轉(zhuǎn)換到另一種類型。例如,你調(diào)用了一個(gè)返回類型為整型的方法,并需要將這個(gè)返回值作為另一個(gè)方法的傳入?yún)?shù),但第二個(gè)方法要求傳入?yún)?shù)必須是長(zhǎng)整型。由于整型數(shù)值在定義上存在于長(zhǎng)整型所允許的數(shù)值范圍之內(nèi),因此int值可以重定義為long類型。
通常,可通過(guò)隱式轉(zhuǎn)換(implicit conversion)或顯式轉(zhuǎn)換(也叫強(qiáng)制類型轉(zhuǎn)換, explicit conversion)進(jìn)行類型轉(zhuǎn)換。此外,我們還需要了解靜態(tài)類型語(yǔ)言(static languages)和動(dòng)態(tài)類型語(yǔ)言(dynamic languages)的區(qū)別,才能完全領(lǐng)會(huì)類型轉(zhuǎn)換的意義。
靜態(tài)類型語(yǔ)言會(huì)在編譯時(shí)進(jìn)行類型檢查(type checking)。這意味著當(dāng)你試圖生成解決方案時(shí),編譯器會(huì)檢查和實(shí)施程序中所有數(shù)據(jù)類型的約束條件。如果檢查失敗,會(huì)停止生成并報(bào)錯(cuò)。C#、Java以及Swift均是靜態(tài)類型語(yǔ)言。
另一方面,動(dòng)態(tài)類型語(yǔ)言會(huì)在執(zhí)行時(shí)進(jìn)行大多數(shù)甚至所有的類型檢查。這意味著如果開發(fā)人員在編程時(shí)有所疏忽,程序或許在生成階段一切正常,但在執(zhí)行時(shí)可能會(huì)出錯(cuò)。Objective-C混用了靜態(tài)和動(dòng)態(tài)類型對(duì)象,它是一種動(dòng)態(tài)類型語(yǔ)言。本章之前所討論的用于存儲(chǔ)數(shù)值型數(shù)據(jù)的純C對(duì)象均為靜態(tài)類型,而Objective-C中的NSNumber和NSDecimalNumber類均為動(dòng)態(tài)類型。思考下面的Objective-C代碼示例:
double?myDouble?=?@"chicken";
NSNumber?*myNumber?=?@"salad";
編譯器會(huì)對(duì)第一行代碼 報(bào)錯(cuò),內(nèi)容為“Initializing 'double' with an expression of incompatible type 'NSString *'”。這是因?yàn)閐ouble是一個(gè)純C的靜態(tài)類型。編譯器甚至在生成之前就知道應(yīng)該怎樣處理這個(gè)靜態(tài)類型,因此這段代碼通不過(guò)檢查。
然而,對(duì)于第二行代碼,編譯器只會(huì)發(fā)出內(nèi)容為“Incompatible pointer types initializing 'NSNumber *' with an expression of type 'NSString *'”的警告。這是因?yàn)镺bjective-C的NSNumber類是一個(gè)動(dòng)態(tài)類型。編譯器很智能,能夠發(fā)現(xiàn)錯(cuò)誤,但仍然會(huì)允許進(jìn)行生成(除非你在生成設(shè)置里指示過(guò)編譯器將警告視為錯(cuò)誤)。
顯然,前面的例子在運(yùn)行時(shí)會(huì)出現(xiàn)錯(cuò)誤,但在有些情況下,即使存在警告,你的應(yīng)用依然會(huì)正常運(yùn)行。然而,無(wú)論你使用的是哪種語(yǔ)言,最好不斷地清除掉已有的警告,再繼續(xù)編程。這樣有助于保持代碼的整潔,并避免出現(xiàn)一些難以診斷的運(yùn)行錯(cuò)誤。
有時(shí)也許并不能及時(shí)地處理警告,這時(shí)應(yīng)當(dāng)清楚地記錄下代碼并說(shuō)明警告源,以便其他開發(fā)人員了解來(lái)龍去脈。在萬(wàn)不得已的時(shí)候,可以利用宏和預(yù)處理器(預(yù)編譯器)命令來(lái)一條條地忽略警告。
不需要在源代碼中使用任何特殊語(yǔ)法的類型轉(zhuǎn)換為隱式轉(zhuǎn)換(implicit casting)。隱式轉(zhuǎn)換較為方便。思考下面的C#代碼示例:
int?a?=?10;
double?b?=?a++;
在上面的例子中,由于a既可以定義為int類型,也可以定義為double類型,且這兩種類型都經(jīng)過(guò)了人為定義,因此可以將a轉(zhuǎn)換為double類型。然而,由于隱式轉(zhuǎn)換并不一定要進(jìn)行人為的類型定義,因此編譯器不一定能完全判斷類型轉(zhuǎn)換所適用的約束條件,所以,直到程序運(yùn)行前,編譯器都無(wú)法進(jìn)行類型轉(zhuǎn)換檢查。這樣會(huì)使隱式轉(zhuǎn)換存在一定的風(fēng)險(xiǎn)。思考下面的C#代碼示例:
double?x?=?"54";
上面的例子并沒(méi)有告訴編譯器該如何處理字符串值,因此這是一個(gè)隱式轉(zhuǎn)換。在這種情況下進(jìn)行應(yīng)用生成,編譯器會(huì)針對(duì)這行代碼 報(bào)錯(cuò),內(nèi)容為“Cannot implicitly convert type 'string' to 'double'”。現(xiàn)在,思考同樣例子的顯式轉(zhuǎn)換:
double?x?=?double.Parse("42");
Console.WriteLine("40?+?2?=?{0}",?x);
/*
輸出結(jié)果
40?+?2?=?42
*/
假設(shè)字符串是可解析的,上述類型轉(zhuǎn)換即顯式轉(zhuǎn)換,因此是類型安全的。
兩種數(shù)據(jù)類型在進(jìn)行類型轉(zhuǎn)換時(shí),轉(zhuǎn)換結(jié)果是否在目標(biāo)數(shù)據(jù)結(jié)構(gòu)所允許的范圍之內(nèi)非常關(guān)鍵。如果源數(shù)據(jù)類型比目標(biāo)數(shù)據(jù)類型所支持的字節(jié)數(shù)多,則這種類型轉(zhuǎn)換為縮限轉(zhuǎn)換(narrowing conversion)。
縮限轉(zhuǎn)換不是什么情況下都能夠進(jìn)行,并且在轉(zhuǎn)換過(guò)程中很可能會(huì)丟失信息。舉例來(lái)說(shuō),將浮點(diǎn)類型轉(zhuǎn)換為整型會(huì)丟失數(shù)據(jù)(損失精度),轉(zhuǎn)換結(jié)果會(huì)被近似為與原始值最接近的整數(shù)。在絕大多數(shù)靜態(tài)類型語(yǔ)言中,縮限轉(zhuǎn)換是不能被隱式執(zhí)行的。以本章之前出現(xiàn)過(guò)的單精度和雙精度類型的C#代碼為例,將雙精度縮限轉(zhuǎn)換為單精度:
//C#
piFloat?=?piDouble;
在這個(gè)例子中,編譯器會(huì)報(bào)錯(cuò),內(nèi)容為“Cannot implicitly convert type 'double' to 'float'. And explicit conversion exists (Are you missing a cast?)”。編譯器發(fā)現(xiàn)了這個(gè)縮限轉(zhuǎn)換,并將精度損失視作錯(cuò)誤。錯(cuò)誤信息建議我們使用顯式轉(zhuǎn)換來(lái)解決問(wèn)題。
//C#
piFloat?=?(float)piDouble;
我們現(xiàn)在使用顯式轉(zhuǎn)換將double類型的piDouble轉(zhuǎn)換為單精度型,編譯器不會(huì)再因?yàn)榫葥p失而報(bào)錯(cuò)。
如果源數(shù)據(jù)類型比目標(biāo)數(shù)據(jù)類型所支持的字節(jié)數(shù)少,則這種類型轉(zhuǎn)換為擴(kuò)展轉(zhuǎn)換(widening conversion)。擴(kuò)展轉(zhuǎn)換會(huì)保留源類型的值,但可能會(huì)改變值的表示方法。大多數(shù)靜態(tài)類型語(yǔ)言都允許隱式擴(kuò)展轉(zhuǎn)換。還是以前面的C#代碼為例:
//C#
piDouble?=?piFloat;
本例中,隱式轉(zhuǎn)換不會(huì)引起編譯器報(bào)錯(cuò),應(yīng)用也能正常生成。將這個(gè)例子進(jìn)一步拓展:
//C#
piDouble?=?(double)piFloat;
上面的顯式轉(zhuǎn)換能提高代碼的可靠性,但無(wú)論如何都不會(huì)改變這條語(yǔ)句的本質(zhì)。即便這樣會(huì)顯得比較冗余,但不會(huì)引起編譯器出錯(cuò)。除了提高可靠性之外,顯式的擴(kuò)展轉(zhuǎn)換不會(huì)對(duì)程序造成其他額外的影響。因此,可根據(jù)個(gè)人喜好來(lái)選用隱式或顯式的擴(kuò)展轉(zhuǎn)換。
我們學(xué)習(xí)了4種最常用的移動(dòng)開發(fā)語(yǔ)言所提供的基本數(shù)據(jù)類型。從底層架構(gòu)及語(yǔ)言規(guī)范角度學(xué)習(xí)了數(shù)值和浮點(diǎn)數(shù)據(jù)類型的特點(diǎn)和操作。我們還學(xué)習(xí)了將對(duì)象從一種類型轉(zhuǎn)換到另一種類型的方法,以及根據(jù)轉(zhuǎn)換中源類型和目標(biāo)類型的大小不同如何進(jìn)行擴(kuò)展轉(zhuǎn)換和縮限轉(zhuǎn)換。
本文摘自《程序員學(xué)數(shù)據(jù)結(jié)構(gòu)》
【美】John Z. Sonmez(約翰 Z. 森梅茲)著
點(diǎn)擊封面購(gòu)買紙書
一本幫助你輕松掌握數(shù)據(jù)結(jié)構(gòu)的實(shí)用指南 Objective-C、C#、Java和Swift多種語(yǔ)言案例
本文轉(zhuǎn)載自異步社區(qū)
開發(fā)者 數(shù)據(jù)結(jié)構(gòu)
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。