在我們學習當中,我們會思考很多問題,下面我們就提出幾個針對前面學習的東西的一些疑惑。
#include
intmain()
else
return0;
}
這段**的執行結果居然會是a凡是函式內部定義的都在stack區,都是全域性變數。凡是函式外部定義的變數都在data區,是全域性變數?
這段話是有問題的,因為當我們定義乙個stact的靜態區域性變數時候,這個時候就不是在stack區,而是data區。
資料區(靜態區)
全域性變數和靜態變數存放在此. 裡面細分有乙個常量區, 字串常量和其他常量也存放在此. 該區域是在程式結束後由作業系統釋放.
continue的作用是跳出本次的迴圈,並且要進行下一次的迴圈。他在不一樣的迴圈中,跳出的位置也不一樣,比如在for迴圈中,當我們執行了continue以後,那麼迴圈體後面的**將不再執行,而是跳轉到for(***1;***2;***3)中的***3語句開始下一次的迴圈。但是當在while語句或者do{}while(***1);當中,continue會跳轉到***1語句繼續判斷執行。continue一般只在迴圈結構**現,而且一般在乙個選擇結構中。
break語句不只是可以在迴圈中使用,並且可以在switch語句中使用。在迴圈中的break意味著結束整個迴圈週期,跳出離他最近的一層的迴圈,實際上break也可以用於多層迴圈的跳出,這樣一段**:
#include
intmain()
}}return0;
}
給外層迴圈起個名字就可以跳出了。
break在switch語句中的作用是:使得程式跳出switch,執行switch結構以後的**。
c語言的return也就意味著當前函式停止執行。
return 語句可以有多個,可以出現在函式體的任意位置,但是每次呼叫函式只能有乙個 return 語句被執行,所以只有乙個返回值。
函式一旦遇到 return 語句就立即返回,後面的所有語句都不會被執行到了。從這個角度看,return 語句還有強制結束函式執行的作用。
return 語句是提前結束函式的唯一辦法。return 後面可以跟乙份資料,表示將這份資料返回到函式外面;return 後面也可以不跟任何資料,表示什麼也不返回,僅僅用來結束函式。
goto 語句是一種無條件流程跳轉語句,通常 goto 語句與 if 語句結合使用,當滿足一定條件時,程式流程跳轉到指定標號處,接著往下執行。
其中,「語句標識」可以是任乙個合法的識別符號,如 pos_1、pos_2、label_1、label_2 等都是合法的語句標識。注意,語句標識後的冒號不能省略。
程式將從對應「語句標號」的**處開始往下執行。
e.g
#include
int main (
void
)printf
("成功輸入正整數:%d\n"
,n);
return0;
}
通過上述執行流程及執行結果的分析,可以發現該例中使用 goto 跳轉語句實現了迴圈的功能。故可以使用迴圈結構的**替換該 goto 結構的**。使用 goto 語句可能會造成程式層次不清晰,可讀性差,故在實際程式設計中,應盡量少使用或避免使用 goto 語句。
#undef 識別符號
用來將前面定義的巨集識別符號取消定義。
這樣一段例項**:
#include
#define int int *
intmain()
這樣就可以實現區域性替換了,後面如果想要使用可以繼續#define來定義。
jmp 無條件轉移指令
add 加法.
mov 傳送字或位元組.
push,pop 把運算元壓入或取出堆疊
sub 減法指令
eax 是"累加器"(accumulator), 它是很多加法乘法指令的預設暫存器。
ebx 是"基位址"(base)暫存器, 在記憶體定址時存放基位址。
ecx 是計數器(counter), 是重複(rep)字首指令和loop指令的內定計數器。
edx 則總是被用來放整數除法產生的餘數。
esi/edi 分別叫做"源/目標索引暫存器"(source/destination index),因為在很多字串操作指令中, ds:esi指向源串,而es:edi指向目標串.
ebp 是"基址指標"(base pointer), 它最經常被用作高階語言函式呼叫的"框架指標"(frame pointer). 在破解的時候,經常可以看見乙個標準的函式起始**:
push ebp ; 儲存當前ebp
mov ebp,esp ; ebp設為當前堆疊指標
sub esp, *** ; 預留***位元組給函式臨時變數.
…這樣一來,ebp 構成了該函式的乙個框架, 在ebp上方分別是原來的ebp, 返回位址和引數. ebp下方則是臨時變數. 函式返回時作 mov esp,ebp/pop ebp/ret 即可.
esp 專門用作堆疊指標,被形象地稱為棧頂指標,堆疊的頂部是位址小的區域,壓入堆疊的資料越多,esp也就越來越小。在32位平台上,esp每次減少4位元組。
eip 暫存器,用來儲存cpu要讀取指令的位址,cpu通過eip暫存器讀取即將要執行的指令。每次cpu執行完相應的彙編指令之後,eip暫存器的值就會增加。
ret 棧頂字單元出棧,其值賦給ip暫存器。即實現了乙個程式的轉移,將棧頂字單元儲存的偏移位址作為下一條指令的偏移位址。
call call指令經常和ret指令配合使用,因為cpu執行call指令進行兩部操作將當前ip或cs和ip壓入棧中轉移(相當於jmp指令)call指令不能實現短轉移(短轉移8位位移,近轉移16位位移),除此之外call指令實現轉移的方法和jmp指令的原理相同(只是多了乙個將cs、ip入棧)。
dword ptr:dword 雙字 就是四個位元組ptr pointer縮寫 即指標
裡的資料是乙個位址值,這個位址指向乙個雙字型資料
比如mov eax, dword ptr [12345678] 把記憶體位址12345678中的雙字型(32位)資料賦給eax 。
const 最好是賦初值,否則可能出現不可預料的後果。
你無法排除編譯器直接將const變數當成常量來用,另外,全域性變數有些編譯器會預設賦值0.
const關鍵字的作用主要有以下幾點:
(1)可以定義const常量,具有不可變性。
例如:const int max=100; int array[max];
(3)可以避免意義模糊的數字出現,同樣可以很方便地進行引數的調整和修改。
(4)可以保護被修飾的東西,防止意外的修改,增強程式的健壯性。還是上面的例子,如果在函式體內修改了i,編譯器就會報錯;
例如:void f(const int i)
(6) 可以節省空間,避免不必要的記憶體分配。
例如:#define pi 3.14159 //常量巨集const doulbe pi=3.14159; //此時並未將pi放入rom中 …doublei=pi; //此時為pi分配記憶體,以後不再分配!double i=pi; //編譯期間進行巨集替換,分配記憶體double j=pi;//沒有記憶體分配double j=pi; //再進行巨集替換,又一次分配記憶體!const定義常量從彙編的角度來看,只是給出了對應的記憶體位址,而不是象#define一樣給出的是立即數,所以,const定義的常量在程式執行過程中只有乙份拷貝,而#define定義的常量在記憶體中有若干個拷貝。
(7) 提高了效率。編譯器通常不為普通const常量分配儲存空間,而是將它們儲存在符號表中,這使得它成為乙個編譯期間的常量,沒有了儲存與讀記憶體的操作,使得它的效率也很高。
c和c++中區別與聯絡:
const int n = 10;
在c中n是乙個唯讀變數,n的值執行時才知道,占有記憶體
在c++中n是常量,編譯時就有值,直接替換使用的地方,不佔記憶體
比如
const int n = 10;
int a[n];
用c編譯是編譯不過的,因為n是變數,編譯的時候大小不知道
但用c++編譯可以編譯通過,因為n是常量,相當與 #defind n 3
C語言疑惑
c語言 存款預算 假設銀行一年整存零取的月息為1.875 現在某人手頭有一筆錢,他打算在今後5年中,每年年底取出1000元作為孩子來年的教育金,到第5年孩子畢業時剛好取完這筆錢,現在算一算第1年年初時他應存入銀行多少錢?define rate 0.225 rate 12 1.875 intmain ...
c 語言使用疑惑小記
第一段程式 關於一級指標與二級指標 int p null int p2 p cout p 這裡需要注意的是 null 不代表位址0,或者說在windows中代表0.null作為乙個巨集定義,任何系統均有自己的實現方式。代表乙個不可取值的地方 二級指標是指向一級指標的位址,與null無關。分水線第二段...
C語言 C語言之continue
c語言迴圈 c 語言中的continue語句有點像break語句。但它不是跳出該迴圈語句,continue是跳過本次迴圈直接開始下一次迴圈的。在for迴圈裡,continue會跳過本次迴圈,但是自增語句仍然會執行,而在while和do while語句裡嘖是跳過迴圈重新執行判斷語句 課堂作業內容為 輸...