C/C++踩坑記錄(二)一段有趣的常量字串
測試平臺:
Ubuntu 16.04
Windows Mingw GCC gcc version 5.3.0 (i686-posix-dwarf-rev0, Built by MinGW-W64 project)
編譯器:GCC 5.3
開始不得不吐槽一下網上那些所謂“C語言字串翻轉”的實現的哥們啊,我看到他們程式碼#include<iostream>
和引入STL的時候我都不知道我是該哭還是該笑。
好吧我們開始,從一個簡單的問題去分析問題。
先貼程式碼:
這是我實現的:
#include <stdio.h> void s_reverse(char *s) { char *h = s; char *t = s; char ch; /* t指向s的尾部 */ while (*t++) {}; t--; /* 與t++抵消 */ t--; /* 回跳過結束符'\0' */ /* 當h和t未重合時,交換它們所指向的字元 */ while (h < t) { ch = *h; *h++ = *t; /* h向尾部移動 */ *t-- = ch; /* t向頭部移動 */ } } int main() { char str[] = {'H','e','l','l','o','\0'};//為啥分成兩個後面說 char *str_1 = "Hello, World!"; printf("origin str ---> %s\n",str); printf("origin str_1 ---> %s\n",str_1); s_reverse(str); s_reverse(str_1); #ifdef _MSC_VER printf("%s\n", str, "Visual Stadio 2017"); printf("str--->%s\n",str); printf("str_1--->%s\n",str_1); #else printf("str--->%s\n",str); printf("str_1--->%s\n",str_1); #endif return 0; }
這是自帶的:
#include <string.h>
int main() {
char *str ="Hello, World!";
strrev(str);
return 0;
}
只關注str_1現在分別來看看它們在Mingw下的表現(注:這裡Mingw下的效果和Linux測試一致)和MSVC下的表現
MyCode on Mingw
MyCode on MSVC
Official on Mingw
Official on MSVC
那麼GCC下的報錯問題出在哪呢
追根溯源才是人類的本質,現在生成一下GCC的彙編程式碼,看看到底發生了什麼。
gcc ./main.c -S
gcc ./str.c -S
現在分別生成了兩份程式碼 main.s裡面我們可以看到這麼一段
LC0:
.ascii "Hello, World!\0"
是的這玩意被定義為常量了,無法被訪問
使用陣列形式宣告字串
char str[] = {'H','e','l','l','o','\0'};
然後我們註釋掉str_1相關的東西再來看看str。
Any thin look like good.
現在再來生成彙編程式碼,我們能看到ascii不見了,所有的東西都在棧上,變成了可以訪問的變數。
雖然已經找到問題所在,下面我們來除錯一下丟擲異常的地方。。
call _strrev
,所以我們根據他的原型,用上面自己實現的程式碼去尋找問題根源。
gdb main.exe
設定斷點
break s_reverse
按下r執行
通過disassemble
命令來檢視彙編程式碼
彙編步進除錯ni
慢慢的除錯過去,會發現
mov %dl,(%eax)
這句話的意思是從資料暫存器DX低位取資料放到eax中,但是此時資料暫存器是不可寫的,所以丟擲了異常中斷
參考文獻:
[DWARF4]http://dwarfstd.org/doc/DWARF4.pdf
[.cfi指令解讀]https://blog.csdn.net/jtli_embeddedcv/article/details/9321253
[劍指offer]
[彙編中的暫存器]https://www.cnblogs.com/wisehead/arti