天天微速訊:C函數(shù)調(diào)用機(jī)制與棧幀原理詳解
當(dāng)一個C函數(shù)被調(diào)用時,函數(shù)的參數(shù)如何傳遞、堆棧指針如何變化、棧幀是如何被建立以及如何被消除的,一直缺乏系統(tǒng)性的理解,因此決定花時間學(xué)習(xí)下函數(shù)調(diào)用時整個調(diào)用機(jī)制并總結(jié)成文,以便加深理解。本文將從匯編的角度講解函數(shù)調(diào)用時,堆棧的變化,參數(shù)的傳遞方式、以及棧幀的建立和消除等方面知識。
這些細(xì)節(jié)跟操作系統(tǒng)平臺及編譯器的實現(xiàn)有關(guān),下面的描述是針對運行在 Intel至強(qiáng)處理器芯片上 Linux的 gcc 編譯器而言。C語言的標(biāo)準(zhǔn)并沒有描述實現(xiàn)的方式,所以,不同的編譯器,處理器,操作系統(tǒng)都可能有自己的建立棧幀的方式。
【資料圖】
堆棧指針及相關(guān)寄存器
堆棧是操作系統(tǒng)中,最為常見的一種數(shù)據(jù)結(jié)構(gòu)。嚴(yán)謹(jǐn)?shù)恼f,堆棧是包括堆和棧兩種數(shù)據(jù)結(jié)構(gòu)的,但是我們通常說堆棧,其實就是指棧。在棧中,最重要的兩個指針是 SP(棧指針) 和 BP(基址指針)。
SP(Stack Pointer),棧指針,在 32 位系統(tǒng)中,ESP(Extended SP) 寄存器存放的就是棧指針。在 64 位系統(tǒng)中,表現(xiàn)為 RSP 寄存器。SP 永遠(yuǎn)指向系統(tǒng)棧最上面一個棧幀的棧頂。所以 SP 是棧頂指針。(SP與ESP共用相同寄存器,SP是ESP的低16位,ESP是32位) BP(Base Pointer),基址指針,在 32 位系統(tǒng)中,EBP(Extended BP)寄存器存放的就是基址指針。在 64 位系統(tǒng)中,表現(xiàn)為 RBP 寄存器。BP 指向棧幀的底部,一般稱之為棧底指針。(BP與EBP共用相同寄存器,BP是ESP的低16位,EBP是32位)
注:由于當(dāng)下主要使用32位及以上寄存器,因此本文以32位寄存器講解為主,下文亦主要使用ESP,EBP為例進(jìn)行介紹。
這些指針及寄存器的作用到底是什么呢?ESP,指針即地址,存放棧頂指針,目的就是,下一次對棧操作的時候,系統(tǒng)可以及時找到棧的當(dāng)前位置。舉個例子來說,push 壓入一個操作數(shù),會在 esp - 4 的地址的內(nèi)存空間,存入一個2個字長的操作數(shù)。EBP 的作用,會在下文講述。
因本文后續(xù)可能會用到很多通用寄存器,為防止讀者不懂寄存器的含義,這里小編整理了通用寄存器的功能和名稱對應(yīng)關(guān)系表,如忘記了可返回查看該表,表格如下圖:
函數(shù)調(diào)用匯編指令
一個函數(shù)調(diào)用另外一個函數(shù),堆棧到底是怎么樣變化的呢?ESP和EBP是如何變更的?函數(shù)形參又是如何傳遞的呢?后面我們會寫一個簡單的Demo程序,加深對堆棧相關(guān)寄存器的理解。在一個函數(shù)中調(diào)用另外一個函數(shù),匯編指令往往有以下幾個步驟:
匯編指令 | 指令歸屬函數(shù) | ESP 變化 | 作用 |
---|---|---|---|
push arg3 | 主調(diào)函數(shù) | esp-4 | 將被調(diào)函數(shù)參數(shù)3壓入棧中,供被調(diào)函數(shù)執(zhí)行時使用。 |
push arg2 | 主調(diào)函數(shù) | esp-4 | 將被調(diào)函數(shù)參數(shù)2壓入棧中,供被調(diào)函數(shù)執(zhí)行時使用。 |
push arg1 | 主調(diào)函數(shù) | esp-4 | 將被調(diào)函數(shù)參數(shù)3壓入棧中,供被調(diào)函數(shù)執(zhí)行時使用。 |
call function | 主調(diào)函數(shù) | esp-4 | 開始調(diào)用被調(diào)函數(shù),同時保存返回地址。 |
push ebp | 被調(diào)函數(shù) | esp-4 | 將主調(diào)函數(shù)的ebp中的基址值壓入棧中,以便被調(diào)函數(shù)執(zhí)行完畢后,恢復(fù)主調(diào)函數(shù)的基址到ebp。 |
mov ebp, esp | 被調(diào)函數(shù) | 無變化 | 將當(dāng)前esp(esp此時指向本函數(shù)棧幀的棧底)的值存入ebp寄存器,目的是讓被調(diào)函數(shù)的基址指針指向本函數(shù)的棧幀的棧底,后續(xù)可通過ebp來定位函數(shù)參數(shù)。 |
sub esp, #num | 被調(diào)函數(shù) | esp-num | 為被調(diào)函數(shù)分配??臻g |
... | 被調(diào)函數(shù) | ... | 被調(diào)函數(shù)的具體實現(xiàn)邏輯 |
pop ebp | 被調(diào)函數(shù) | esp+4 | 將棧中保存的主調(diào)函數(shù)的基址地址彈出棧,保存到ebp寄存器。 |
ret | 被調(diào)函數(shù) | sp+4 | 將棧中保存的被調(diào)函數(shù)調(diào)用處的下一條指令的地址彈出棧并保存至eip寄存器,同時esp+4 |
說明
push arg 在調(diào)用一個函數(shù)之前,需要把傳遞的參數(shù)壓入棧。每次 push 之后,棧多了2個字長(32 位系統(tǒng) --> 4 字節(jié)),因此棧頂需要往上移動 4 字節(jié),該指令暗含 sub esp, #4call call 指令用來調(diào)用某個函數(shù),該指令含有兩個操作(1)將返回地址壓入棧;(2)esp = esp - 4push ebp; mov ebp, esp 這樣的操作,會經(jīng)常出現(xiàn)在各個函數(shù)反匯編的開頭,保存上一個函數(shù)棧的基址,并更新本函數(shù)的基址ret,即 return,此時 esp 應(yīng)該指向 call 指令壓入的返回地址;執(zhí)行 ret 其實就是將此時棧中的數(shù)據(jù)彈出,存至 eip 寄存器。eip 存放的是當(dāng)前被調(diào)用函數(shù)被調(diào)用位置處的下一條即將執(zhí)行的指令的地址(即返回地址)。同時 esp = esp + 4ret 指令相當(dāng)于 pop eip; esp = esp + 4
call 指令相當(dāng)于 push eip; esp = esp - 4
通過以上匯編代碼及解釋說明,你可能還是不能完全了解函數(shù)調(diào)用過程堆棧的變化,沒關(guān)系,我們看下一個典型的棧幀是如何構(gòu)成的,見下圖。
圖1
綠色表示調(diào)用函數(shù)的匯編指令和棧空間, 藍(lán)色表示被調(diào)用函數(shù)的匯編指令和相應(yīng)的??臻g。紅色箭頭表示通過被調(diào)用函數(shù)的ebp訪問被調(diào)用函數(shù)的參數(shù)以及局部變量。
上圖棧頂在下,棧底在上,??臻g由高地址向低地址增長。
如下函數(shù)的調(diào)用時堆棧變化即可用圖1近似表示:
#include< stdio.h >int func(int arg1, int arg2, int arg3){ int x = 1; int y = 2; return (arg1 + arg2 + arg3);}int main(){ func(5,6,7); return 0;}
func函數(shù)有兩個局部int型局部變量(每個變量4字節(jié))。在這個簡化的場景中,main調(diào)用func,而程序的控制仍在func中。此處,main是調(diào)用函數(shù)(caller),func是被調(diào)用函數(shù)(callee)。
esp被func函數(shù)使用來表示棧頂。ebp相當(dāng)于一個“基準(zhǔn)指針”。從main傳遞到func的參數(shù)以及func函數(shù)本身的局部變量都可以以這個基準(zhǔn)指針為參考,加上偏移量找到。
由于被調(diào)用函數(shù)也允許使用EAX,ECX和EDX寄存器,所以如果調(diào)用函數(shù)希望保存這些寄存器的值,就必須在調(diào)用被調(diào)用函數(shù)之前顯式地將這些寄存器的值保存在棧中。另外,除了上面提到的幾個寄存器,被調(diào)用函數(shù)還想使用其他別的寄存器,比如EBX,ESI和EDI,那么被調(diào)用函數(shù)就必須在棧中保存這些被使用的額外的寄存器,并且需要在調(diào)用返回前恢復(fù)他它們。換一句話說,即如果被調(diào)用函數(shù)只使用約定的EAX,ECX和EDX寄存器,它們則由調(diào)用函數(shù)負(fù)責(zé)保存并恢復(fù);如果被調(diào)用函數(shù)還額外使用了別的寄存器,則必須由被調(diào)用函數(shù)自己保存并恢復(fù)這些寄存器的值。
傳遞給func的參數(shù)被壓到棧中的順序為最后一個參數(shù)先進(jìn)棧,第二個參數(shù)其次進(jìn)棧,第一個參數(shù)最后進(jìn)棧。因此圖1中,arg3比arg1先入棧。func函數(shù)中聲明的局部變量以及函數(shù)執(zhí)行過程中需要用到的一些臨時變量也都在保存在棧中。
注意:在被調(diào)用函數(shù)返回時, 小于以及等于4個字節(jié)的返回值會被保存在EAX中,如果返回值大于4字節(jié),小于8字節(jié),那么返回值則會被保存在EDX中。但是如果返回值占用的空間大于8個字節(jié),則調(diào)用函數(shù)會向被調(diào)用函數(shù)傳遞一個額外的參數(shù),這個額外的參數(shù)指向?qū)⒁4娣祷刂档目臻g的地址。用C語言的話來說,就是函數(shù)調(diào)用:
x = func(i,j,k);
被轉(zhuǎn)化為
func(&x,i,j,k);
上述情況僅僅在返回值占用空間超過8個字節(jié)時才會發(fā)生。有的編譯器不用EDX保存返回值,所以當(dāng)返回值大于4個字節(jié)時,就會用這種轉(zhuǎn)換。
當(dāng)然,不是所有的函數(shù)調(diào)用都是將返回值直接賦值給一個變量,還有可能是直接參與到某個表達(dá)式的計算中,如:
n = func(i,j,k) + func(x,y,z);
又或者是作為另外的函數(shù)的參數(shù),如:
func(func(i,j,k),4);
這種情況下,func的返回值會被保存在一個臨時的變量中參加后續(xù)的運算,所以func(i,j,k)還是可以被轉(zhuǎn)化成func(&tmp,i,j,k)。
接下來,讓我們一起看下在c函數(shù)的調(diào)用中,一個棧幀的建立以及消除過程。
函數(shù)調(diào)用前調(diào)用函數(shù)的動作
我們?nèi)砸陨厦胬訛槔{(diào)用函數(shù)是main,它準(zhǔn)備調(diào)用被調(diào)函數(shù)func。在函數(shù)調(diào)用前,main函數(shù)正在用esp和ebp寄存器指示它自己的棧幀。
首先,main函數(shù)把傳遞給func的參數(shù)壓入棧中。不過,該步驟是可選的,只在這三個寄存器內(nèi)容需要保留的時候執(zhí)行此步驟。
緊接著,main函數(shù)會把傳遞給func的參數(shù)一一壓入棧中,最后的參數(shù)最先進(jìn)棧,第一個參數(shù)最后進(jìn)棧。假如,我們的函數(shù)調(diào)用是:
x = func(5,6,7);
則對應(yīng)的匯編語言指令如下:
push 0x7push 0x6push 0x5
最后,main函數(shù)用call指令調(diào)用被調(diào)函數(shù)
call func
如前面所說,call指令含有兩個操作,首先是先將eip指令寄存器中的返回地址(即被調(diào)函數(shù)在被調(diào)用處的下一條指令的地址)壓入棧中;其次是棧頂指針esp的值減4,即esp=esp-4。此時返回地址就在棧頂了。在call指令執(zhí)行完畢以后,下一個執(zhí)行周期將從名為func的標(biāo)記處開始。
圖2展示了call指令執(zhí)行完以后棧的內(nèi)容。圖2以及后續(xù)圖中的綠色粗虛線表示了被調(diào)用函數(shù)在被調(diào)用之前棧頂?shù)奈恢谩.?dāng)整個func函數(shù)調(diào)用過程結(jié)束以后,棧頂將又會回到該位置。
圖2
函數(shù)調(diào)用發(fā)生后被調(diào)用函數(shù)的動作
當(dāng)函數(shù)func,即被調(diào)用函數(shù)取得程序的控制權(quán),它必須做三件事:
建立它自己的棧幀為局部變量分配空間如果有必要,保存寄存器EBX,ESI和EDI的值首先,func函數(shù)必須建立它自己的棧幀。ebp寄存器現(xiàn)在正在指向main函數(shù)的棧幀中的某個位置,這個值必須被保留,因此,ebp保存的值需要進(jìn)棧,即push ebp。之后,就可以隨意操作ebp寄存器了(因為ebp內(nèi)保存的main的基址已入棧),此時,將esp的內(nèi)容賦值給ebp,即mov ebp, esp;由圖2我們可知,在調(diào)用func函數(shù)的過程中,原本指向main函數(shù)的棧頂指針,會隨著EAX、ECX和EDX寄存器以及實際參數(shù)的入棧而不斷發(fā)生變化,在call指令執(zhí)行之后,此時esp棧頂指針已指向返回地址(被調(diào)用函數(shù)在被調(diào)用處的下一條指令的地址)位置,此時,func的ebp即和esp棧頂指針一樣指向返回地址處。
當(dāng)func函數(shù)建立它自己棧幀時,保留ebp寄存器內(nèi)容(即main函數(shù)的ebp)的位置所對應(yīng)的地址,即為func函數(shù)的基址,也就是func函數(shù)的ebp指向的地址。換一句話說,就是func函數(shù)的ebp指向的位置保存了main函數(shù)的ebp。
func的ebp寄存器在被esp賦值后,func函數(shù)的參數(shù)就可以通過對ebp附加一個偏移量得到,而棧頂寄存器就可以空出來做其它事情。如此一來,幾乎所有的c函數(shù)都由如下兩個指令開始:
push ebpmov ebp,esp
此時,堆棧分布如圖3所示。在該場景中,第一個參數(shù)的地址是ebp+8,因為main的ebp和返回地址各在棧中占了4個字節(jié)。
圖3
接下來,func必須為它的局部變量分配??臻g,與此同時,也必須為它可能會用到的一些臨時變量分配??臻g。比如func中可能包括一些復(fù)雜的表達(dá)式,其子表達(dá)式的中間值就必須得有地方存放。這些存放中間值的地方統(tǒng)稱為臨時的,因為它們可以被下一個復(fù)雜的表達(dá)式所使用。為方便說明,我們假設(shè)func中有兩個int類型(每個4字節(jié))的局部變量,同時,需要額外的2字節(jié)的臨時存儲空間,則可以簡單地把棧頂指針減去10便為這10個字節(jié)分配了??臻g,匯編指令如下:
sub esp,10
此時,局部變量以及臨時變量都可以通過基址指針ebp加上偏移量來找到了。
最后,如果func函數(shù)用到EBX,ESI和EDI寄存器,則它必須在自己的棧幀里保存它們。如下圖4所示:
圖4
func函數(shù)的函數(shù)體現(xiàn)在可以執(zhí)行了。這其中可能包含進(jìn)棧、出棧的動作,棧指針esp會上下移動,但ebp則是保持不變的。這也就表示我們可以一直使用[esp+8]找到第一個參數(shù),而不需要管函數(shù)中有多少進(jìn)出棧的動作。
函數(shù)func執(zhí)行過程中也許還會調(diào)用其它函數(shù),甚至遞歸地調(diào)用func自身。然而只要ebp寄存器在這些子調(diào)用返回時被恢復(fù),就可以繼續(xù)用ebp加上偏移量的方式訪問實際參數(shù)、局部變量以及臨時變量。
被調(diào)用函數(shù)返回前的動作
func函數(shù)把程序控制權(quán)返回給調(diào)用函數(shù)之前,被調(diào)用函數(shù)func必須先把返回值保存在EAX寄存器中。正如前面所討論過的,當(dāng)返回值占用多于4個或8個字節(jié)時,接收返回值的變量地址會作為一個額外的指針參數(shù)被傳到函數(shù)func中,而函數(shù)func本身就不需要返回值了。這種情況下,被調(diào)用函數(shù)直接通過內(nèi)存拷貝把返回值直接拷貝到接收地址,從而省去了一次通過棧的中轉(zhuǎn)拷貝。
其次,func必須恢復(fù)EBX,ESI和EDI寄存器的值。如果這些寄存器被修改,正如前面所說,我們會在func執(zhí)行開始時把它們的原始值壓入棧中。如果esp寄存器指向如圖4所示的正確位置,寄存器的原始值即可出棧并恢復(fù)。由此可見,func函數(shù)執(zhí)行過程中正確地跟蹤esp是多么重要,也即進(jìn)棧和出棧的次數(shù)必須保持平衡。
上面兩步之后,我們便不再需要func函數(shù)的局部變量和臨時變量了,我們可以通過下面的指令消除棧幀:
mov esp,ebppop ebp
上面執(zhí)行后的結(jié)果就是棧里面的內(nèi)容跟圖2中所示的棧完全一樣。
現(xiàn)在可以執(zhí)行返回指令了。從棧里彈出返回地址,賦值給eip寄存器。棧如圖5所示:
圖5
i386指令集有一條"leave"指令,它與上面提到的mov和pop指令所做的動作完全相同。所以c函數(shù)通常以這樣的指令結(jié)束:
leaveret
這樣被調(diào)用函數(shù)執(zhí)行完畢,下一步將繼續(xù)執(zhí)行被調(diào)用函數(shù)調(diào)用處的下一條的指令。但是此時, esp 指向原先的 arg1,并沒有指向原先主函數(shù)的棧頂。如果原先棧中還有其他數(shù)據(jù),esp 沒有歸位會導(dǎo)致調(diào)用函數(shù)引用棧中數(shù)據(jù)出錯。
堆棧平衡
在這種背景下,出現(xiàn)了堆棧平衡的概念。即,還需對esp進(jìn)行單獨操作,才能將esp指向調(diào)用函數(shù)的棧頂。以常見的c語言,函數(shù)有好幾種調(diào)用規(guī)則。比如 cdecl 方式和 stdcall 方式。
cdecl 方式中,由調(diào)用函數(shù)執(zhí)行 add esp, n 指令調(diào)整 esp,達(dá)到堆棧平衡。在 stdcall 方式中,由被調(diào)用函數(shù)在返回時,執(zhí)行 ret n 平衡堆棧。n 其實就是函數(shù)的參數(shù)所占的空間大小。
在程序控制權(quán)又返回到調(diào)用函數(shù)(即我們例子中的main函數(shù))后,棧如圖5所示。這時傳遞給func的參數(shù)通常已經(jīng)不需要了。我們可以把3個參數(shù)一起彈出棧,這可以通過把棧頂指針加0xc(即3個4字節(jié))實現(xiàn):
add esp,0xc
如果在函數(shù)調(diào)用前,EAX,ECX和EDX寄存器的值被保存在棧中,調(diào)用函數(shù)main現(xiàn)在則可以把它們彈出棧。在這個動作以后,棧頂就回到了我們開始整個函數(shù)調(diào)用前的位置,也就是圖5中綠色粗線的位置。
實例演示
C函數(shù)源碼和前面一樣,如下:
#include< stdio.h >int func(int arg1, int arg2, int arg3){ int x = 1; int y = 2; return (arg1 + arg2 + arg3);}int main(){ func(5,6,7); return 0;}
執(zhí)行編譯指令如下:
#gcc test.c -m32 -o test In file included from /usr/include/features.h:462, from /usr/include/bits/libc-header-start.h:33, from /usr/include/stdio.h:27, from test.c:2:/usr/include/gnu/stubs.h:7:11: fatal error: gnu/stubs-32.h: No such file or directory # include < gnu/stubs-32.h > ^~~~~~~~~~~~~~~~compilation terminated.
我們會看到編譯報錯,讓我們先解析一下gcc編譯參數(shù), -m32表示生成32位的代碼,如果沒有-m32,則會生成跟操作系統(tǒng)位數(shù)一致的代碼。
經(jīng)過檢索得知,64位機(jī)器由于缺少32位兼容包,所以在編譯32代碼時,會報錯,通過如下指令安裝開發(fā)包即可解決:
#sudo yum -y install glibc-devel.i686
注意小編是用的CentOS機(jī)器,如果是Ubuntu機(jī)器,則可通過如下命令安裝:
#sudo apt-get install libc6-dev-i386
安裝完開發(fā)包以后,則編譯不會再報錯,編譯成功后,使用 objdump 或者 ida 查看匯編代碼,可以看出,默認(rèn)使用 cdecl
方式平衡堆棧。經(jīng)過反匯編得到部分截圖如下:
部分匯編源碼如下:
080484ad < func >: 80484ad: 55 push ebp 80484ae: 89 e5 mov ebp,esp 80484b0: 83 ec 10 sub esp,0x10 80484b3: c7 45 fc 01 00 00 00 mov DWORD PTR [ebp-0x4],0x1 80484ba: c7 45 f8 02 00 00 00 mov DWORD PTR [ebp-0x8],0x2 80484c1: 8b 55 08 mov edx,DWORD PTR [ebp+0x8] 80484c4: 8b 45 0c mov eax,DWORD PTR [ebp+0xc] 80484c7: 01 c2 add edx,eax 80484c9: 8b 45 10 mov eax,DWORD PTR [ebp+0x10] 80484cc: 01 d0 add eax,edx 80484ce: c9 leave 80484cf: c3 ret080484d0 < main >: 80484d0: 8d 4c 24 04 lea ecx,[esp+0x4] 80484d4: 83 e4 f0 and esp,0xfffffff0 80484d7: ff 71 fc push DWORD PTR [ecx-0x4] 80484da: 55 push ebp 80484db: 89 e5 mov ebp,esp 80484dd: 51 push ecx 80484de: 83 ec 04 sub esp,0x4 80484e1: 6a 07 push 0x7 80484e3: 6a 06 push 0x6 80484e5: 6a 05 push 0x5 80484e7: e8 c1 ff ff ff call 80484ad < func > 80484ec: 83 c4 0c add esp,0xc 80484ef: 83 ec 08 sub esp,0x8 80484f2: 50 push eax 80484f3: 68 9c 85 04 08 push 0x804859c 80484f8: e8 53 fe ff ff call 8048350 < printf@plt > 80484fd: 83 c4 10 add esp,0x10 8048500: b8 00 00 00 00 mov eax,0x0 8048505: 8b 4d fc mov ecx,DWORD PTR [ebp-0x4] 8048508: c9 leave 8048509: 8d 61 fc lea esp,[ecx-0x4] 804850c: c3 ret 804850d: 66 90 xchg ax,ax 804850f: 90 nop xchg ax,ax
注1:
lea,官方解釋Load Effective Address,即裝入有效地址的意思,它的操作數(shù)就是地址;常見幾種用法:
1)、lea eax,[addr]
就是將表達(dá)式addr的值放入eax寄存器,如:lea eax,[401000h]; 將值401000h寫入eax寄存器中;lea指令右邊的操作數(shù)表示一個精指針,上述指令和mov eax,401000h是等價的。
2)lea eax,dword ptr [ebx];將ebx的值賦值給eax。
3)lea eax,c;其中c為一個int型的變量,該條語句的意思是把c的地址賦值給eax。
注2:
dword:雙字,就是四個字節(jié)
ptr:pointer縮寫 即指針
[]里的數(shù)據(jù)是一個地址值,這個地址指向一個雙字型數(shù)據(jù) 比如mov eax, dword ptr [12345678] 把內(nèi)存地址12345678中的雙字型(32位)數(shù)據(jù)賦給eax。
到此,相信小伙伴們就可以按照前文的講解,看懂最后的函數(shù)匯編源碼了,為檢測學(xué)習(xí)成果,請對照前文所講,自行翻譯最后的匯編源碼。
最后,借用一句話送給大家:"紙上得來終覺淺,絕知此事要躬行"。
標(biāo)簽:
精彩推送
天天微速訊:C函數(shù)調(diào)用機(jī)制與棧幀原理詳解
C函數(shù)調(diào)用機(jī)制與棧幀原理詳解-當(dāng)一個C函數(shù)被調(diào)用時,函數(shù)的參數(shù)如何傳
齊魯銀行濟(jì)南科創(chuàng)金融中心支行揭牌暨齊魯銀行與濟(jì)南高新區(qū)戰(zhàn)略合作簽約活動舉行
6月6日,齊魯銀行濟(jì)南科創(chuàng)金融中心支行揭牌暨齊魯銀行與濟(jì)南高新區(qū)戰(zhàn)略
全球速訊:陜西煤業(yè):6月7日融券賣出33.22萬股,融資融券余額28.54億元
6月7日,陜西煤業(yè)(601225)融資買入4302 18萬元,融資償還6078 24萬元
【時快訊】數(shù)列收斂的幾何意義是什么_數(shù)列收斂的定義
想必現(xiàn)在有很多小伙伴對于數(shù)列收斂的定義方面的知識都比較想要了解,那
潤達(dá)醫(yī)療與華為云簽署戰(zhàn)略合作協(xié)議
6月6日,潤達(dá)醫(yī)療與華為云計算技術(shù)有限公司正式簽署全面戰(zhàn)略合作協(xié)議,
新聞快訊
-
福特汽車公司即將達(dá)成一項出售德國工廠的協(xié)議,據(jù)悉兩個來自中國的汽車制造商團(tuán)體在三家主要競標(biāo)者中|環(huán)球簡訊
-
綿陽師范學(xué)院教務(wù)系統(tǒng)(綿陽師范學(xué)院教務(wù)網(wǎng)登錄入口)-世界今日訊
-
今日快看!漢源縣花椒現(xiàn)代農(nóng)業(yè)園區(qū)_關(guān)于漢源縣花椒現(xiàn)代農(nóng)業(yè)園區(qū)簡述
-
全球宏觀態(tài)勢每周觀察(2023年第21期):美國兩黨達(dá)成初步協(xié)議,德國經(jīng)濟(jì)陷入技術(shù)性衰退
-
電商發(fā)布618戰(zhàn)報 這一產(chǎn)品增近5倍 行業(yè)拐點愈發(fā)明朗 環(huán)球熱點評
-
蘋果將加速AR/VR向生產(chǎn)力工具演進(jìn) 蘋果2023年WWDC開幕式要點匯總 環(huán)球今日報
新聞快訊
- 天天微速訊:C函數(shù)調(diào)用機(jī)制與棧幀原理詳解
- 齊魯銀行濟(jì)南科創(chuàng)金融中心支行揭牌暨齊魯銀行與濟(jì)南高新區(qū)戰(zhàn)略合作簽約活動舉行
- 全球速訊:陜西煤業(yè):6月7日融券賣出33.22萬股,融資融券余額28.54億元
- 【時快訊】數(shù)列收斂的幾何意義是什么_數(shù)列收斂的定義
- 23安徽債58今日發(fā)布發(fā)行公告-前沿資訊
- 金巧福鉑金多少錢一克(2023年06月07日)參考價格
- 您敢選電車嗎?撞收費站起火的案例帶給我們哪些警示 每日熱聞
- 潤達(dá)醫(yī)療與華為云簽署戰(zhàn)略合作協(xié)議
- biubiu加速器如何切換線路 biubiu加速器切換智能模式教程-天天播報
- 天天微動態(tài)丨剛剛,華為重大發(fā)布!孟晚舟發(fā)聲
- 股票板塊怎么分類-焦點觀察
- 中集來福士獲世界級“滾裝船技術(shù)與環(huán)境獎”_熱聞
- 【世界快播報】【學(xué)習(xí)】又到高考時 一起重溫總書記對莘莘學(xué)子的青春寄語
- 把專業(yè)設(shè)備搬進(jìn)音樂街區(qū),這場活動暖心又惠民-觀速訊
- 電腦鍵盤怎樣轉(zhuǎn)換中文|天天觀點
- 商務(wù)預(yù)報:5月29日-6月4日36個大中城市食用農(nóng)產(chǎn)品零售價格情況
- 環(huán)球視點!左窄右寬的字有哪些_左窄右寬的字有
- 拍拍嚴(yán)選如何發(fā)布商品 具體操作方法介紹-天天日報
- 全球觀焦點:塞爾達(dá)傳說王國之淚塔吉卡茲怎么打[多圖]
- “浦貝杯”2023中國廣場舞大賽20支隊伍集結(jié)完畢
- 世界新動態(tài):民生銀行重要公告:打擊信用卡他人代還
- 機(jī)構(gòu):科網(wǎng)股二季度業(yè)績在低基數(shù)下增速或更高 依次推薦五只龍頭股_焦點速看
- 甘肅榆中鄉(xiāng)村農(nóng)耕博物館:館里留“鄉(xiāng)愁” 全球觀天下
- 6月7日基金凈值:景順長城競爭優(yōu)勢混合最新凈值0.8129,跌0.54%_天天看點
- 世界今頭條?。ǜ郯呐_)多項數(shù)據(jù)顯示臺灣經(jīng)濟(jì)成長壓力仍在
- 熱文:再接再厲意思是什么意思是什么_再接再厲的意思解釋
- 全球快資訊:禁止直播!杭州開了個極壞的頭……
- 天天熱門:芯塔電子獲近億元Pre-A輪融資,吳興產(chǎn)投領(lǐng)投
- 江蘇銀行北京廣渠門支行:關(guān)愛農(nóng)民工,守住“錢袋子”
- 關(guān)注:又有三家私募股權(quán)基金管理人因未履行謹(jǐn)慎勤勉義務(wù)、從事非私募業(yè)務(wù)等被監(jiān)管責(zé)令改正
- 全國碳市場今日收盤價與前一日持平 報58.50元/噸|每日熱文
- 雋諾實現(xiàn)日產(chǎn)200噸RDF替代燃料,高效助力綠色循環(huán)碳減排!
- 澳優(yōu)出售新西蘭奶粉工廠 買方為伊利全資子公司
- 當(dāng)前關(guān)注:上海物貿(mào)3漲停
- 前臉更加凌厲,雷克薩斯TX最新預(yù)告圖公布_天天日報
- 和平精英信號槍怎么用(信號槍怎么用)
- EDUCAUSE 2023年度十大IT議題⑤丨改進(jìn)高校IT服務(wù) 支持混合工作模式
- 星環(huán)科技:擬定增募資不超過15.2億元,
- 國企領(lǐng)導(dǎo)出差帶“小三”逛街?中央巡視組回應(yīng):將督促查實
- 進(jìn)銷存軟件免費版
- 全球微資訊!隨君直到夜郎西出自哪首詩_隨君直到夜郎西
- 每日時訊!影視資訊,星輝總代艾米麗在巴黎第四季推遲拍攝 受編劇罷工影響
- 天士力:獲得《藥物臨床試驗批準(zhǔn)通知書》
- 看來國家是動真格了?第二批央企總部離京“正式啟動” 世界視點
- 山西省長治市平順縣廣泛開展“五大”活動,推進(jìn)高質(zhì)量發(fā)展 環(huán)球熱文
- 奉天省造老銀元價格(2023年06月07日)
- 道孚縣氣象臺發(fā)布雷電黃色預(yù)警信號【III級/較重】【2023-06-07】
- 環(huán)球今日報丨今年端午假期出行免車輛押金 一嗨租車受熱捧
- 2023父親節(jié)暖心短句有哪些 世界快播
- 記敘文寫作方法與技巧_記敘文 世界最資訊