溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

深入iOS系統(tǒng)底層之XCODE對(duì)匯編的支持介紹

發(fā)布時(shí)間:2020-07-01 03:20:29 來(lái)源:網(wǎng)絡(luò) 閱讀:992 作者:歐陽(yáng)大哥 欄目:移動(dòng)開(kāi)發(fā)

工欲善其事必先利其器 --《論語(yǔ)·衛(wèi)靈公》

一個(gè)好的IDE不僅要提供舒適簡(jiǎn)潔和方便的源代碼編輯環(huán)境,還要提供功能強(qiáng)大的調(diào)試環(huán)境。XCODE是目前來(lái)說(shuō)對(duì)iOS應(yīng)用開(kāi)發(fā)支持的最好的IDE(雖然Visual Studio2017也開(kāi)始支持iOS應(yīng)用的開(kāi)發(fā)了),畢竟XCODE和iOS都是蘋果公司的親生兒子。唯一要吐槽的就是系統(tǒng)和編譯環(huán)境綁的太死了,每當(dāng)手機(jī)操作系統(tǒng)的一個(gè)小升級(jí),都需要去升級(jí)一個(gè)好幾G的新版本程序,這確實(shí)是有點(diǎn)坑爹!
目前市面上有很多反編譯的工具,比如IDA、Hopper Disassembler等還有操作系統(tǒng)自帶的工具諸如otool、lldb。這些工具里面有的擅長(zhǎng)靜態(tài)分析有的擅長(zhǎng)調(diào)試的,這里就不展開(kāi)分析了。如果在程序運(yùn)行時(shí)去窺探一些系統(tǒng)內(nèi)部實(shí)現(xiàn)以及做實(shí)時(shí)調(diào)試分析我覺(jué)得XCODE本身也非常的棒,既然深入系統(tǒng)我們必須要了解和學(xué)習(xí)一些關(guān)于匯編的東西,那么就必須要了解和掌握一些工具,而XCODE其實(shí)就是你手頭上最方便的工具之一。

XCODE的匯編模式切換

你是否在聯(lián)機(jī)運(yùn)行時(shí)因?yàn)橄到y(tǒng)崩潰而出現(xiàn)過(guò)如下的畫面:

深入iOS系統(tǒng)底層之XCODE對(duì)匯編的支持介紹

不要慌!它其實(shí)就是XCODE的匯編模式的界面。我們不僅在程序崩潰時(shí)可以看到它,我們也可以人為的進(jìn)入到這個(gè)界面模式里面。這篇文章更像是一個(gè)XCODE工具使用上的一些介紹,您可以經(jīng)常在使用它們,也可能還從來(lái)沒(méi)有接觸和了解過(guò)它們。對(duì)于匯編代碼和源代碼之間的切換可以通過(guò)菜單:Debug -> Debug Workflow -> Always Show Disassembly 來(lái)完成。

深入iOS系統(tǒng)底層之XCODE對(duì)匯編的支持介紹

記得要設(shè)置有斷點(diǎn)并運(yùn)行到斷點(diǎn)處時(shí)切換才能看到匯編指令??!

上一篇文章深入iOS系統(tǒng)底層之指令集介紹中我們有說(shuō)過(guò)模擬器上運(yùn)行的是Intel指令,而真機(jī)上運(yùn)行的是arm指令,在這里我們分別看模擬器和真機(jī)下的匯編指令的差異性:
深入iOS系統(tǒng)底層之XCODE對(duì)匯編的支持介紹

深入iOS系統(tǒng)底層之XCODE對(duì)匯編的支持介紹

深入iOS系統(tǒng)底層之XCODE對(duì)匯編的支持介紹

通過(guò)上面三張圖你會(huì)發(fā)現(xiàn)其中的源代碼和匯編代碼之間有很大的差異,以及不同指令集下的匯編代碼之間也有很大的差異!匯編代碼的差異其實(shí)就是不同CPU上運(yùn)行的指令的差異。還記得前一篇文章所說(shuō)的指令集嗎?前者是在模擬器上運(yùn)行的所以展示的是x64的指令,而后者是在真機(jī)上運(yùn)行的因此展示的是arm64指令。通過(guò)圖片對(duì)比你能否發(fā)現(xiàn)他們之間的相同點(diǎn)和差異嗎?

  • 系統(tǒng)所有的代碼都是由一個(gè)個(gè)的函數(shù)或者說(shuō)方法組成,即使是類中定義的方法以及Block里面的方法也是如此。在編譯時(shí)系統(tǒng)將所有定義的函數(shù)方法依次編譯鏈接為機(jī)器指令并保存到文件的代碼段中,一個(gè)函數(shù)內(nèi)的機(jī)器指令是連續(xù)存儲(chǔ)的,但是函數(shù)之間卻不一定是連續(xù)存儲(chǔ)的。
  • 上面的圖片中每條匯編指令都和一條機(jī)器指令唯一對(duì)應(yīng),這里要注意的是雖然顯示的是匯編代碼,但是真實(shí)存儲(chǔ)和運(yùn)行的還是機(jī)器代碼,只不過(guò)我們通過(guò)匯編代碼來(lái)展示能夠容易閱讀和理解而已。
  • 每條指令前面的地址表示的是這條指令在運(yùn)行時(shí)所處在的內(nèi)存地址。也許你會(huì)問(wèn)指令不是在CPU上嗎?沒(méi)有錯(cuò),指令雖然是在CPU上執(zhí)行,但是存儲(chǔ)還是要在內(nèi)存或者磁盤上。CPU上有一個(gè)叫ip(Intel)或者pc(arm)的寄存器保存著下一條將要執(zhí)行的指令的內(nèi)存地址,這樣每執(zhí)行一條指令時(shí)都是從ip/pc中所指定的內(nèi)存地址讀取出指令并執(zhí)行,并同時(shí)將當(dāng)前指令的下一條繼續(xù)保存在ip/pc上,就這樣不停重復(fù)的方式來(lái)完成指令的執(zhí)行(實(shí)際上CPU為了加快處理速度會(huì)將一部分內(nèi)存中的指令緩存到CPU的內(nèi)部緩存中去,而不是每條指令都從內(nèi)存中讀取)。
  • 每個(gè)函數(shù)方法的第一個(gè)地址,就是這個(gè)函數(shù)的入口地址,也就是說(shuō)我們進(jìn)行函數(shù)調(diào)用時(shí),實(shí)際上是讓CPU跳轉(zhuǎn)到這個(gè)地址并執(zhí)行,更加具體的就是將ip/pc寄存器的值設(shè)置為這個(gè)函數(shù)的入口地址。 對(duì)于OC類中的方法來(lái)說(shuō)方法入口地址其實(shí)就是這個(gè)方法的IMP。
  • 在模擬器下你會(huì)發(fā)現(xiàn)每條指令的長(zhǎng)度是不一樣的,有1個(gè)字節(jié)到7個(gè)字節(jié)不等,所以你看到的每條指令的偏移量都不一樣,而真機(jī)時(shí)你會(huì)發(fā)現(xiàn)每條指令的長(zhǎng)度總是固定為4個(gè)字節(jié)。這其實(shí)就是CISC和RISC指令集中的一個(gè)非常顯著的差別:CISC指令長(zhǎng)度不固定而RISC指令則長(zhǎng)度固定。你還會(huì)發(fā)現(xiàn)模擬器下的匯編代碼數(shù)量要比真機(jī)下的匯編代碼數(shù)量要少,這也是CISC指令和RISC指令的差別:CISC指令復(fù)雜而且眾多,一條指令完成的功能要比RISC多;而RISC則指令簡(jiǎn)單,因此某些功能需要多條指令來(lái)完成。
  • 在匯編模式下的注釋都是由;號(hào)開(kāi)頭的。大家在通過(guò)匯編語(yǔ)言研究?jī)?nèi)部實(shí)現(xiàn)時(shí)建議看模擬器下的AT&T匯編,原因其實(shí)就是模擬器下運(yùn)行的匯編注釋要比真機(jī)模式下的匯編指令要詳細(xì)一些。
  • 每條匯編指令的格式總是由: 操作碼, 操作數(shù)1,操作數(shù)2,操作數(shù)3組成。 操作數(shù)要么就是常數(shù),要么就是寄存儲(chǔ)器,要么就是內(nèi)存地址。你所看到的操作數(shù)中的RAX,RSI,RDI,R0,R1... 這些都是CPU中的寄存器(關(guān)于寄存器部分我將在下一篇文章中具體介紹)。而且在XCODE的左下角部分我們可以查看當(dāng)前CPU中的所有寄存器的值,你可以打印并修改他們。

斷點(diǎn)

可能有的同學(xué)會(huì)說(shuō)為什么我打開(kāi)了匯編模式我還是看不到匯編代碼?那是因?yàn)槟銢](méi)有給你的代碼設(shè)置斷點(diǎn)!什么是斷點(diǎn)?為什么設(shè)置了斷點(diǎn)程序就會(huì)暫停運(yùn)行? 一般情況下CPU總是按照順序依次執(zhí)行指令并完成任務(wù),當(dāng)正在執(zhí)行某個(gè)任務(wù)時(shí)如果遇到了特殊事件或者更高優(yōu)先級(jí)的任務(wù)時(shí)就需要打斷現(xiàn)有執(zhí)行的代碼并去執(zhí)行優(yōu)先級(jí)更高的代碼,這種機(jī)制就是中斷。中斷有因?yàn)橥獠坑布O(shè)備事件而產(chǎn)生的硬中斷, 同時(shí)CPU也提供一個(gè)軟中斷指令。當(dāng)在代碼里面執(zhí)行一條軟中斷指令時(shí),程序就會(huì)暫停運(yùn)行,同時(shí)CPU把操作權(quán)限提交給操作系統(tǒng)來(lái)執(zhí)行中斷處理程序。當(dāng)我們?cè)诔绦蚰程幵O(shè)置了斷點(diǎn)或者某個(gè)指令處設(shè)置斷點(diǎn)時(shí),系統(tǒng)會(huì)將斷點(diǎn)處的指令保存到一個(gè)臨時(shí)的斷點(diǎn)列表中,同時(shí)將斷點(diǎn)處的指令替換為軟中斷指令,這樣當(dāng)程序運(yùn)行到斷點(diǎn)處時(shí)因?yàn)閳?zhí)行的其實(shí)是軟中斷指令,而導(dǎo)致系統(tǒng)調(diào)用的發(fā)生,并執(zhí)行軟中斷處理程序,軟中斷處理程序等待用戶處理斷點(diǎn)處的操作,比如當(dāng)用戶按下的是鍵盤上的Ctrl + F7時(shí),軟中斷處理程序就會(huì)把保存在臨時(shí)斷點(diǎn)列表中真實(shí)斷點(diǎn)處的指令恢復(fù)到指定的內(nèi)存,同時(shí)把下次要執(zhí)行的指令改為真實(shí)的指令,然后再次執(zhí)行真實(shí)的指令,這樣就完成了斷點(diǎn)處指令的繼續(xù)執(zhí)行。(要想了解斷點(diǎn)的具體實(shí)現(xiàn),需要具有一些匯編的知識(shí),這里就不展開(kāi)了,后面我會(huì)在專門的章節(jié)里面詳解介紹斷點(diǎn)的實(shí)現(xiàn)原理)。

符號(hào)斷點(diǎn)

當(dāng)我們?cè)诔绦虼a某處設(shè)置了斷點(diǎn)或者指令某處設(shè)置了斷點(diǎn)后,程序執(zhí)行到斷點(diǎn)處時(shí)就會(huì)暫停下來(lái)。這時(shí)候如果我們是在匯編模式下,您看到的就是匯編程序斷點(diǎn),而當(dāng)你在源代碼模式下時(shí),你看到的將是源代碼斷點(diǎn)。 除了在代碼處設(shè)置斷點(diǎn)外我們還可以設(shè)置符號(hào)斷點(diǎn)。我們先來(lái)考察下面3個(gè)應(yīng)用場(chǎng)景:

  1. 我們程序的某個(gè)視圖的frame值在運(yùn)行時(shí)不知道什么原因總是被莫名其妙的改變了,但是你就是不知道在哪里執(zhí)行了視圖frame的更改設(shè)置。這時(shí)候一個(gè)解決方法就是重載setFrame方法并設(shè)置斷點(diǎn)來(lái)調(diào)試查看frame被何時(shí)調(diào)用。

  2. 我們的上線程序出現(xiàn)了在某個(gè)系統(tǒng)方法被調(diào)用時(shí)的crash問(wèn)題,但是因?yàn)槭窍到y(tǒng)的方法我們無(wú)法看到其中的源代碼,從而無(wú)法進(jìn)行crash問(wèn)題分析(比如我們遇到的很多沒(méi)有上下文的crash).

  3. 假如我懂匯編語(yǔ)言,我想研究一下系統(tǒng)框架的某個(gè)方法是如何實(shí)現(xiàn)的。

上面的三個(gè)問(wèn)題我不知道大家會(huì)如何去解決? 其實(shí)這三種場(chǎng)景我們都可以借助于符號(hào)斷點(diǎn)來(lái)完成。一般情況下我們可以在源代碼某處設(shè)置斷點(diǎn)來(lái)調(diào)試程序,對(duì)于沒(méi)有源代碼的情況下我們則可以通過(guò)設(shè)置符號(hào)斷點(diǎn)來(lái)實(shí)現(xiàn)程序的調(diào)試和運(yùn)行。要設(shè)置符號(hào)斷點(diǎn)很簡(jiǎn)單。你只需要在XCODE的菜單:Debug -> Breakpoints -> Create Symbolic Breakpoint 或者快捷鍵:option + command + \ 來(lái)建立符號(hào)斷點(diǎn):
深入iOS系統(tǒng)底層之XCODE對(duì)匯編的支持介紹

建立符號(hào)斷點(diǎn)后,當(dāng)某個(gè)與符號(hào)名相同某個(gè)函數(shù)或者方法在執(zhí)行開(kāi)始前就會(huì)產(chǎn)生斷點(diǎn),從而可以窺探某個(gè)方法的內(nèi)部實(shí)現(xiàn)。還可以幫助我們對(duì)那些沒(méi)有上下文以及非源代碼處產(chǎn)生的崩潰進(jìn)行分析和重現(xiàn),從而幫助我們定位問(wèn)題。下面是運(yùn)行符號(hào)斷點(diǎn)后的我們看到的兩處符號(hào)斷點(diǎn)的匯編語(yǔ)言內(nèi)容:

VCTest1`-[ViewController setA:]:
->  0x1029855e0 <+0>:  sub    sp, sp, #0x20             ; =0x20 
    0x1029855e4 <+4>:  adrp   x8, 4
    0x1029855e8 <+8>:  add    x8, x8, #0x70             ; =0x70 
    0x1029855ec <+12>: str    x0, [sp, #0x18]
    0x1029855f0 <+16>: str    x1, [sp, #0x10]
    0x1029855f4 <+20>: str    w2, [sp, #0xc]
    0x1029855f8 <+24>: ldr    w2, [sp, #0xc]
    0x1029855fc <+28>: ldr    x0, [sp, #0x18]
    0x102985600 <+32>: ldrsw  x8, [x8]
    0x102985604 <+36>: add    x8, x0, x8
    0x102985608 <+40>: str    w2, [x8]
    0x10298560c <+44>: add    sp, sp, #0x20             ; =0x20 
    0x102985610 <+48>: ret    

-----------------

libsystem_c.dylib`abs:
->  0x1813dd984 <+0>: cmp    w0, #0x0                  ; =0x0 
    0x1813dd988 <+4>: cneg   w0, w0, mi
    0x1813dd98c <+8>: ret    

你是否看到了屬性setA的內(nèi)部實(shí)現(xiàn)以及函數(shù)abs的內(nèi)部實(shí)現(xiàn)了?

調(diào)試

調(diào)試程序是一個(gè)程序員應(yīng)該掌握的最基本的工夫,這里就不介紹其他的詳細(xì)的調(diào)試命令以及方法,其他很多文章里面都有介紹了。主要介紹一下調(diào)試代碼時(shí)單步運(yùn)行的幾個(gè)菜單和快捷鍵:

  • 源代碼模式下
 F7 :  代碼單步執(zhí)行,當(dāng)遇到函數(shù)調(diào)用時(shí)會(huì)跳入函數(shù)內(nèi)部。
 F6:   代碼單獨(dú)執(zhí)行,當(dāng)遇到函數(shù)調(diào)用時(shí)不會(huì)跳入函數(shù)內(nèi)部。
 F8:   跳出函數(shù)執(zhí)行,返回到調(diào)用此函數(shù)的下一句代碼。
  • 匯編模式下
    control + F7 :  指令單步執(zhí)行,當(dāng)遇到函數(shù)調(diào)用時(shí)會(huì)跳入函數(shù)內(nèi)部。
    control + F6:   指令單獨(dú)執(zhí)行,當(dāng)遇到函數(shù)調(diào)用時(shí)不會(huì)跳入函數(shù)內(nèi)部。
  • 多線程之間的切換:
 control + shift + F7:  切換到當(dāng)前線程,并執(zhí)行單步指令。
 control  + shift + F6:  切換到當(dāng)前線程,并跳轉(zhuǎn)到函數(shù)調(diào)用的者的下一條指令。

在調(diào)試運(yùn)行時(shí)當(dāng)出現(xiàn)斷點(diǎn)時(shí)我們可以在lldb命令行中輸入各種調(diào)試命令,其他的不介紹,就單獨(dú)介紹一下expr命令。expr命令其實(shí)是p或者po的完整版本,通過(guò)expr命令除了能夠用來(lái)顯示外,還可以用來(lái)進(jìn)行數(shù)據(jù)的修改、方法的調(diào)用等強(qiáng)大能力。下面展示一下一些常用的expr方法:

   expr   變量|表達(dá)式              //顯示變量或者表達(dá)式的值。
   expr -f h --  變量|表達(dá)式     //以16進(jìn)制格式顯示變量或表達(dá)式的內(nèi)容
   expr -f b --  變量|表達(dá)式    //以二進(jìn)制格式顯示變量或者表達(dá)式的內(nèi)容。
   expr -o --  oc對(duì)象              //等價(jià)于po  oc對(duì)象
   expr -P  3 -- oc對(duì)象           //上面命令的加強(qiáng)版本,他還會(huì)顯示出對(duì)象內(nèi)數(shù)據(jù)成員的結(jié)構(gòu),具體的P后面的數(shù)字就是你要想顯示的層次。
   expr my_struct->a = my_array[3]    //給my_struct的a成員賦值。
   expr (char*)_cmd           //顯示某個(gè)oc方法的方法名。
   expr (IMP)[self methodForSelector:_cmd]    //執(zhí)行某個(gè)方法調(diào)用.

查看內(nèi)存地址

程序運(yùn)行時(shí),操作系統(tǒng)為其構(gòu)建出一個(gè)進(jìn)程,同時(shí)構(gòu)建出一個(gè)虛擬的內(nèi)存空間。操作系統(tǒng)將進(jìn)程中的虛擬內(nèi)存空間劃分為代碼存儲(chǔ)區(qū)域、全局?jǐn)?shù)據(jù)存儲(chǔ)區(qū)域、堆存儲(chǔ)區(qū)域、棧存儲(chǔ)區(qū)域等區(qū)域。每種區(qū)域都有特殊的用途:代碼存儲(chǔ)區(qū)域保存的是程序中的代碼部分(這部分也可稱為映像image);全局?jǐn)?shù)據(jù)存儲(chǔ)區(qū)域保存的是一些全局?jǐn)?shù)據(jù)、常量以及一些描述信息(比如runtime里面的所有OC類的定義描述信息也是存儲(chǔ)在這個(gè)區(qū)域中);堆存儲(chǔ)區(qū)域則用來(lái)進(jìn)行堆內(nèi)存的動(dòng)態(tài)分配;棧存儲(chǔ)區(qū)域則保存著函數(shù)中的局部變量。因此可以看出無(wú)論是代碼和數(shù)據(jù)在運(yùn)行時(shí)都保存在內(nèi)存中。每個(gè)進(jìn)程能訪問(wèn)的內(nèi)存空間的尺寸大小由操作系統(tǒng)決定,一般來(lái)說(shuō)32位的操作系統(tǒng)中每個(gè)進(jìn)程的內(nèi)存空間為2^32 = 4GB;而64位的操作系統(tǒng)中每個(gè)進(jìn)程的內(nèi)存空間為2^64 = 4TB。需要注意的是這個(gè)空間是虛擬的可訪問(wèn)空間并不是真實(shí)的物理內(nèi)存可訪問(wèn)的空間,操作系統(tǒng)內(nèi)部通過(guò)分頁(yè)映射的方式將虛擬空間轉(zhuǎn)化為真實(shí)的物理空間。
進(jìn)程的虛擬內(nèi)存空間是一個(gè)可以連續(xù)存儲(chǔ)和訪問(wèn)的線性空間,為了能夠訪問(wèn)這些內(nèi)存空間,操作系統(tǒng)為其進(jìn)行了編碼,這個(gè)編碼就是內(nèi)存的地址。地址也被稱為指針,因此我們所說(shuō)的某個(gè)變量的指針其實(shí)就是這個(gè)變量在內(nèi)存中的地址。為了更好的理解內(nèi)存和地址的概念,你可以將內(nèi)存理解為一個(gè)數(shù)組,而地址則是訪問(wèn)這個(gè)數(shù)組元素時(shí)所用到的索引。我們對(duì)數(shù)組中元素的讀寫操作總是通過(guò)索引進(jìn)行,同樣CPU對(duì)內(nèi)存中的數(shù)據(jù)訪問(wèn)時(shí)也是通過(guò)內(nèi)存地址進(jìn)行的。進(jìn)程中的內(nèi)存地址總是從0開(kāi)始編碼,并以字節(jié)為單位進(jìn)行遞增,直到虛擬內(nèi)存空間的上限。
上面說(shuō)過(guò)進(jìn)程中的代碼和數(shù)據(jù)都保存在內(nèi)存中,當(dāng)我們要想一覽整個(gè)進(jìn)程內(nèi)存中的代碼和數(shù)據(jù)時(shí),你可以在程序運(yùn)行時(shí)通過(guò)菜單:Debug -> Debug Workflow -> View Memory 或者通過(guò)快捷鍵:shift+command + m 來(lái)調(diào)用內(nèi)存查看界面:

深入iOS系統(tǒng)底層之XCODE對(duì)匯編的支持介紹

上面的圖片剛好展示的是一個(gè)類的所有方法名稱在內(nèi)存中的位置和布局??梢钥闯鑫覀兛梢院芊奖愕慕柚榭磧?nèi)存地址菜單的功能來(lái)了解以及分析代碼以及數(shù)據(jù)在內(nèi)存中的結(jié)構(gòu)。你可以在地址輸入欄中輸入你想查看的任意內(nèi)存地址。比如你想查看某個(gè)函數(shù)代碼的機(jī)器指令,那么你只需要在匯編模式下將函數(shù)最開(kāi)始的地址輸入到內(nèi)存查看界面的地址欄中,那么就會(huì)展示出這個(gè)函數(shù)代碼的所有機(jī)器指令字節(jié)碼。這里還要注意一點(diǎn)的是因?yàn)閮?nèi)存地址是從低位按字節(jié)依次排列而來(lái),所以對(duì)于比如int類型的值的讀取我們就要從高位到低位開(kāi)始讀取。

計(jì)算器 應(yīng)用

程序調(diào)試時(shí)代碼和地址以及一些數(shù)據(jù)都經(jīng)常以16進(jìn)制的形式顯示。數(shù)據(jù)處理時(shí),尤其是計(jì)算地址偏移都以16進(jìn)制的形式進(jìn)行展示。你可以在lldb中通過(guò)expr或者p命令來(lái)計(jì)算。如果你喜歡界面形式的工具,則可以啟動(dòng)mac OS操作系統(tǒng)中的應(yīng)用:計(jì)算器 來(lái)處理各種計(jì)算,你要做的就是在顯示菜單中選擇編程型即可,編程型界面的效果如下(別告訴我作為一個(gè)程序員的你不會(huì)操作這些功能):

深入iOS系統(tǒng)底層之XCODE對(duì)匯編的支持介紹

bc 命令

如果你喜歡命令行的方式來(lái)做計(jì)算,那么還可以介紹給你一個(gè)系統(tǒng)提供的命令式計(jì)算工具:bc。這個(gè)工具的官方定義是:一個(gè)任意精度計(jì)算器語(yǔ)言(An arbitrary precision calculator language)。我們可以以交互的方式進(jìn)入bc:
bc -i

深入iOS系統(tǒng)底層之XCODE對(duì)匯編的支持介紹

使用bc時(shí)你可以通過(guò)ibase = [2|8|10|16]的值來(lái)指定輸入數(shù)字的進(jìn)制,可以通過(guò)指定obase=[2|8|10|16]的值來(lái)指定輸出數(shù)字的顯示格式。你還可以通過(guò)scale=n來(lái)指定輸出的小數(shù)位數(shù),你可以在里面用表達(dá)式、函數(shù)、運(yùn)算符、甚至可以定義變量和函數(shù)??梢钥闯鯾c可不是只有計(jì)算的功能這么簡(jiǎn)單,你可以用bc來(lái)編寫程序?。【唧wbc的使用你可以在終端下執(zhí)行 man bc 查看bc的使用手冊(cè)。下面是一段用bc語(yǔ)言寫的代碼(請(qǐng)?jiān)趫?zhí)行了bc -i 命令后編寫如下代碼):

sum = 0
for (i = 0; i < 100; i++)
{
   sum += i
}
sum

敬請(qǐng)期待下一篇:深入iOS系統(tǒng)底層之CPU寄存器介紹


目錄
1.深入iOS系統(tǒng)底層之匯編語(yǔ)言
2.深入iOS系統(tǒng)底層之指令集介紹
3.深入iOS系統(tǒng)底層之XCODE對(duì)匯編的支持介紹
4.深入iOS系統(tǒng)底層之CPU寄存器介紹
5.深入iOS系統(tǒng)底層之機(jī)器指令介紹
6.深入iOS系統(tǒng)底層之賦值指令介紹
7.深入iOS系統(tǒng)底層之函數(shù)調(diào)用介紹
8.深入iOS系統(tǒng)底層之其他常用指令介紹
9.深入iOS系統(tǒng)底層之函數(shù)棧介紹
10.深入iOS系統(tǒng)底層之函數(shù)棧(二)介紹
11.深入iOS系統(tǒng)底層之不定參數(shù)函數(shù)實(shí)現(xiàn)原理介紹
12.深入iOS系統(tǒng)底層之在高級(jí)語(yǔ)言中嵌入?yún)R編語(yǔ)言介紹
13.深入iOS系統(tǒng)底層之常見(jiàn)的匯編代碼片段介紹
14.深入iOS系統(tǒng)底層之OC中的各種屬性以及修飾的實(shí)現(xiàn)介紹
15.深入iOS系統(tǒng)底層之ABI介紹
16.深入iOS系統(tǒng)底層之編譯鏈接過(guò)程介紹
17.深入iOS系統(tǒng)底層之可執(zhí)行文件結(jié)構(gòu)介紹
18.深入iOS系統(tǒng)底層之MACH-O文件格式介紹
19.深入iOS系統(tǒng)底層之映像文件操作API介紹
20.深入iOS系統(tǒng)底層之知名load command結(jié)構(gòu)介紹
21.深入iOS系統(tǒng)底層之程序加載過(guò)程介紹
22.深入iOS系統(tǒng)底層之靜態(tài)庫(kù)介紹
23.深入iOS系統(tǒng)底層之動(dòng)態(tài)庫(kù)介紹
24.深入iOS系統(tǒng)底層之framework介紹
25.深入iOS系統(tǒng)底層之基地址介紹
26.深入iOS系統(tǒng)底層之模塊內(nèi)函數(shù)調(diào)用介紹
27.深入iOS系統(tǒng)底層之模塊間函數(shù)調(diào)用介紹
28.深入iOS系統(tǒng)底層之機(jī)器指令動(dòng)態(tài)構(gòu)造介紹
29.深入iOS系統(tǒng)底層之crash問(wèn)題解決方法
30.深入iOS系統(tǒng)底層之無(wú)上下文crash解決方法
31.深入iOS系統(tǒng)底層之常用工具和命令的實(shí)現(xiàn)原理介紹
32.深入iOS系統(tǒng)底層之真實(shí)的OC類內(nèi)存結(jié)構(gòu)介紹


歡迎大家訪問(wèn)我的github地址和簡(jiǎn)書(shū)地址

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI