-1

在编写了一个反转字符串的程序后,我无法理解为什么在尝试反转字符串时会出现段错误。我在下面列出了我的程序。

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

void reverse(char *);

int main() {
  char *str = calloc(1,'\0');
  strcpy(str,"mystring0123456789");
  reverse(str);
  printf("Reverse String is: %s\n",str);
  return 0;
}

void reverse(char *string) {
  char ch, *start, *end;
  int c=0;
  int length = strlen(string);
  start = string;
  end = string;

  while (c < length-1){
    end++;
    c++;
  }
  c=0;

  while(c < length/2){
    ch = *end;
    *end = *start;
    *start = ch;
    start++;
    end--;
    c++;
  }
}

第一个问题:

即使我只为 char 指针 str ( calloc(1,'\0')) 分配了 1 个字节的内存,并且我将一个 18 字节的字符串复制mystring0123456789到其中,它也没有抛出任何错误,并且程序在没有任何 SEGFAULT 的情况下运行良好。

为什么我的程序没有抛出错误?理想情况下,它应该抛出一些错误,因为它没有任何内存来存储那个大字符串。有人可以解释一下吗?

该程序运行完美,并给了我输出Reverse String is: 9876543210gnirtsym

第二个问题:

如果替换语句

strcpy(str,"mystring0123456789");

str="mystring0123456789\0";

即使我为 str ( malloc(100)) 分配了足够的内存,程序也会出现分段错误。

为什么程序抛出分段错误?

4

5 回答 5

5

即使我只为 char 指针 str(calloc(1,'\0')) 分配了 1 个字节的内存,并且我将一个 18 字节的字符串“mystring0123456789”复制到其中,它没有抛出任何错误并且程序运行良好,没有任何 SEGFAULT。

你的代码有一个错误——当然它不会像你期望的那样。修复错误,谜团就会消失。

如果替换语句
strcpy(str,"mystring0123456789");

str="mystring0123456789\0";
即使我为 str (malloc(100)) 分配了足够的内存,程序也会出现分段错误。

因为当你完成这个时,str指向一个常数。这会丢弃 的先前值str,一个指向您分配的内存的指针,并将其替换为指向该常量的指针。

您不能修改常量,这就是使其成为常量的原因。该strcpy函数将常量复制到一个变量中,然后您可以对其进行修改。

想象一下,如果你能做到这一点:

int* h = &2;

现在,如果您这样做了,*h = 1;您将尝试更改代码中的常量2,这当然是您无法做到的。

这实际上就是你正在做的事情str="mystring0123456789\0";。它str指出了源代码中的那个常量,当然,你不能修改它。

于 2013-03-18T13:09:04.757 回答
2
  1. 不要求它引发分段错误。所发生的一切是您损坏的代码调用了未定义的行为。如果该行为没有明显的影响,那很好。如果它格式化硬盘驱动器并将屏幕涂成蓝色,那也很好。它是未定义的。
  2. 您正在用字符串文字的地址覆盖指针值,这完全不使用分配的内存。然后您尝试反转只读内存中的字符串文字,这会导致分段错误。
于 2013-03-18T13:09:33.303 回答
0
  1. 你的程序没有抛出错误,因为即使你做错了事,也抓住了你(更多下文)。你写了你不应该写的数据,但是你很“幸运”并且没有因为这样做而破坏任何东西。

  2. strcpy(str,"mystring0123456789");将数据复制到指向的位置str。碰巧的是,在那个地方,您可以写入数据而不会引起陷阱(这次)。相反,str="mystring0123456789\0";变化str指向一个新的地方。它指向的地方"mystring0123456789\0"就是存储的地方。那个地方很可能是只读存储器,所以,当你尝试在reverse例程中写入它时,你会遇到一个陷阱。

更多关于1:

calloc分配内存时,它只是安排有一些您可以使用的空间。物理上,还有其他内存存在。您可以写入其他内存,但不应该。这正是现实世界中事情的运作方式:如果你租了一个酒店房间,你可以使用那个酒店房间,但你使用其他房间是错误的,即使它们碰巧是开放的。

有时,当您在现实世界或程序中闯入不应该进入的地方时,没有人会看到,而您会侥幸逃脱。有时你会被抓住。您没有被抓住的事实并不意味着没关系。

关于calloc:您要求它为一件大小为零的事物分配空间(源代码的'\0'计算结果为零)。所以你要求零字节。各种标准(例如 C 和 Open Unix)可能对此有不同的说法,因此当您要求零字节时,可能calloc会给您一个字节。但是,它肯定不会像您使用strcpy.

于 2013-03-18T13:31:41.397 回答
0

听起来您正在编写来自动态语言或至少一种执行自动字符串处理的语言的 C 程序。由于缺乏更正式的定义,我发现 C 是一种非常接近机器架构的语言。也就是说,您做出了很多编程决策。你的很多程序问题是你的代码导致未定义行为的结果。你有一个 strcpy 的段错误,因为你将内存复制到一个受保护的位置;行为未定义。而分配固定字符串“mystring0123456789\0”只是将该指针分配给str。

当您在 C 中实现时,您决定是否要在编译时或运行时定义存储区域,或者决定从堆中分配存储(malloc/calloc)。在任何一种情况下,您都必须编写管理例程以确保不会超出您定义的存储空间。

将字符串分配给指针只是分配字符串在内存中的地址;它不会复制字符串,并且引号“test-string”内的固定字符串是只读的,您无法修改它。您的程序在完成该任务后可能工作得很好,即使它不被认为是良好的 C 编码实践。

以这种方式处理存储分配是有好处的,这就是 C 是一种流行语言的原因。

于 2013-03-18T13:51:06.440 回答
0

另一种情况是,当您正确使用内存并且堆变得如此之大以至于您的物理内存无法管理它时,您可能会遇到段错误(不与 stack|text|data|bss -> link重叠)

证明:链接,可能原因#2部分

于 2014-08-29T15:39:19.707 回答