为啥这个字符串反转 C 代码会导致分段错误? [复制]

Posted

技术标签:

【中文标题】为啥这个字符串反转 C 代码会导致分段错误? [复制]【英文标题】: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 【参考方案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】:

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

    s 为 NULL s 指向一个保存在只读内存中的 const 字符串 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终止?文字字符串始终以空值结尾。

以上是关于为啥这个字符串反转 C 代码会导致分段错误? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的字符串分配会导致分段错误?

为啥这个字符会出现分段错误?

为啥释放内存会导致分段错误?

为啥 const int main = 195 会导致程序正常工作,但没有 const 会导致分段错误?

为啥重新声明 std::cout 会导致分段错误?

请解释为啥这个 C 代码给我一个分段错误?