05 | 數(shù)組:為什么很多編程語(yǔ)言中數(shù)組都從0開(kāi)始編號(hào)?
提到數(shù)組月而,我想你肯定不陌生汗洒,甚至還會(huì)自信地說(shuō),它很簡(jiǎn)單啊父款。
是的溢谤,在每一種編程語(yǔ)言中,基本都會(huì)有數(shù)組這種數(shù)據(jù)類(lèi)型憨攒。不過(guò)世杀,它不僅僅是一種編程語(yǔ)言中的數(shù)據(jù)類(lèi)型,還是一種最基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu)肝集。盡管數(shù)組看起來(lái)非痴鞍樱基礎(chǔ)、簡(jiǎn)單杏瞻,但是我估計(jì)很多人都并沒(méi)有理解這個(gè)基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)的精髓所刀。
在大部分編程語(yǔ)言中衙荐,數(shù)組都是從0開(kāi)始編號(hào)的,但你是否下意識(shí)地想過(guò)浮创, 為什么數(shù)組要從0開(kāi)始編號(hào)忧吟,而不是從1開(kāi)始呢?
從1開(kāi)始不是更符合人類(lèi)的思維習(xí)慣嗎斩披?
你可以帶著這個(gè)問(wèn)題來(lái)學(xué)習(xí)接下來(lái)的內(nèi)容溜族。
如何實(shí)現(xiàn)隨機(jī)訪(fǎng)問(wèn)?
什么是數(shù)組垦沉?我估計(jì)你心中已經(jīng)有了答案煌抒。不過(guò),我還是想用專(zhuān)業(yè)的話(huà)來(lái)給你做下解釋乡话。
數(shù)組(Array)是一種線(xiàn)性表數(shù)據(jù)結(jié)構(gòu)摧玫。它用一組連續(xù)的內(nèi)存空間,來(lái)存儲(chǔ)一組具有相同類(lèi)型的數(shù)據(jù)绑青。
這個(gè)定義里有幾個(gè)關(guān)鍵詞,理解了這幾個(gè)關(guān)鍵詞屋群,我想你就能徹底掌握數(shù)組的概念了闸婴。下面就從我的角度分別給你“點(diǎn)撥”一下。
第一是 線(xiàn)性表 (Linear
List)芍躏。顧名思義邪乍,線(xiàn)性表就是數(shù)據(jù)排成像一條線(xiàn)一樣的結(jié)構(gòu)。每個(gè)線(xiàn)性表上的數(shù)據(jù)最多只有前和后兩個(gè)方向对竣。其實(shí)除了數(shù)組庇楞,鏈表、隊(duì)列否纬、棧等也是線(xiàn)性表結(jié)構(gòu)吕晌。
而與它相對(duì)立的概念是 非線(xiàn)性表 ,比如二叉樹(shù)临燃、堆睛驳、圖等。之所以叫非線(xiàn)性膜廊,是因?yàn)榉Ψ校诜蔷€(xiàn)性表中,數(shù)據(jù)之間并不是簡(jiǎn)單的前后關(guān)系爪瓜。
第二個(gè)是 連續(xù)的內(nèi)存空間和相同類(lèi)型的數(shù)據(jù)
蹬跃。正是因?yàn)檫@兩個(gè)限制,它才有了一個(gè)堪稱(chēng)“殺手锏”的特性:“隨機(jī)訪(fǎng)問(wèn)”铆铆。但有利就有弊蝶缀,這兩個(gè)限制也讓數(shù)組的很多操作變得非常低效丹喻,比如要想在數(shù)組中刪除、插入一個(gè)數(shù)據(jù)扼劈,為了保證連續(xù)性驻啤,就需要做大量的數(shù)據(jù)搬移工作。
說(shuō)到數(shù)據(jù)的訪(fǎng)問(wèn)荐吵,那你知道數(shù)組是如何實(shí)現(xiàn)根據(jù)下標(biāo)隨機(jī)訪(fǎng)問(wèn)數(shù)組元素的嗎骑冗?
我們拿一個(gè)長(zhǎng)度為10的int類(lèi)型的數(shù)組int[] a = new
int[10]來(lái)舉例。在我畫(huà)的這個(gè)圖中先煎,計(jì)算機(jī)給數(shù)組a[10]贼涩,分配了一塊連續(xù)內(nèi)存空間1000~1039,其中薯蝎,內(nèi)存塊的首地址為base_address =
1000遥倦。
我們知道,計(jì)算機(jī)會(huì)給每個(gè)內(nèi)存單元分配一個(gè)地址占锯,計(jì)算機(jī)通過(guò)地址來(lái)訪(fǎng)問(wèn)內(nèi)存中的數(shù)據(jù)袒哥。當(dāng)計(jì)算機(jī)需要隨機(jī)訪(fǎng)問(wèn)數(shù)組中的某個(gè)元素時(shí),它會(huì)首先通過(guò)下面的尋址公式消略,計(jì)算出該元素存儲(chǔ)的內(nèi)存地址:
a[i]_address = base_address + i * data_type_size
其中data_type_size表示數(shù)組中每個(gè)元素的大小堡称。我們舉的這個(gè)例子里,數(shù)組中存儲(chǔ)的是int類(lèi)型數(shù)據(jù)艺演,所以data_type_size就為4個(gè)字節(jié)却紧。這個(gè)公式非常簡(jiǎn)單,我就不多做解釋了胎撤。
這里我要特別糾正一個(gè)“錯(cuò)誤”晓殊。我在面試的時(shí)候,常常會(huì)問(wèn)數(shù)組和鏈表的區(qū)別伤提,很多人都回答說(shuō)巫俺,“鏈表適合插入、刪除飘弧,時(shí)間復(fù)雜度O(1)识藤;數(shù)組適合查找,查找時(shí)間復(fù)雜度為O(1)”次伶。
實(shí)際上痴昧,這種表述是不準(zhǔn)確的。數(shù)組是適合查找操作冠王,但是查找的時(shí)間復(fù)雜度并不為O(1)赶撰。即便是排好序的數(shù)組,你用二分查找,時(shí)間復(fù)雜度也是O(logn)豪娜。所以餐胀,正確的表述應(yīng)該是,數(shù)組支持隨機(jī)訪(fǎng)問(wèn)瘤载,根據(jù)下標(biāo)隨機(jī)訪(fǎng)問(wèn)的時(shí)間復(fù)雜度為O(1)否灾。
低效的“插入”和“刪除”
前面概念部分我們提到,數(shù)組為了保持內(nèi)存數(shù)據(jù)的連續(xù)性鸣奔,會(huì)導(dǎo)致插入墨技、刪除這兩個(gè)操作比較低效。現(xiàn)在我們就來(lái)詳細(xì)說(shuō)一下挎狸,究竟為什么會(huì)導(dǎo)致低效扣汪?又有哪些改進(jìn)方法呢?
我們先來(lái)看 插入操作 锨匆。
假設(shè)數(shù)組的長(zhǎng)度為n崭别,現(xiàn)在雳殊,如果我們需要將一個(gè)數(shù)據(jù)插入到數(shù)組中的第k個(gè)位置压恒。為了把第k個(gè)位置騰出來(lái),給新來(lái)的數(shù)據(jù)弃酌,我們需要將第k~n這部分的元素都順序地往后挪一位土榴。那插入操作的時(shí)間復(fù)雜度是多少呢暗膜?你可以自己先試著分析一下。
如果在數(shù)組的末尾插入元素鞭衩,那就不需要移動(dòng)數(shù)據(jù)了,這時(shí)的時(shí)間復(fù)雜度為O(1)娃善。但如果在數(shù)組的開(kāi)頭插入元素论衍,那所有的數(shù)據(jù)都需要依次往后移動(dòng)一位,所以最壞時(shí)間復(fù)雜度是O(n)聚磺。
因?yàn)槲覀冊(cè)诿總€(gè)位置插入元素的概率是一樣的坯台,所以平均情況時(shí)間復(fù)雜度為(1+2+…n)/n=O(n)。
如果數(shù)組中的數(shù)據(jù)是有序的瘫寝,我們?cè)谀硞€(gè)位置插入一個(gè)新的元素時(shí)蜒蕾,就必須按照剛才的方法搬移k之后的數(shù)據(jù)。但是焕阿,如果數(shù)組中存儲(chǔ)的數(shù)據(jù)并沒(méi)有任何規(guī)律咪啡,數(shù)組只是被當(dāng)作一個(gè)存儲(chǔ)數(shù)據(jù)的集合。在這種情況下暮屡,如果要將某個(gè)數(shù)據(jù)插入到第k個(gè)位置撤摸,為了避免大規(guī)模的數(shù)據(jù)搬移,我們還有一個(gè)簡(jiǎn)單的辦法就是,直接將第k位的數(shù)據(jù)搬移到數(shù)組元素的最后准夷,把新的元素直接放入第k個(gè)位置钥飞。
為了更好地理解,我們舉一個(gè)例子衫嵌。假設(shè)數(shù)組a[10]中存儲(chǔ)了如下5個(gè)元素:a读宙,b,c楔绞,d结闸,e。
我們現(xiàn)在需要將元素x插入到第3個(gè)位置墓律。我們只需要將c放入到a[5]膀估,將a[2]賦值為x即可。最后耻讽,數(shù)組中的元素如下: a察纯,b,x针肥,d饼记,e,c慰枕。
利用這種處理技巧具则,在特定場(chǎng)景下,在第k個(gè)位置插入一個(gè)元素的時(shí)間復(fù)雜度就會(huì)降為O(1)具帮。這個(gè)處理思想在快排中也會(huì)用到博肋,我會(huì)在排序那一節(jié)具體來(lái)講,這里就說(shuō)到這兒蜂厅。
我們?cè)賮?lái)看 刪除操作 匪凡。
跟插入數(shù)據(jù)類(lèi)似,如果我們要?jiǎng)h除第k個(gè)位置的數(shù)據(jù)掘猿,為了內(nèi)存的連續(xù)性病游,也需要搬移數(shù)據(jù),不然中間就會(huì)出現(xiàn)空洞稠通,內(nèi)存就不連續(xù)了衬衬。
和插入類(lèi)似,如果刪除數(shù)組末尾的數(shù)據(jù)改橘,則最好情況時(shí)間復(fù)雜度為O(1)滋尉;如果刪除開(kāi)頭的數(shù)據(jù),則最壞情況時(shí)間復(fù)雜度為O(n)唧龄;平均情況時(shí)間復(fù)雜度也為O(n)兼砖。
實(shí)際上奸远,在某些特殊場(chǎng)景下,我們并不一定非得追求數(shù)組中數(shù)據(jù)的連續(xù)性讽挟。如果我們將多次刪除操作集中在一起執(zhí)行懒叛,刪除的效率是不是會(huì)提高很多呢?
我們繼續(xù)來(lái)看例子耽梅。數(shù)組a[10]中存儲(chǔ)了8個(gè)元素:a薛窥,b,c眼姐,d诅迷,e,f众旗,g罢杉,h。現(xiàn)在贡歧,我們要依次刪除a滩租,b,c三個(gè)元素利朵。
為了避免d律想,e,f绍弟,g技即,h這幾個(gè)數(shù)據(jù)會(huì)被搬移三次,我們可以先記錄下已經(jīng)刪除的數(shù)據(jù)樟遣。每次的刪除操作并不是真正地搬移數(shù)據(jù)而叼,只是記錄數(shù)據(jù)已經(jīng)被刪除。當(dāng)數(shù)組沒(méi)有更多空間存儲(chǔ)數(shù)據(jù)時(shí)豹悬,我們?cè)儆|發(fā)執(zhí)行一次真正的刪除操作澈歉,這樣就大大減少了刪除操作導(dǎo)致的數(shù)據(jù)搬移。
如果你了解JVM屿衅,你會(huì)發(fā)現(xiàn),這不就是JVM標(biāo)記清除垃圾回收算法的核心思想嗎莹弊?沒(méi)錯(cuò)涤久,數(shù)據(jù)結(jié)構(gòu)和算法的魅力就在于此,
很多時(shí)候我們并不是要去死記硬背某個(gè)數(shù)據(jù)結(jié)構(gòu)或者算法忍弛,而是要學(xué)習(xí)它背后的思想和處理技巧响迂,這些東西才是最有價(jià)值的
。如果你細(xì)心留意细疚,不管是在軟件開(kāi)發(fā)還是架構(gòu)設(shè)計(jì)中蔗彤,總能找到某些算法和數(shù)據(jù)結(jié)構(gòu)的影子。
警惕數(shù)組的訪(fǎng)問(wèn)越界問(wèn)題
了解了數(shù)組的幾個(gè)基本操作后,我們來(lái)聊聊數(shù)組訪(fǎng)問(wèn)越界的問(wèn)題然遏。
首先贫途,我請(qǐng)你來(lái)分析一下這段C語(yǔ)言代碼的運(yùn)行結(jié)果:
int main(int argc, char* argv[]){
int i = 0;
int arr[3] = {0};
for(; i<=3; i++){
arr[i] = 0;
printf("hello world\n");
}
return 0;
}
你發(fā)現(xiàn)問(wèn)題了嗎?這段代碼的運(yùn)行結(jié)果并非是打印三行“hello word”待侵,而是會(huì)無(wú)限打印“hello world”丢早,這是為什么呢?
因?yàn)檠砬悖瑪?shù)組大小為3怨酝,a[0],a[1]那先,a[2]农猬,而我們的代碼因?yàn)闀?shū)寫(xiě)錯(cuò)誤,導(dǎo)致for循環(huán)的結(jié)束條件錯(cuò)寫(xiě)為了i<=3而非i<3售淡,所以當(dāng)i=3時(shí)斤葱,數(shù)組a[3]訪(fǎng)問(wèn)越界。
我們知道勋又,在C語(yǔ)言中苦掘,只要不是訪(fǎng)問(wèn)受限的內(nèi)存,所有的內(nèi)存空間都是可以自由訪(fǎng)問(wèn)的楔壤。根據(jù)我們前面講的數(shù)組尋址公式鹤啡,a[3]也會(huì)被定位到某塊不屬于數(shù)組的內(nèi)存地址上,而這個(gè)地址正好是存儲(chǔ)變量i的內(nèi)存地址蹲嚣,那么a[3]=0就相當(dāng)于i=0递瑰,所以就會(huì)導(dǎo)致代碼無(wú)限循環(huán)。
數(shù)組越界在C語(yǔ)言中是一種未決行為隙畜,并沒(méi)有規(guī)定數(shù)組訪(fǎng)問(wèn)越界時(shí)編譯器應(yīng)該如何處理抖部。因?yàn)椋L(fǎng)問(wèn)數(shù)組的本質(zhì)就是訪(fǎng)問(wèn)一段連續(xù)內(nèi)存议惰,只要數(shù)組通過(guò)偏移計(jì)算得到的內(nèi)存地址是可用的慎颗,那么程序就可能不會(huì)報(bào)任何錯(cuò)誤。
這種情況下言询,一般都會(huì)出現(xiàn)莫名其妙的邏輯錯(cuò)誤俯萎,就像我們剛剛舉的那個(gè)例子,debug的難度非常的大运杭。而且夫啊,很多計(jì)算機(jī)病毒也正是利用到了代碼中的數(shù)組越界可以訪(fǎng)問(wèn)非法地址的漏洞,來(lái)攻擊系統(tǒng)辆憔,所以寫(xiě)代碼的時(shí)候一定要警惕數(shù)組越界撇眯。
但并非所有的語(yǔ)言都像C一樣报嵌,把數(shù)組越界檢查的工作丟給程序員來(lái)做,像Java本身就會(huì)做越界檢查熊榛,比如下面這幾行Java代碼锚国,就會(huì)拋出java.lang.ArrayIndexOutOfBoundsException。
int[] a = new int[3];
a[3] = 10;
容器能否完全替代數(shù)組来候?
針對(duì)數(shù)組類(lèi)型跷叉,很多語(yǔ)言都提供了容器類(lèi),比如Java中的ArrayList营搅、C++
STL中的vector云挟。在項(xiàng)目開(kāi)發(fā)中,什么時(shí)候適合用數(shù)組转质,什么時(shí)候適合用容器呢园欣?
這里我拿Java語(yǔ)言來(lái)舉例。如果你是Java工程師休蟹,幾乎天天都在用ArrayList沸枯,對(duì)它應(yīng)該非常熟悉。那它與數(shù)組相比赂弓,到底有哪些優(yōu)勢(shì)呢绑榴?
我個(gè)人覺(jué)得,ArrayList最大的優(yōu)勢(shì)就是 可以將很多數(shù)組操作的細(xì)節(jié)封裝起來(lái)
盈魁。比如前面提到的數(shù)組插入翔怎、刪除數(shù)據(jù)時(shí)需要搬移其他數(shù)據(jù)等。另外杨耙,它還有一個(gè)優(yōu)勢(shì)赤套,就是 支持動(dòng)態(tài)擴(kuò)容 。
數(shù)組本身在定義的時(shí)候需要預(yù)先指定大小珊膜,因?yàn)樾枰峙溥B續(xù)的內(nèi)存空間容握。如果我們申請(qǐng)了大小為10的數(shù)組,當(dāng)?shù)?1個(gè)數(shù)據(jù)需要存儲(chǔ)到數(shù)組中時(shí)车柠,我們就需要重新分配一塊更大的空間剔氏,將原來(lái)的數(shù)據(jù)復(fù)制過(guò)去,然后再將新的數(shù)據(jù)插入竹祷。
如果使用ArrayList介蛉,我們就完全不需要關(guān)心底層的擴(kuò)容邏輯,ArrayList已經(jīng)幫我們實(shí)現(xiàn)好了溶褪。每次存儲(chǔ)空間不夠的時(shí)候,它都會(huì)將空間自動(dòng)擴(kuò)容為1.5倍大小践险。
不過(guò)猿妈,這里需要注意一點(diǎn)吹菱,因?yàn)閿U(kuò)容操作涉及內(nèi)存申請(qǐng)和數(shù)據(jù)搬移,是比較耗時(shí)的彭则。所以鳍刷,如果事先能確定需要存儲(chǔ)的數(shù)據(jù)大小,最好
在創(chuàng)建ArrayList的時(shí)候事先指定數(shù)據(jù)大小 俯抖。
比如我們要從數(shù)據(jù)庫(kù)中取出10000條數(shù)據(jù)放入ArrayList输瓜。我們看下面這幾行代碼,你會(huì)發(fā)現(xiàn)芬萍,相比之下尤揣,事先指定數(shù)據(jù)大小可以省掉很多次內(nèi)存申請(qǐng)和數(shù)據(jù)搬移操作。
ArrayList<User> users = new ArrayList(10000);
for (int i = 0; i < 10000; ++i) {
users.add(xxx);
}
作為高級(jí)語(yǔ)言編程者柬祠,是不是數(shù)組就無(wú)用武之地了呢北戏?當(dāng)然不是,有些時(shí)候漫蛔,用數(shù)組會(huì)更合適些嗜愈,我總結(jié)了幾點(diǎn)自己的經(jīng)驗(yàn)。
1.Java
ArrayList無(wú)法存儲(chǔ)基本類(lèi)型莽龟,比如int蠕嫁、long,需要封裝為Integer毯盈、Long類(lèi)剃毒,而Autoboxing、Unboxing則有一定的性能消耗奶镶,所以如果特別關(guān)注性能迟赃,或者希望使用基本類(lèi)型,就可以選用數(shù)組厂镇。
2.如果數(shù)據(jù)大小事先已知纤壁,并且對(duì)數(shù)據(jù)的操作非常簡(jiǎn)單,用不到ArrayList提供的大部分方法捺信,也可以直接使用數(shù)組酌媒。
3.還有一個(gè)是我個(gè)人的喜好,當(dāng)要表示多維數(shù)組時(shí)迄靠,用數(shù)組往往會(huì)更加直觀(guān)秒咨。比如Object[][]
array;而用容器的話(huà)則需要這樣定義:ArrayList<ArrayList > array掌挚。
我總結(jié)一下雨席,對(duì)于業(yè)務(wù)開(kāi)發(fā),直接使用容器就足夠了吠式,省時(shí)省力陡厘。畢竟損耗一丟丟性能抽米,完全不會(huì)影響到系統(tǒng)整體的性能。但如果你是做一些非常底層的開(kāi)發(fā)糙置,比如開(kāi)發(fā)網(wǎng)絡(luò)框架云茸,性能的優(yōu)化需要做到極致,這個(gè)時(shí)候數(shù)組就會(huì)優(yōu)于容器谤饭,成為首選标捺。
現(xiàn)在我們來(lái)思考開(kāi)篇的問(wèn)題:為什么大多數(shù)編程語(yǔ)言中,數(shù)組要從0開(kāi)始編號(hào)揉抵,而不是從1開(kāi)始呢亡容?
從數(shù)組存儲(chǔ)的內(nèi)存模型上來(lái)看,“下標(biāo)”最確切的定義應(yīng)該是“偏移(offset)”功舀。前面也講到萍倡,如果用a來(lái)表示數(shù)組的首地址,a[0]就是偏移為0的位置辟汰,也就是首地址列敲,a[k]就表示偏移k個(gè)type_size的位置,所以計(jì)算a[k]的內(nèi)存地址只需要用這個(gè)公式:
a[k]_address = base_address + k * type_size
但是帖汞,如果數(shù)組從1開(kāi)始計(jì)數(shù)戴而,那我們計(jì)算數(shù)組元素a[k]的內(nèi)存地址就會(huì)變?yōu)椋?/p>
a[k]_address = base_address + (k-1)*type_size
對(duì)比兩個(gè)公式,我們不難發(fā)現(xiàn)翩蘸,從1開(kāi)始編號(hào)所意,每次隨機(jī)訪(fǎng)問(wèn)數(shù)組元素都多了一次減法運(yùn)算,對(duì)于CPU來(lái)說(shuō)催首,就是多了一次減法指令扶踊。
數(shù)組作為非常基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu)郎任,通過(guò)下標(biāo)隨機(jī)訪(fǎng)問(wèn)數(shù)組元素又是其非逞砗模基礎(chǔ)的編程操作,效率的優(yōu)化就要盡可能做到極致舶治。所以為了減少一次減法操作分井,數(shù)組選擇了從0開(kāi)始編號(hào),而不是從1開(kāi)始霉猛。
不過(guò)我認(rèn)為尺锚,上面解釋得再多其實(shí)都算不上壓倒性的證明,說(shuō)數(shù)組起始編號(hào)非0開(kāi)始不可惜浅。所以我覺(jué)得最主要的原因可能是歷史原因瘫辩。
C語(yǔ)言設(shè)計(jì)者用0開(kāi)始計(jì)數(shù)數(shù)組下標(biāo),之后的Java、JavaScript等高級(jí)語(yǔ)言都效仿了C語(yǔ)言伐厌,或者說(shuō)阅仔,為了在一定程度上減少C語(yǔ)言程序員學(xué)習(xí)Java的學(xué)習(xí)成本,因此繼續(xù)沿用了從0開(kāi)始計(jì)數(shù)的習(xí)慣弧械。實(shí)際上,很多語(yǔ)言中數(shù)組也并不是從0開(kāi)始計(jì)數(shù)的空民,比如Matlab刃唐。甚至還有一些語(yǔ)言支持負(fù)數(shù)下標(biāo),比如Python界轩。
內(nèi)容小結(jié)
我們今天學(xué)習(xí)了數(shù)組画饥。它可以說(shuō)是最基礎(chǔ)、最簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu)了浊猾。數(shù)組用一塊連續(xù)的內(nèi)存空間抖甘,來(lái)存儲(chǔ)相同類(lèi)型的一組數(shù)據(jù),最大的特點(diǎn)就是支持隨機(jī)訪(fǎng)問(wèn)葫慎,但插入衔彻、刪除操作也因此變得比較低效,平均情況時(shí)間復(fù)雜度為O(n)偷办。在平時(shí)的業(yè)務(wù)開(kāi)發(fā)中艰额,我們可以直接使用編程語(yǔ)言提供的容器類(lèi),但是椒涯,如果是特別底層的開(kāi)發(fā)柄沮,直接使用數(shù)組可能會(huì)更合適。
前面我基于數(shù)組的原理引出JVM的標(biāo)記清除垃圾回收算法的核心理念废岂。我不知道你是否使用Java語(yǔ)言祖搓,理解JVM,如果你熟悉湖苞,可以在評(píng)論區(qū)回顧下你理解的標(biāo)記清除垃圾回收算法拯欧。
前面我們講到一維數(shù)組的內(nèi)存尋址公式,那你可以思考一下袒啼,類(lèi)比一下哈扮,二維數(shù)組的內(nèi)存尋址公式是怎樣的呢?
歡迎留言和我分享蚓再,我會(huì)第一時(shí)間給你反饋滑肉。