【问题标题】:Why is this string reversal C code causing a segmentation fault? [duplicate]为什么这个字符串反转 C 代码会导致分段错误? [复制]
【发布时间】:2010-12-09 12:58:15
【问题描述】:

我正在尝试编写代码来反转字符串(我只是想在 C 编程和指针操作方面做得更好),但我不知道为什么我会收到 segmentation fault

#include <string.h>

void reverse(char *s);

int main() {
    char* s = "teststring";
    reverse(s);

    return 0;
}

void reverse(char *s) {
    int i, j;
    char temp;

    for (i=0,j = (strlen(s)-1); i < j; i++, j--) {
        temp = *(s+i);     //line 1
        *(s+i) = *(s+j);   //line 2
        *(s+j) = temp;     //line 3
    }
}

导致分段错误的是第 2 行和第 3 行。我知道可能有更好的方法可以做到这一点,但我有兴趣找出具体在我的代码中导致分段错误的原因。

更新:我已按要求包含调用函数。

【问题讨论】:

  • Segfault 几乎总是意味着您试图取消引用空指针。通过 GDB 运行您的代码,找到它在哪一行出现段错误,并查看那里的哪个指针为空 (0x000000)。
  • 另外,为什么要使用两个变量(i 和 j)?你完全可以用 counter 做到这一点。
  • 你能贴出创建 char* 并将其传递给 reverse() 的代码吗?
  • 并且不要在循环条件中使用strlen()
  • @Carl Norum:如果你仔细看,上面代码示例中的循环条件中没有使用strlen

标签: c string segmentation-fault


【解决方案1】:

仅凭该代码无法判断。很可能,您传递的指针指向无效内存、不可修改内存或其他类型的内存,这些内存无法按照您在此处的处理方式进行处理。

你如何调用你的函数?

添加:您正在传递一个指向字符串文字的指针。字符串文字是不可修改的。您不能反转字符串文字。

改为传递指向可修改字符串的指针

char s[] = "teststring";
reverse(s); 

这已经在这里解释得很清楚了。 "teststring" 是一个字符串文字。字符串文字本身是一个不可修改的对象。在实践中,编译器可能(并且将)将其放入只读内存中。当你像这样初始化一个指针时

char *s = "teststring";

指针直接指向字符串文字的开头。在一般情况下,任何修改s 所指内容的尝试都被视为失败。你可以读它,但你不能写它。因此,强烈建议仅使用指向 const 变量的指针来指向字符串文字

const char *s = "teststring";

但是当你声明你的s

char s[] = "teststring";

你会得到一个完全独立的数组s,它位于普通的可修改内存中,它只是用字符串字面量初始化。这意味着独立的可修改数组s 将从字符串文字中获取其初始值已复制。之后,您的 s 数组和字符串文字继续作为完全独立的对象存在。文字仍然不可修改,而您的 s 数组是可修改的。

基本上,后一个声明在功能上等价于

char s[11];
strcpy(s, "teststring");

【讨论】:

  • 这行得通。谢谢。但是您能否更详细地解释一下这两个字符串初始化之间的区别? IE。为什么我创建字符串文字的方式,而“数组”语法创建可修改的字符串?
  • @james 字符串文字进入只读存储。但是通过执行“char []s = ...”,您是在声明一个数组并将其初始化为文字,而不是获取指向文字的指针。
  • @james:请参阅我的回复中的附加文本。
【解决方案2】:

由于多种原因,您的代码可能会出现段错误。以下是我想到的那些

  1. s 为 NULL
  2. s 指向一个保存在只读内存中的 const 字符串
  3. s 不是 NULL 终止的

我认为#2 是最有可能的。可以给我们看看reverse的调用点吗?

编辑

根据您的示例 #2 绝对是答案。 C/C++ 中的字符串文字不可修改。正确的类型实际上是const char* 而不是char*。您需要做的是将可修改的字符串传递到该缓冲区。

快速示例:

char* pStr = strdup("foobar");
reverse(pStr);
free(pStr);

【讨论】:

  • 文字“teststring”位于只读内存中的某处,您不能对其进行写入。这曾经是一种常见的做法,即分配一些内存的一种方式,但大多数现代系统都不允许这样做。 JaredPar 的示例之所以有效,是因为 strdup 分配了您随后拥有的内存。
【解决方案3】:

你是在测试这样的东西吗?

int main() {
    char * str = "foobar";
    reverse(str);
    printf("%s\n", str);
}

这使 str 成为字符串文字,您可能无法编辑它(对我来说是段错误)。如果您定义char * str = strdup(foobar),它应该可以正常工作(对我有用)。

【讨论】:

    【解决方案4】:

    你的声明完全错误:

    char* s = "teststring";
    

    “teststring”存储在代码段中,和代码一样是只读的。并且,s 是指向“teststring”的指针,同时,您正在尝试更改只读内存范围的值。因此,分段错误。

    但是有:

    char s[] = "teststring";
    

    s 用“teststring”初始化,当然它在代码段中,但是在这种情况下还有一个额外的复制操作到堆栈。

    【讨论】:

      【解决方案5】:

      请参阅 C 常见问题列表中的 Question 1.32

      这些初始化有什么区别?

      char a[] = "string literal";
      char *p  = "string literal";
      

      如果我尝试为 p[i] 分配新值,我的程序会崩溃。

      答案:

      字符串文字(C 源代码中双引号字符串的正式术语)可以以两种略有不同的方式使用:

      作为 char 数组的初始值设定项,就像在 char a[] 的声明中一样,它指定了该数组中字符的初始值(以及必要时的大小)。

      在其他任何地方,它都会变成一个未命名的静态字符数组,这个未命名的数组可能存储在只读内存中,因此不一定可以修改。在表达式上下文中,数组像往常一样立即转换为指针(参见第 6 节),因此第二个声明初始化 p 以指向未命名数组的第一个元素。

      有些编译器有一个开关来控制字符串文字是否可写(用于编译旧代码),有些编译器可能有选项可以将字符串文字正式视为const char 的数组(以便更好地捕获错误)。

      强调我的

      另见Back to BasicsJoel

      【讨论】:

      • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review
      • @Sree 现在应该修复了。
      【解决方案6】:

      您使用的是哪个编译器和调试器?使用 gcc 和 gdb,我将使用 -g 标志编译代码,然后在 gdb 中运行它。当它出现段错误时,我会做一个回溯(gdb 中的 bt 命令),看看哪个是导致问题的违规行。此外,我会一步一步地运行代码,同时“观察”gdb 中的指针值并知道问题到底出在哪里。

      祝你好运。

      【讨论】:

        【解决方案7】:

        正如上面提供的一些答案,字符串内存是只读的。但是,一些编译器提供了使用可写字符串进行编译的选项。例如。使用 gcc,支持 3.x 版本 -fwritable-strings,但不支持较新的版本。

        【讨论】:

          【解决方案8】:

          我认为strlen 不能工作,因为 s 不是 NULL 终止的。所以你的 for 迭代的行为不是你所期望的。 由于 strlen 的结果将优于 s 长度,因此您将在不应该写入的内存中写入。

          此外,s 指向一个由只读存储器保存的常量字符串。你不能修改它。尝试像 strlen 示例中那样使用 gets 函数来初始化 s

          【讨论】:

          • s如何不为null终止?文字字符串始终以空值结尾。
          猜你喜欢
          • 2016-10-25
          • 2017-08-18
          • 1970-01-01
          • 2011-03-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-09-22
          相关资源
          最近更新 更多