C语言字符串倒序
某天在某处看到一个C语言问题,要求将字符串倒序,说是倒序函数有bug,当执行交换两个数的时候会出现core dump,开始看到这个错误以为指针错误,但是别人调试过发现指针并没有问题,然后一下就没找到问题所在,于是回来就用gdb调试了一下,函数到关键部分如下:
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--;
}
}
拿到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[]定义字符串可读可写