如下文章來源於程序員入門進階 ,做者明哥程序員
C/C++的學習裙【七一二 二八四 七零五 】,不管你是小白仍是進階者,是想轉行仍是想入行均可以來了解一塊兒進步一塊兒學習!裙內有開發工具,不少乾貨和技術資料分享!編程
不要陷在指針裏面,最好的方法是跳出指針,咱們從最終結果來思考問題。因而個人解題思路老是很偏,可是直指本質。數組
咱們寫一段代碼:函數
編譯,反編譯,反編譯這裏咱們用objdump -d hello >1.txt,若是你是用IDA,會發現出來的彙編不同,由於各類格式的彙編,有不一樣的寫法,這裏主要就是Intel和AT&T,GNU遵循的是AT&T的寫法。工具
在裏面找到咱們的add 和main學習
這裏我不會展開去講AT&T,這個玩意就是查表,我這裏主要講幾個內容,這個很是重要。彙編語言中本身要關注堆棧平衡,再一個就是寄存器的保存與恢復,第三個就是調用參數約定。開發工具
舉例來講,add %edx,%eax ,這個的結果在哪裏?這個都是指令直接就決定的,也就是咱們的CPU設計時候,它的這條指令執行完,數據會放在哪裏。優化
咱們看到的main方法中的spa
mov $0x6,%esi操作系統
mov $0x5,%edi
這兩個就是咱們add方法執行的兩個參數,它賦值到這兩個寄存器,那麼在用這兩個寄存器,是否是要把寄存器當前的值保存下來呢?因此你能看到緊挨着上面的就是保存動做。
而後callq 調用add方法,這裏咱們看緊跟着的 mov %eax ,-0x4(%rbp) ,咱們剛纔說了add方法執行後,eax裏面是結果。
這裏將%eax的值放到了 %rbp寄存器-0x4的地方,這個地方是什麼?是棧,具體到代碼中,就是sum的位置。
int sum =add(5,6);的執行過程就是這樣的。我這裏分享一個圖,主要說的是傳參的約定,咱們知道調用函數時候是有參數的約定,其實二進制這裏也是有的,這個叫作System V ABI 。
我通常是怎麼掌握這些規則,去寫彙編,通常就是用C寫一些,編譯,反彙編來看,這個你們能夠參考一本書,
咱們編譯完的程序,是沒有sum這個變量,在執行的代碼中,都是變成了具體的位置,這裏簡單說下就是堆仍是棧,局部變量是在棧上面,局部靜態變量是在堆上面。
全局數據分兩類,一類是初始化的全局變量,一類是未初始化的,未初始化的運行時候系統會默認給初始化爲0(可是不要覺得它就必須是0,這個就是跟運行機制有關,咱們寫代碼必定記住,不要去嘗試依賴外部不肯定的因素)
全局數據區分爲 data rodata 和 bss ,rodata這個就是read only,只讀區域這個是由加載器加載程序進入進程時候,會對這個數據區域的page,作設定,設定只讀,若是後續在這裏寫入數據,就會報錯。
data就是咱們常規的數據,舉例就是 int a=100;這類全局變量會放在data區域。而咱們若是是int a;這個全局變量,就會放置到bss,這個區域叫作全局未初始化區域,這個跟data的區別在於,這個bss在程序中不佔用大小,只是在加載時候會在內存中佔用大小。
text區域就是代碼段。
說到這裏,我這裏再說一個內容,咱們在看到代碼時候,發現printf這個函數,後面有個plt。
咱們來講下這個plt。plt的意思是,這個方法不在這個程序裏面,是在外面的,而對應的位置,這裏就是4003f0,這個位置是什麼?咱們知道printf是在glibc.so ,這裏用的動態庫。
咱們程序要跑起來,是要補全這裏的printf的執行塊的,系統的作法就是,先放置一個佔位位置,而後程序加載的時候,加載器知道這裏須要一個printf的真實地址,這些須要放置地址的區域,統一在plt這個區域裏面,在這個位置放入真正的printf的入口點。
聽起來很繞,咱們用一個例子,你就能明白了。可是這裏的例子,估計又牽扯進來新的概念,你們先理解下吧。
typedef int (*operate)(int a,int b);這個定義了一個類型,類型是一個有兩個參數,一個返回值的函數類型。咱們平時的類型就是int,這裏是一個函數的類型。
而後聲明一個列表,把add,和sub放進來,咱們直接調用便可。這裏就想給你們說,這個是能夠放置一個函數名的,等下咱們繼續操做,就可以更深刻的理解這個函數名。
那麼看到這裏,咱們開始真正進入指針的世界,咱們來理解指針。個人操做就是,編譯,反彙編,咱們先看下代碼:
這裏咱們引入了指針p,儲存了變量a的地址,而後*p表明拿出p地址裏面的內容,咱們看下反編譯彙編,分析下這個過程。
這裏mov $0x5,-0xc(%rbp)將5放入rbp-0xc的位置。lea -0xc(%rbp),%rax mov %rax,-0x18(%rbp) 這兩句話的意思是,拿到 -0xc(%rbp)的地址,放入 -0x18(%rbp)位置,也就是指針p的位置。
這裏的mov (%rax),%eax 指的意思是,將rax裏面的值讀出來,找到這個值對應的地址的內容,存儲到%eax裏面,這裏能夠用c語言寫就是,int c=*p;
從這裏面我想說的就是,咱們的指針,這些,在彙編形態下,不過是兩種類型,一個是讀取寄存器的值,一個是讀取把寄存器中的值當作地址對應位置的值。
咱們只要這樣子去理解,基本上就能清晰的瞭解指針,指針所存儲的值,咱們通常都是用它所指向的地址內容,它自己的地址只是途徑,相似於咱們在圖書館查出來書的序號A-1-303,咱們真正要的是這個位置的那本書。
當咱們理解了這個,這裏的add函數就是個地址,咱們這麼來看下。
咱們直接用void *p=add;而後把這個p讓編譯器按照add對應的參數,返回類型去調用,這樣子就能夠用到add函數。
int 這類咱們就能理解了,那麼咱們再來講下int[];這個看完彙編語句,一會兒就明白了。咱們說過一點,就是在真實的計算機上面,執行的是指令,指令理解就兩類,一個是值,一個是地址,也能夠理解成直接引用,間接引用。
這裏想說的是,array在編譯器裏面,就是理解成一個指針,指向了一個int數組。咱們把array賦值到p指針,發現p[1]跟array[1]是同樣的。咱們看反彙編代碼:打印語句改爲printf("p[1]=%d,array[1]=%d\n",p[1],array[1]);,來比對下。
這裏數組的取值,直接被優化了,直接用的mov -0x1c(%rbp),%edx 從上面的存儲能夠看到,這個位置直接就是array[1],具體指令是movl $0x2,-0x1c(%rbp),咱們指針的獲取,這裏很明確,拿到數組起始地址,用add %0x4,%rax,進行了偏移,找到了p[1]位置。
這裏分享下,地址的+1,指的是地址所指向的內容的大小,進行偏移。理解了這個,再去理解數組,就很好理解了。
咱們把代碼改爲
long *p=array;
printf("p[1]=%d,array[1]=%d\n",(int)(p[1]),array[1]);
打印出來就不同了,緣由就是p+1,是加的sizeof(long) 的大小,也就是它所指向的內容所表明的大小。因此咱們再來講下,
int array[3][5]={1,2,3,4,5,
6,7,8,9,10,
11,12,13,14,15};
而後 int (*p)[5] =array; 那麼p[1][0]是多少呢?咱們前面說了,p+1是依據它指向的大小,這裏就是 int [5] 的大小,因此就是輸出的6。這裏也就是array實際就是一個指向一行五個int的一個數組指針。
核心一句話,指針的+1是根據指向的數據大小決定,同時在指令級別去看,只有兩種解析,就是值和地址。
C/C++應用於Windows操做系統,驅動、補丁,圖像處理、音視頻處理,工業控制軟件、嵌入式(手機、智能機)等領域,C++ 已經成爲了最受開發人員歡迎的語言之一,鞏固了全球的系統和服務。
◆若是你想提高你的編程能力,以便更好從事編程類工做的話!這裏爲你分享一個學習基地!【點我進入】
分享(源碼、項目實戰視頻、項目筆記,基礎入門教程)歡迎轉行和學習編程的夥伴,利用更多的資料學習成長比本身琢磨更快哦!