为什么我的程序在使用堆分配的内存时会抛出分段错误?

时间:2013-03-18 13:05:25

标签: c memory-management char-pointer

在编写程序来反转字符串后,我无法理解为什么在尝试反转字符串时遇到seg错误。我在下面列出了我的程序。

#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指针分配了1个字节的内存 str(calloc(1,'\0')),我将一个18字节的字符串mystring0123456789复制到其中,它没有抛出任何错误,程序运行正常,没有任何SEGFAULT。

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

该程序运行良好,并为我提供输出Reverse String is: 9876543210gnirtsym

第二个问题:

如果替换声明

strcpy(str,"mystring0123456789");

str="mystring0123456789\0";

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

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

5 个答案:

答案 0 :(得分:5)

  

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

你的代码有一个错误 - 当然它不会做你期望的。修复错误,神秘感将会消失。

  

如果更换声明
  的strcpy(STR “mystring0123456789”);
  与
  STR = “mystring0123456789 \ 0”;
  即使我为str(malloc(100))分配了足够的内存,程序也会给出分段错误。

因为完成此操作后,str指向常量。这会丢弃先前的str值,这是一个指向你分配的内存的指针,并用指向该常量的指针替换它。

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

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

int * h =&amp; 2;

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

这实际上就是你在str="mystring0123456789\0";所做的。它使str指向源代码中的常量,当然,您无法修改。

答案 1 :(得分:2)

  1. 不要求它抛出分段错误。所发生的一切都是你破坏的代码调用未定义的行为。如果这种行为没有明显效果,那很好。如果它格式化硬盘并将屏幕涂成蓝色,那也没关系。这是未定义
  2. 你用字符串文字的地址覆盖指针值,完全不使用分配的内存。然后尝试反转只读内存中的字符串文字,这会导致分段错误。

答案 2 :(得分:0)

  1. 你的程序没有抛出错误,因为,即使你做错了什么,也没有抓到你(更多信息如下)。你写的数据是你不应该的,但你有“幸运”,并且没有做任何事情。

  2. strcpy(str,"mystring0123456789");将数据复制到str分的位置。碰巧的是,在那个地方,你可以在不引起陷阱的情况下编写数据(这次)。相比之下,str="mystring0123456789\0";str更改为指向新位置。它指向的地方是"mystring0123456789\0"存储的地方。那个地方很可能是只读内存,因此,当你试图在reverse程序中写入它时,你会得到一个陷阱。

  3. 更多关于1:

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

    有时当你侵入你不应该去的地方时,在现实世界或程序中,没有人会看到,你就会侥幸逃脱。有时你会被抓住。你不被抓住的事实并不意味着它没事。

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

答案 3 :(得分:0)

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

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

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

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

答案 4 :(得分:0)

另一种情况是当你使用内存正确时你可以有一个段错误你的堆变得太大而你的物理内存无法管理它(没有重叠堆栈| text | data | bss - &gt ; link

证明:link,部分可能的原因#2