Ⅰ 匯編:獲取段地址的問題
data 是有一個地址的 在你定義這個data的時候 就產生地址 就像C中 定義一個 int a ; 那麼&a是有物理地址的。一般在mov ax,data 這一句的後面應該會接著 一句是 mov ds,ax 就是把該地址放到數據段寄存器中 供後面代碼定址用, 只是這個data的值和他真正的地址查一個*10的關系 如果data的值是1000h 那麼他的真正的地址就是1000h*10
Ⅱ 堆棧的定義
一個程序一般分為3段:text段,data段,bss段
text段:就是放程序代碼的,編譯時確定,只讀,
data段:存放在編譯階段(而非運行時)就能確定的數據,可讀可寫
就是通常所說的靜態存儲區,賦了初值的全局變數和靜態變數存放在這個區域,常量也存放在這個區域
bss段:定義而沒有賦初值的全局變數和靜態變數,放在這個區域
這個夠不夠清楚呢?
堆棧就是棧的簡稱。
堆和棧的區別
一、預備知識—程序的內存分配
一個由c/C++編譯的程序佔用的內存分為以下幾個部分
1、棧區(stack)— 由編譯器自動分配釋放 ,存放函數的參數值,局部變數的值等。其操作方式類似於數據結構中的棧。
2、堆區(heap) — 一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收 。注意它與數據結構中的堆是兩回事,分配方式倒是類似於鏈表,呵呵。
3、全局區(靜態區)(static)—,全局變數和靜態變數的存儲是放在一塊的,初始化的全局變數和靜態變數在一塊區域, 未初始化的全局變數和未初始化的靜態變數在相鄰的另一塊區域。 - 程序結束後有系統釋放
4、文字常量區—常量字元串就是放在這里的。 程序結束後由系統釋放
5、程序代碼區—存放函數體的二進制代碼。
二、例子程序
這是一個前輩寫的,非常詳細
//main.cpp
int a = 0; 全局初始化區
char *p1; 全局未初始化區
main()
{
int b; 棧
char s[] = "abc"; 棧
char *p2; 棧
char *p3 = "123456"; 123456\0在常量區,p3在棧上。
static int c =0; 全局(靜態)初始化區
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
分配得來得10和20位元組的區域就在堆區。
strcpy(p1, "123456"); 123456\0放在常量區,編譯器可能會將它與p3所指向的"123456"優化成一個地方。
}
二、堆和棧的理論知識
2.1申請方式
stack:
由系統自動分配。 例如,聲明在函數中一個局部變數 int b; 系統自動在棧中為b開辟空間
heap:
需要程序員自己申請,並指明大小,在c中malloc函數
如p1 = (char *)malloc(10);
在C++中用new運算符
如p2 = (char *)malloc(10);
但是注意p1、p2本身是在棧中的。
2.2
申請後系統的響應
棧:只要棧的剩餘空間大於所申請空間,系統將為程序提供內存,否則將報異常提示棧溢出。
堆:首先應該知道操作系統有一個記錄空閑內存地址的鏈表,當系統收到程序的申請時,
會遍歷該鏈表,尋找第一個空間大於所申請空間的堆結點,然後將該結點從空閑結點鏈表中刪除,並將該結點的空間分配給程序,另外,對於大多數系統,會在這塊內存空間中的首地址處記錄本次分配的大小,這樣,代碼中的delete語句才能正確的釋放本內存空間。另外,由於找到的堆結點的大小不一定正好等於申請的大小,系統會自動的將多餘的那部分重新放入空閑鏈表中。
2.3申請大小的限制
棧:在Windows下,棧是向低地址擴展的數據結構,是一塊連續的內存的區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個編譯時就確定的常數),如果申請的空間超過棧的剩餘空間時,將提示overflow。因此,能從棧獲得的空間較小。
堆:堆是向高地址擴展的數據結構,是不連續的內存區域。這是由於系統是用鏈表來存儲的空閑內存地址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限於計算機系統中有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大。
2.4申請效率的比較:
棧由系統自動分配,速度較快。但程序員是無法控制的。
堆是由new分配的內存,一般速度比較慢,而且容易產生內存碎片,不過用起來最方便.
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配內存,他不是在堆,也不是在棧是直接在進程的地址空間中保留一快內存,雖然用起來最不方便。但是速度快,也最靈活。
2.5堆和棧中的存儲內容
棧: 在函數調用時,第一個進棧的是主函數中後的下一條指令(函數調用語句的下一條可執行語句)的地址,然後是函數的各個參數,在大多數的C編譯器中,參數是由右往左入棧的,然後是函數中的局部變數。注意靜態變數是不入棧的。
當本次函數調用結束後,局部變數先出棧,然後是參數,最後棧頂指針指向最開始存的地址,也就是主函數中的下一條指令,程序由該點繼續運行。
堆:一般是在堆的頭部用一個位元組存放堆的大小。堆中的具體內容有程序員安排。
2.6存取效率的比較
char s1[] = "aaaaaaaaaaaaaaa";
char *s2 = "bbbbbbbbbbbbbbbbb";
aaaaaaaaaaa是在運行時刻賦值的;
而bbbbbbbbbbb是在編譯時就確定的;
但是,在以後的存取中,在棧上的數組比指針所指向的字元串(例如堆)快。
比如:
#include
void main()
{
char a = 1;
char c[] = "1234567890";
char *p ="1234567890";
a = c[1];
a = p[1];
return;
}
對應的匯編代碼
10: a = c[1];
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
0040106A 88 4D FC mov byte ptr [ebp-4],cl
11: a = p[1];
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]
00401070 8A 42 01 mov al,byte ptr [edx+1]
00401073 88 45 FC mov byte ptr [ebp-4],al
第一種在讀取時直接就把字元串中的元素讀到寄存器cl中,而第二種則要先把指針值讀到edx中,在根據edx讀取字元,顯然慢了。
2.7小結:
堆和棧的區別可以用如下的比喻來看出:
使用棧就象我們去飯館里吃飯,只管點菜(發出申請)、付錢、和吃(使用),吃飽了就走,不必理會切菜、洗菜等准備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。
使用堆就象是自己動手做喜歡吃的菜餚,比較麻煩,但是比較符合自己的口味,而且自由度大。
Ⅲ 請高手解釋下匯編語言的代碼段和程序段的具體定義和變數的定義方法,有離子的最好.
代碼段和程序段定義的名字可以任意,只是代碼段會被執行,由最後的end +標號指出起始標號 以湯叔的程序為例
SixteenBits DW 12345
這句話定義了一個16位的變數 sixteenbits
定義格式 變數名+長度+初值
段定義方式
不加描述符的最簡單的段定義
段名+segment 後以段明+ends表明段結束
在下面這個程序里 data段是數據段 code段是代碼段
Assume CS:Code,DS:Data 這條偽指令指定段的關聯寄存器
; 本程序通過編譯,運行正確。
Data Segment
SixteenBits DW 12345
Data Ends
Code Segment
Assume CS:Code,DS:Data
Start: mov ax,Data ;取數據段地址
mov ds,ax ;賦給數據段寄存器ds
mov ax,SixteenBits
push cs
pop ds
lea di,binary ;二進制字元串首地址
mov cx,16 ;移位次數
cld
push di
; 轉換成二進制字元串
Shift_Left: sal ax,1 ;算術左移
jc Carry_Yes ;有進位,跳轉
mov byte ptr [di],'0' ;無進位
jmp Next_Bit
Carry_Yes: mov byte ptr [di],'1' ;有進位
Next_Bit: inc di
loop Shift_Left
mov byte ptr [di],'$' ;字元串結束符
pop dx
; 顯示二進制字元串
mov ah,9
int 21h
Exit_Proc: MOV AX,4C00H ;結束程序
INT 21H
binary:
Code ENDS
END Start ;編譯到此結束
Ⅳ 求問初始化的全局變數一定放在.data段中嗎
學過C語言的都知道,已經初始化的全局變數是放在.data段中的,沒有初始化的全局變數是放在.bss段中的。一直以來我也是這么認為的,但在開發MyOS的過程中,一些明明已經初始化的數據在執行時得到的卻是隨機值,使我對這個說法產生了懷疑。例如,在MyOS的VBE驅動中,背景色明明設成了黑色,可系統啟動後屏幕卻是紅色的。昨天,在真機上調試最新的MyOS代碼時,任務調度老是調度不到別的線程去,只有一個Idle線程在跑。輸出了很多的調試信息,可就是找不到哪錯了。最後發現,系統的確是進行調度了,可被選中的新的線程還是Idle線程。而進程和線程管理的代碼經過檢查是沒錯的。而且還有一個奇怪的現象就是在調試信息中為了看到系統的確的不斷輸出,我在每個輸出語句後加了一個從0開始不斷增加的數,每次輸出就遞增,這樣就能看到變化了。在VMWare上,工作的很好,是從0開始遞增的,可在真機上是從一個很大的數開始遞增的。就在我即將崩潰之際,突然想到是不是flag這個控制線程創建的全局變數也沒有按照我想的那樣初始為0,導致根本沒有別的線程被創建,所以就只有Idle一個線程在運行。當我把flag設成局部變數後,系統就正常了。
我猜想是不是編譯器發現變數是被初始化成了0,所以就把變數放到.bss段中了。因為.bss段一般情況下會清0的。所以,編譯器認為沒有必要把這個變數放在.data段中。而MyOS在啟動的時候,並沒有把.bss段佔用的內存清0。因為我一直認為.bss段中放的是未初始化的數據,清不清0關系不大,只要我保證在使用時初始化就是了。沒想到編譯器把已經初始化為0的變數也放到了.bss段中了。
當然,這樣可以減少可執行文件的體積。而且正如上面所說,一般的系統都會把.bss段清0,所以不會有問題。而MyOS是個操作系統,沒有人幫我們把它清0,才出現了上面說到的很多問題。
接下來的幾天,應該修復MyOS的這個Bug,添加把.bss段清0的代碼。當然,只需4行C代碼即可,即時是匯編也不會超過10行。
大家可以看出來,這個說法其實是我的猜測,所以還是需要實際驗證。但我想應該是這樣的。
Ⅳ 編譯器編譯高級語言為低級語言的時候,給全局變數或靜態變數是如何分配內存的
對於C和C++的編譯器,全局變數和靜態變數都是在專門的數據區保存的,更具體一點,一般是在.data和.bss段保存的,具體在哪個段,編譯器會根據代碼中是否對這些變數進行了初始化來決定,如果初始化過,並且初始化的值不為0,那麼這個這個變數一般就會被放在編譯結果的.data段中,否則就是放在.bss段中。
.data段中就保存變數的符號,還保存變數的初始化值,而在.bss段中,只保存變數的符號,而不保存值,這是因為這部分的變數都將被初始化為0,這也是為什麼static聲明的變數即使沒有初始化也會是0的原因。
這些段都會在程序被執行的時候由操作系統(或鏈接器)載入到指定的內存中,便完成相應的初始化。
Ⅵ 以下聲明變數的方式,哪些在編譯時確定變數的內存地址
首先說一下所謂的編譯時變數確定內存地址,之所以稱之為編譯時確定內存地址是因為編譯後(其實應是鏈接後),有一部分數據放入程序的數據段即data段和bss段。data段主要包含,初始化了的全局變數和初始化了的靜態變數。除了數據段,能存放變數的還有bss段和棧。bss段一般放的是未初始化的全局變數和未初始化的靜態變數。除了放入bss段和數據段的數據外,其他數據都放在棧中(主要是局部變數)。bss段的數據和data段的數據地址是確定的。根據上面說的:
A放入數據段data段,地址確定
B放入bss段,地址也是確定的,
C中分auto局部 變數和static靜態局部變數。auto就是普通的變數,變數放在棧中,地址不能確定。static變數放在bss段中,地址確定。
D宏內的變數聲明並不能分為一類,他是上面三種的已匯總
E 同C
Ⅶ 關於編程的問題 高分
是這樣的,一個編譯好的可用的可執行程序,是分段的(.data段 .bss 段 .text段詳細點就網路下),而這個可執行程序在被生成之前需要先將源文件(.c 文件)編譯成 .o(二進制文件),而這個時候就開始了分段(上面所說的 .data段 .bss 段,.test 段),而你這種情況的出現可能是編譯時的編譯選項(例如著名的編譯工具 gcc, gcc -c 1.c -o 1.o ,這里的 -c 就是編譯選項)配置成了 -ffunction-sections -fdata-sections 造成的,這個會把每個文件的函數部分和非局部變數單獨放到一個段里,經過鏈接(這個過程可暫不用了解)最後相同的段組合起來就成了最後的可執行文件。你現在這種情況應該是這種情況造成的。這個可以通過修改編譯選項或鏈接腳本解決。
以上你的代碼並沒有明顯的問題,只是頭文件包含的這個文件我不太懂,可能是跑在特定平台上的,這不是重點,如果頭文件不正確會報出函數未聲明的警告,這不是導致這個情況發生的原因
解決辦法:
1、最粗暴方法:換個編譯器,windows 常用的編譯器有 cl(也就是vc 6.0 什麼的,大學時用的),Linux 操作系統(Ubuntu,Red hat,Centos ...)常用的 c 編譯器就是 gcc c++ 編譯器是 g++
2、修改鏈接腳本,因為看你的代碼應該是跑在某個平台上的,而一般這種情況下會有自己的鏈接腳本,你可以在 .data 段中添加這樣一句:*(.data*) 解決當前的 .data.align4 段無處可放的問題
再有問題再回復
Ⅷ 匯編,定義data段,不懂
$是在用到09號DOS中斷顯示的時候才用的,它是標記顯示的字元在次的前一個結束。不用09號顯示的話,就可以不要「,0DH,0AH,ˊ$ˊ」這些了。還有就是指定當前指令所在的地址也是用$指定的
如: jmp $
就是指一直在執行jmp指令,在此死循環了。沒有什麼意意義的。
Ⅸ C或C++程序編譯時內存分為幾個存儲區
1、從操作系統原理的角度來看,只有一個存儲區就是虛擬內存。
2、根據功能可以分為 ,棧區 、堆區、靜態區, 棧區一般指的一個函數局部變數,在編譯原理中這叫做一個棧幀。 堆區一般是為了用戶自由分配的,一般C語言中用MALLOC函數分配,C++中用NEW運算符來分配,它是有操作系統的堆管理器來管理的,拿windows來說,在一個程序運行後,一般至少有兩個默認的堆,一個是new堆,一個進程 自己的堆, 靜態區,這個一般是全局變數或者static變數使用的區域,這個區域,如果你對PE結構熟悉,就會明白這實際上是pe 區段中的.data區段,當程序運行後變成進程,這個區段是直接內存文件映射過去的。
Ⅹ 編譯時分配內存和運行時分配內存的理解,麻煩講解下
編譯時無所謂分配內存,程序在載入後才佔用內存,不知道你說的是不是說編譯生成的目標文件所佔的空間,目標文件里的各個段.bss .data .text等都要佔據一定空間,而運行時隨時都在分配內存,堆棧都是內存。
不管是局部的還是全局靜態變數,都會佔用目標文件的空間,但是也可以不初始化,不初始化的保存在.bss段,初始化的保存在.data段。