为啥这个字符串反转 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 代码会导致分段错误? [复制]的主要内容,如果未能解决你的问题,请参考以下文章