運行以下程序時,出現這類錯誤:*** stack smashing detected ***: ./test_global terminated
。錯誤緣由多是由於scanf("%d%d", &row, &col)
接收的是int
型,可是我使用的是short int
,長度是Int
的一半。修改爲int
後錯誤消失。linux
#include<stdio.h> int main(){ int row, col; scanf("%d%d", &row, &col); printf("%d %d", row, col); return 0; }
使用gcc編譯時出現的警告以下:
出現的錯誤以下:
ios
運行以下程序時,會無終止地打印-1。緣由是變量p所指向的變量k在addr()函數執行後自行銷燬,k所使用的內存被分配給loop()中的變量i,從而致使p指向i。而此時對p的操做是減1,對i的操做是加1,致使i的值始終爲-1,沒法跳出循環。shell
#include<stdio.h> void addr(); void loop(); long *p; int main(){ addr(); loop(); } void addr(){ long k; k = 0; p = &k; } void loop(){ long i, j; j = 0; for (i = 0; i<10;i++){ (*p)--; j++; printf("%d\n", i); } }
程序運行輸出結果以下:
程序調試結果以下:
數組
雖然運行下面代碼不會出錯,可是對數組a[10]的寫操做超出了維度,致使在地址爲a+10的地方也寫入了數據,可是容易引起潛在bug。函數
#include<stdio.h> int main() { int i; int a[10]; for (i = 0; i <= 10; ++i) { a[i] = 0; printf("%d\n", i); } exit(0); }
對於將指針做爲參數進行傳遞時,若是是將在子函數內賦值給一個新申請的空間,那麼就要注意在傳遞指針時,須要傳遞指針的地址,即指針的指針。錯誤程序以下:oop
#include<stdio.h> void allocateInt(int * i, int m); void main() { int m = 5; int * i = &m; printf("i address: %x\n", &i); allocateInt(i, m); printf("*i = %d\n", *i); } void allocateInt(int * i, int m) { printf("i address: %x\n", &i); i = (int *) malloc(sizeof(int)); *i = 3; }
雖然對該問題的解釋通常是:在傳遞參數時,系統爲子函數的變量新申請一部分空間,所以在void allocateInt(int * i)
中,i的地址和在void main()
中的地址是不一樣的,而void allocateInt(int * i)
中的i是局部變量,在子函數運行結束會被釋放掉,所以void main()
中的i是沒法獲得malloc的地址的,更不可能獲得新的賦值。
下面經過gdb調試以及反彙編來進行說明:性能
allocateInt(i, m);
語句時,變量i和m的內存地址以下圖所示,&i=0x7fffffffdaf0,&m=0x7fffffffdaec:allocateInt
函數。進入時,i=0x7ffff7ffe168, m=0,也就是說i和m還並無被傳遞賦值,結果以下所示:void allocateInt(int * i, int m)
中的printf("i address: %x\n", &i);
,以下圖所示:push %rbp
,也就是把rbp寄存器入棧;mov %rsp,%rbp
,其中rsp是堆棧指針。也就是把堆棧指針的值賦值給rbp寄存器;sub $0x10,%rsp
,也就是把堆棧指針所指向的地址減小16個字節。這是由於變量i和m一共佔用了16個字節;mov %rdi,-0x8(%rbp)
,也就是把寄存器rdi的值(rdi=0x7fffffffdaec,以下圖所示)賦值給i。由於i的地址就是rbp-0x8;mov %esi,-0xc(%rbp)
,做用相似於第4條,將寄存器esi的值(esi=0x5,以下圖所示)賦值給m。lea
命令能夠網上查找,主要就是一種更加有效的mov方法;callq 0x4004a0 <printf@plt>
,意思是調用print函數。可是這裏並非直接調用print函數,而是調用相似於print函數在進程中的別名。由於這是公用庫中的函數,所以不一樣進程中都會調用,因此只在進程中存留一個函數地址或者別名就好。具體參見stackoverflow上的一篇文章What does @plt mean here?。運行以下代碼時,本意是用g_logger.WriteLog()將"in A()"寫入文本文件中,可是結果倒是將"in A()"打印在了shell裏。ui
// file: main.cc #include <iostream> #include "CLLogger.h" using namespace std; extern CLLogger g_logger; class A { public: A() { CLStatus s = g_logger.WriteLog("in A()", 0); if(!s.IsSuccess()) cout << "g_logger.WriteLog error" << endl; } }; A g_a; CLLogger g_logger; int main() { return 0; } // file: CLLogger.h #include "CLStatus.h" class CLLogger { public: CLLogger(); virtual ~CLLogger(); CLStatus WriteLog(const char *pstrMsg, long lErrorCode); private: CLLogger(const CLLogger&); CLLogger& operator=(const CLLogger&); private: int m_Fd; }; // file: CLStatus.h class CLStatus { public: CLStatus(long lReturnCode, long lErrorCode); CLStatus(const CLStatus& s); virtual ~CLStatus(); public: bool IsSuccess(); public: const long& m_clReturnCode; const long& m_clErrorCode; private: long m_lReturnCode; long m_lErrorCode; };
緣由是g_a是定義在g_logger以前,所以在運行到語句CLStatus s = g_logger.WriteLog("in A()", 0);
時,g_logger仍未定義。但因爲在文件開頭聲明瞭extern CLLogger g_logger;
,所以編譯器不會報錯,而此時默認將聲明爲外部變量的g_logger中的文件操做符m_Fd賦值爲0,以下:
spa
int a = -5; int b = 0; ................................................ if(a > 0){ if(a <= 0){ b = 1; b = 2; } } else{ else{ b = 2; b=1; } }
關於分支預測的一些預測方式能夠參考一篇博客C++性能榨汁機之分支預測器
參考資料
Visual Studio文檔:寄存器使用
探究Linux下參數傳遞及查看和修改方法
gdb 調試入門,大牛寫的高質量指南
GDB的調試命令
What does @plt mean here?
C++性能榨汁機之分支預測器