为什么这个字符串反转 C 代码会导致分段错误?

发布于 2024-08-08 22:42:17 字数 634 浏览 7 评论 0原文

我正在尝试编写代码来反转字符串(我只是想更好地进行 C 编程和指针操作),但我无法弄清楚为什么我会得到 分段错误

#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 行导致了分段错误。我知道可能有更好的方法来做到这一点,但我有兴趣找出我的代码中具体是什么导致了分段错误。

更新:我已按要求添加了调用功能。

I am trying to write code to reverse a string in place (I'm just trying to get better at C programming and pointer manipulation), but I cannot figure out why I am getting a 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
    }
}

It's lines 2 and 3 that are causing the segmentation fault. I understand that there may be better ways to do this, but I am interested in finding out what specifically in my code is causing the segmentation fault.

Update: I have included the calling function as requested.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

逆蝶 2024-08-15 22:42:17

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

你如何调用你的函数?

补充:您正在传递一个指向字符串文字的指针。字符串文字是不可修改的。您无法反转字符串文字。

而是传递一个指向可修改字符串的指针

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");

There's no way to say from just that code. Most likely, you are passing in a pointer that points to invalid memory, non-modifiable memory or some other kind of memory that just can't be processed the way you process it here.

How do you call your function?

Added: You are passing in a pointer to a string literal. String literals are non-modifiable. You can't reverse a string literal.

Pass in a pointer to a modifiable string instead

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

This has been explained to death here already. "teststring" is a string literal. The string literal itself is a non-modifiable object. In practice compilers might (and will) put it in read-only memory. When you initialize a pointer like that

char *s = "teststring";

the pointer points directly at the beginning of the string literal. Any attempts to modify what s is pointing to are deemed to fail in general case. You can read it, but you can't write into it. For this reason it is highly recommended to point to string literals with pointer-to-const variables only

const char *s = "teststring";

But when you declare your s as

char s[] = "teststring";

you get a completely independent array s located in ordinary modifiable memory, which is just initialized with string literal. This means that that independent modifiable array s will get its initial value copied from the string literal. After that your s array and the string literal continue to exist as completely independent objects. The literal is still non-modifiable, while your s array is modifiable.

Basically, the latter declaration is functionally equivalent to

char s[11];
strcpy(s, "teststring");
如此安好 2024-08-15 22:42:17

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

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

我认为#2是最有可能的。您能给我们展示一下反向调用的地址吗?

编辑

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

快速示例:

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

You code could be segfaulting for a number of reasons. Here are the ones that come to mind

  1. s is NULL
  2. s points to a const string which is held in read only memory
  3. s is not NULL terminated

I think #2 is the most likely. Can you show us the call site of reverse?

EDIT

Based on your sample #2 is definitely the answer. A string literal in C/C++ is not modifiable. The proper type is actually const char* and not char*. What you need to do is pass a modifiable string into that buffer.

Quick example:

char* pStr = strdup("foobar");
reverse(pStr);
free(pStr);
冷心人i 2024-08-15 22:42:17

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

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

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

Are you testing this something like this?

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

This makes str a string literal and you probably won't be able to edit it (segfaults for me). If you define char * str = strdup(foobar) it should work fine (does for me).

许一世地老天荒 2024-08-15 22:42:17

您的声明完全错误:

char* s = "teststring";

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

但是 with:

char s[] = "teststring";

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

Your declaration is completely wrong:

char* s = "teststring";

"teststring" is stored in the code segment, which is read-only, like code. And, s is a pointer to "teststring", at the same time, you're trying to change the value of a read-only memory range. Thus, segmentation fault.

But with:

char s[] = "teststring";

s is initialized with "teststring", which of course is in the code segment, but there is an additional copy operation going on, to the stack in this case.

橪书 2024-08-15 22:42:17

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

这些初始化有什么区别?

char a[] = "字符串文字";
char *p = "字符串文字";

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

答案:

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

作为 char 数组的初始值设定项,如 char a[] 的声明所示,它指定该数组中字符的初始值(如果需要,还指定其大小)。

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

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

强调我的

另请参阅 回到基础知识 作者 < a href="http://www.joelonsoftware.com/" rel="nofollow noreferrer">乔尔。

See Question 1.32 in the C FAQ list:

What is the difference between these initializations?

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

My program crashes if I try to assign a new value to p[i].

Answer:

A string literal (the formal term for a double-quoted string in C source) can be used in two slightly different ways:

As the initializer for an array of char, as in the declaration of char a[], it specifies the initial values of the characters in that array (and, if necessary, its size).

Anywhere else, it turns into an unnamed, static array of characters, and this unnamed array may be stored in read-only memory, and which therefore cannot necessarily be modified. In an expression context, the array is converted at once to a pointer, as usual (see section 6), so the second declaration initializes p to point to the unnamed array's first element.

Some compilers have a switch controlling whether string literals are writable or not (for compiling old code), and some may have options to cause string literals to be formally treated as arrays of const char (for better error catching).

(emphasis mine)

See also Back to Basics by Joel.

っ左 2024-08-15 22:42:17

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

祝你好运。

Which compiler and debugger are you using? Using gcc and gdb, I would compile the code with -g flag and then run it in gdb. When it segfaults, I would just do a backtrace (bt command in gdb) and see which is the offending line causing the problem. Additionally, I would just run the code step by step, while "watching" the pointer values in gdb and know where exactly is the problem.

Good luck.

月隐月明月朦胧 2024-08-15 22:42:17

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

As some of the answers provided above, the string memory is read-only. However, some compilers provide an option to compile with writable strings. E.g. with gcc, 3.x versions supported -fwritable-strings but newer versions don't.

薄情伤 2024-08-15 22:42:17

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

另外 s 指向只读存储器保存的常量字符串。您无法修改它。尝试使用 gets 函数来 init ,就像在 strlen 例子

I think strlen can not work since s is not NULL terminated. So the behaviour of your for iteration is not the one you expect.
Since the result of strlen will be superior than s length you will write in memory where you should not be.

In addition s points to a constant strings hold by a read only memory. You can not modify it. Try to init s by using the gets function as it is done in the strlen example

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文