【问题标题】:Segmentation Fault caused by realloc?由realloc引起的分段错误?
【发布时间】:2017-02-15 20:35:32
【问题描述】:

嘿,我正在尝试解决这个学校练习..

编写一个程序,不断读取字符串并将它们连接起来(将它们添加到单个字符串中)。连接应该发生在一个函数中,如果成功则返回 1,如果失败则返回 0。对于内存分配,仅使用 realloc!

我在调试程序时没有收到任何错误,但是当我尝试运行程序时,插入字符串后唯一出现的是“Segmentation Fault”,这是什么?这是代码:

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

int cat(char **, char *);

int main(void)
{
  char string[51];
  char *output=NULL;
  char choice;
  do
  {
    printf("Please enter a string [<50 chars]: ");
    fgets(string,50,stdin);
    if(string[strlen(string)-1]=='\n') /* if newline was read as well */
      string[strlen(string)-1]=0;      /* discard it */
    if(cat(&output,string))
      printf("\n\nThe string now contains:\n%s\n",output);
    else
    {
      printf("error: memory (re-)allocation failed!\n\n");
      return 1; /* exit with error */ 
    }
    printf("Continue? (y/n) - ");
    fgets(string,3,stdin); /* read input from keyboard - leave a safety buffer to account for read newline */
    choice=string[0]; /* use the first character from the previous read as the choice */
  } while(choice=='y' || choice=='Y');

  free(output);
  return 0;
}

int cat(char **dest, char *src)
{

  int i;
  int length1=strlen(src);
  int length2=strlen(*dest);
  int length3=length1+length2;
  *dest=(char*)realloc(NULL,sizeof(*src));
  printf("%p", *dest);
  if(*dest==NULL) return 0; /* if allocation failed */
  for(i=0;i<=length3;i++)
  {
      if(i<=length1)
        (*dest)[i]=(*dest)[i];
      else
        (*dest)[i]=(src)[i];
  }
  free(src);
  return 1;
}

【问题讨论】:

  • 欢迎来到 Stack Overflow。请花时间阅读The Tour 并参考Help Center 中的材料,您可以在这里问什么以及如何问。
  • 调试器是解决此类问题的正确工具。 询问 Stack Overflow 之前,您应该逐行浏览您的代码。如需更多帮助,请阅读How to debug small programs (by Eric Lippert)。至少,您应该 [编辑] 您的问题以包含一个重现您的问题的 Minimal, Complete, and Verifiable 示例,以及您在调试器中所做的观察。
  • 您可以使用toupper()tolower() 来减少choice 的比较,例如:toupper(choice) == 'Y')
  • free(src) - 您尝试在此处释放数组 string。另外,*dest=(char*)realloc(NULL,sizeof(*src)); - 这会为 1 个char 分配空间。
  • 您费尽心思寻找新的长度(但忘记了终止符),然后用*dest=(char*)realloc(NULL,sizeof(*src)); 将分配1 字节 放在了你的剑上。此外,传递指针变量本身,如果您尚未分配任何内存,则可能带有值 NULL

标签: c realloc dma


【解决方案1】:

您的代码至少有 5 个问题:

1) 你应该free 只在堆上分配你自己。不要free(src),因为您传入的src 指向堆栈内存(char string[51]; 会自动释放)。

2) 您可能打算重新分配 dest,并且 3) 您打算分配大小为 length3(+1 空终止符)的内存。

    *dest=(char*)realloc(*dest, length3 + 1);

4) 当*dest 最初为 NULL 时,strlen(*dest) 将崩溃。

    int length2=(*dest)?strlen(*dest):0;

5) 我认为您的 for 循环不正确。它不会连接字符串,您的偏移量计算已关闭。

【讨论】:

  • 我按照你说的做了...但它仍然给了我同样的错误
  • 通过将realloc() 的返回值直接分配给指向正在重新分配的内存的指针,您可能会丢失指向数据的指针并获得内存泄漏。如果realloc() 失败,则返回一个空指针。您应该将返回值存储在一个临时指针中,然后检查以确保分配成功。
【解决方案2】:

指针output的初始值为NULL。但是在函数内部没有检查指针是否等于 NULL。因此,将函数 strlen 应用于指针会导致未定义的行为。

您还需要为终止零多保留一个字符。

函数中没有正确重新分配内存。而且sizeof( *src )等于一个字节。

此声明

  if(i<=length1)
    (*dest)[i]=(*dest)[i];

意义不大。重新分配的内存只要正确重新分配就已经包含原始字符串。

您不应释放指针src,因为它不指向动态分配的内存。

该函数在演示程序中可以如下所示。

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

int cat( char **dest, const char *src )
{
    size_t n = strlen( src ) + ( *dest == NULL ? 0 : strlen( *dest ) );

    char *tmp = realloc( *dest, n + 1 );
    int success = tmp != NULL;

    if ( success )
    {
        if ( *dest == NULL ) *tmp = '\0';
        *dest = tmp;

        while ( *tmp ) ++tmp;

        while ( ( *tmp++ = *src++ ) );
    }       

    return success;
}   

#define N   50

int main(void) 
{
    char *output = NULL;
    char choice = 'n';

    do
    {
        char string[N];


        printf( "Please enter a string [<%d chars]: ", N );
        fgets( string, sizeof( string ),stdin );

        string[strcspn( string, "\n" )] = '\0';

        if ( cat( &output, string ) )
        {
            printf( "\nThe string now contains:\n\"%s\"\n\n", output );
        }           
        else
        {
            printf( "error: memory (re-)allocation failed!\n\n" );
            return 1; /* exit with error */ 
        }

        printf( "Continue? (y/n) - " );
        fgets( string, 3, stdin ); /* read input from keyboard - leave a safety buffer to account for read newline */
        choice = string[0]; /* use the first character from the previous read as the choice */
    } while ( choice == 'y' || choice == 'Y' );

    free( output );

    return 0;
}

它的输出可能看起来像

Please enter a string [<50 chars]: Hi Stefano Feltre

The string now contains:
"Hi Stefano Feltre"

Continue? (y/n) - y
Please enter a string [<50 chars]: 

The string now contains:
"Hi Stefano Feltre "

Continue? (y/n) - y
Please enter a string [<50 chars]: Let's learn C

The string now contains:
"Hi Stefano Feltre Let's learn C"

Continue? (y/n) - n

【讨论】:

  • @Stefano Feltre 请参阅我的回答中的演示程序。
  • 嗯...不使用宏就无法实现吗?此外,对于这个练习,我需要将函数保留为: int cat(char **dest, char *src) 我无法修改它。而且我真的不明白为什么你需要变量 *tmp...我们不能直接使用变量 *dest 来进行 realloc 吗?谢谢
  • @StefanoFeltre 宏引入了名称而不是幻数 50。使用命名常量而不是幻数总是更好。如果您的编译器支持可变长度数组,那么您可以将其替换为常量变量。例如 const size_t N = 50;您应使用限定符 const 声明第二个参数。函数与使用该函数的客户端之间的约定是在函数内不会更改参数。需要变量 tmp 因为 realloc 可以返回 NULL。在这种情况下,dest 的值将会丢失。
  • @StefanoFeltre 另请参阅 David Bowling 对上一篇文章的评论。
猜你喜欢
  • 1970-01-01
  • 2018-07-13
  • 1970-01-01
  • 2014-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-05
相关资源
最近更新 更多