函式裡定義的字串常量與字元指標
今天產品同事問了個檔案系統相關的問題,平臺這邊的檔案系統自己組織,定義了個結構體陣列,數組裡面有個字元指標,在產品呼叫相關介面函式時將函式名(其實是個字串)賦給該指標。產品同事說這樣有問題,說沒有給指標申請空間,就直接用了,他這麼一問我也懵了,平臺一直在用,沒有問題啊,百度了一下有如下解答:
問題:
請問函式裡定義的字串常量會隨著函式結束時自動消亡嗎?如下程式碼:
char *GetString(void) { char p[] = "hello world"; return p; } void Test4(void) { char *str = NULL; str = GetString(); cout<< str << endl; } // char p[] = "hello world"; 這句helloworld 常量在靜態區,會隨著函式結束消失嗎?
解答:
在windows環境下"hello world"在程式結束前不會消失 聯結器在連線.exe檔案時,將"hello world"儲存在.exe檔案(PE檔案格式)的.rdata區內(只讀儲存區)。在執行這個.exe檔案時,它被對映至程序的地址空間(一般為0x00400000處,所以"hello world"可能在類似0x00420C40處),直至程式執行結束,windows解除對映時,"hello world"才從程序的地址空間消除。所以說,它永久存在。 char p[] = "hello world"; return p; 編譯器在編譯此句時,產生的程式碼是類似於: mov p,0x00420C40("hello world"), 即將"hello world"在程序地址空間的地址傳給p,p在GetString函式退出後不存在(這也只是在高階語言的角度,彙編中p是在棧中所已退出函式後自然不存在),而0x00420c40處的內容卻沒人能改變,因為它是隻讀的,且直接對應於.exe檔案 樓主可以做個實驗:在GetString中檢視"hello world"的地址(即p的值), 退回到main後再在調式器的memory察看視窗輸入這個地址(一般為0x0042....),肯定能看到hello world\0的ascii碼,注意:普通console程式,0x00120000左右的地址表示的是棧上地址,0x00400000之後的地址是.exe檔案的對映,0x7c000000左右的地址是系統dll的變數與函式 其他作業系統的實現也大同小異。總之,"hello world"在程式執行期間一直存在。
通過上面的問題以及解析,對於函式裡的字串常量,首先,它是有記憶體空間的;其次它的地址在記憶體的常量區;再次,它是隻讀的,在你打算對該記憶體區域進行寫操作時,則會出現異常,如:
char *p = NULL;
p = "strings";/*把字串的地址賦給指標p*/
*p = 'S';
該段程式碼執行時就會報錯,在vc上提示xxx記憶體不能為written。
與之相關的一個問題是在除錯IBC時出現的,程式碼如下:
void debugSynSend(int destNode,ulong_t ulMode) { ulong_t rc=0; char *pcTxMsg=NULL;
ros_printf("debugSynSend:destnode=%d,ulMode=0x%lx\r\n",destNode,ulMode); pcTxMsg = (char *)ibcAllocMsg(40,10); /*為訊息負載申請記憶體*/ if(NULL == pcTxMsg) { ros_printf("malloc for ibc test failed\r\n"); return ; } //strcpy(pcTxMsg,"raisecom,syn data"); /*正常程式碼*/ pcTxMsg = "hello,syn data"; /*問題程式碼*/ rc=ibcSend(TID_IBC_DEBUG_SYN_CHANNEL, destNode, TID_IBC_DEBUG_SYN_CHANNEL, IBC_MSG_TM_SYNC | ulMode, (void*)pcTxMsg, 40, 10); ros_printf("rc=0x%lx\r\n",rc); ros_printf("send_test,synchronous send data %s\r\n",rc == IBC_SUCCESS ? "SUCCESS!":"FAILED!"); ibcFreeMsg(pcTxMsg);/*釋放記憶體*/ }
這個程式碼裡面的問題出在將字串常量的地址賦給了一個已經指向一段動態申請的記憶體空間的指標變數,它改了指標變數pcTxMsg裡面的值,其實到這裡還是沒有問題的,但是在最後的釋放記憶體時,就成了釋放常量區了,這段記憶體應該是不可以用free進行操作的,於是出現記憶體釋放問題;另外就我的應用而言,在呼叫封裝的記憶體申請介面時,會在返回的地址前面留了多申請的一段記憶體,用來儲存需要的資訊,而在執行pcTxMsg = "raisecom,syn data";後,因為改變了pcTxMsg的值,所以使用pcTxMsg往前偏移是找不到我要用的東西的。