试图覆盖const char *是一个糟糕的访问

时间:2016-08-05 23:40:35

标签: c

我写了一个简单的就地字符串反转函数,它适用于leetcode(面试练习),但在我自己的系统上给出了一个令人惊讶的错误访问错误。这是一个如此简单的功能,调试器告诉我的其他一切似乎应该是A-OK。为什么我得到了糟糕的访问权限?

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

char* reverseString(char* s) {
  char *t,*r,tmp;
  t=r=s;
  while(*(s++));
  s-=2;
  while(t<s){
    tmp=*t;
    *t=*s; //<--- this is where I have a bad access(?)
    *s=tmp;
    s--;
    t++;
  }
  return r;
}

int main(){
  char *s="12345";
  reverseString(s);
  return 0;
}

请耐心等待,调试器信息非常简单。

╰─$ lldb ./a.out
lldb ./a.out
(lldb) target create "./a.out"
Current executable set to './a.out' (x86_64).
(lldb) r
r
Process 38750 launched: './a.out' (x86_64)
Process 38750 stopped
* thread #1: tid = 0xa0c378, 0x0000000100000f23 a.out`reverseString(s="5") + 99 at reverse-string.c:12, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x100000f91)
    frame #0: 0x0000000100000f23 a.out`reverseString(s="5") + 99 at reverse-string.c:12
   9      s-=2;
   10     while(t<s){
   11       tmp=*t;
-> 12       *t=*s;
   13       *s=tmp;
   14       s--;
   15       t++;
(lldb) frame variable
frame variable
(char *) s = 0x0000000100000f95 "5"
(char *) t = 0x0000000100000f91 "12345"
(char *) r = 0x0000000100000f91 "12345"
(char) tmp = '1'
(lldb) call *s
call *s
(char) $0 = '5'
(lldb) call *t
call *t
(char) $1 = '1'
(lldb) call *t=*s
call *t=*s
(char) $2 = '5'
(lldb) 

请注意,我甚至可以在lldb中调用*t=*s;并获得预期结果。是什么给了什么?

2 个答案:

答案 0 :(得分:3)

修改字符串常量是未定义的行为。即使你将它投射到char *也没有成功。

首先尝试在堆栈或堆上创建非const副本。

答案 1 :(得分:2)

int main(){
  char *s="12345";

s是const char *。您将其分配给char *(应该发出警告),但这并不意味着您现在可以更改它。它仍然是const。在较旧的操作系统中,这不受保护,这意味着您可以修改数据存储。这是hackers-101获取某些东西的方式 - 在一个(假设的)const字符串上编写机器代码,然后让它执行,并且 - 宾果游戏!

较新的操作系统通过使用const数据标记内存页面来保护它(并且还将它们标记为不可执行 - &#39;数据执行保护&#39;在Windows中)。