標籤:abc printf 交換 完整 分代 null 資料 部分 use
某天在某處看到一個C語言問題,要求將字串倒序,說是倒序函數有bug,當執行交換兩個數的時候會出現core dump,開始看到這個錯誤以為指標錯誤,但是別人調試過發現指標並沒有問題,然後一下就沒找到問題所在,於是回來就用gdb調試了一下,函數到關鍵區段如下:
void reverse_array(char *arr){ char *p = NULL,*q = NULL,tmp = 0; p = arr; q = arr; while( *(++q) != ‘\0‘ );q--; while(p < q){ tmp = *p; *p = *q; *q = tmp; p++; q--; }}
拿到linux下寫了個完整程式,如下
#include <stdio.h>
void reverse_str(char *str){ char *p = NULL,*q = NULL,tmp = 0; p = str; q = str; while( *(++q) != ‘\0‘ );q--; while(p < q){ tmp = *p; *p = *q; *q = tmp; p++; q--; }}int main(void){ char *str = "abcdefg"; printf("%s\n",str); reverse_str(str); printf("%s\n",str); return 0;}
編譯:gcc -o tst.elf tst.c -g,然後運行,跟上面提到的bug一樣,出現Core Dump直接退出,接著調試:gdb ./tst.elf,打上斷點b 5,然後一次單步,最後發現運行到*p = *q這句話時,程式就出現上述錯誤了,但是此時p,q指標並不是野指標,指向的內容也都是正確的,這裡一下卡住了,然後晚上搜尋了一下,發現是字串常量的唯讀屬性導致此處修改失敗,只需要將char *str = "abcdefg" 修改為char str[] = "abcdefg",修改後發現正確了,下面簡單總結一下:
bug原因:
char *str聲明的字串位於唯讀資料區段,無法被修改,從而導致*p = *q這個嘗試修改唯讀資料引發錯誤,程式退出
解決方案:
修改字串使其處於棧區,代碼如下
char str[] = "abcdefg";
調試:修改程式如下
#include <stdio.h>void reverse_str(char *str){ char *p = NULL,*q = NULL,tmp = 0; p = str; q = str; while( *(++q) != ‘\0‘ );q--; while(p < q){ tmp = *p; *p = *q; *q = tmp; p++; q--; }}int main(void){ char *tst = "abcdef"; char str[] = "abcdef"; reverse_str(str); printf("%s\n",str); return 0;}
編譯:gcc -o tst.elf tst.c -g,然後反組譯碼:objdump -D tst.elf > tst.dis,得到相關部分代碼如下所示,
0000000000400617 <main>: 400617: 55 push %rbp 400618: 48 89 e5 mov %rsp,%rbp 40061b: 48 83 ec 20 sub $0x20,%rsp 40061f: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax 400626: 00 00 400628: 48 89 45 f8 mov %rax,-0x8(%rbp) 40062c: 31 c0 xor %eax,%eax 40062e: 48 c7 45 e8 04 07 40 movq $0x400704,-0x18(%rbp) // char *tst = "abcdefg"; 400635: 00 400636: c7 45 f0 61 62 63 64 movl $0x64636261,-0x10(%rbp) // char tst[] = "abcdefg"; 40063d: 66 c7 45 f4 65 66 movw $0x6665,-0xc(%rbp) Disassembly of section .rodata:0000000000400700 <_IO_stdin_used>: 400700: 01 00 add %eax,(%rax) 400702: 02 00 add (%rax),%al 400704: 61 (bad) 400705: 62 63 64 65 66 (bad) {%k5}
由此可知:編譯後產生的程式,char *str會放到.rodata段,也就是唯讀段;而char str[]則是放入棧(rbp是對棧的操作),是可修改的
最後總結起來一句話:char *str定義字串唯讀不可寫,char str[]定義字串可讀可寫
C語言字串倒序