2017-02-15 32 views
1

嘿,我试图解决这个学校练习..realloc引起的分割错误?

编写一个程序,不断读取字符串并连接它们(将它们添加到单个字符串)。串联应该在函数中发生,如果成功则返回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; 
} 
+0

欢迎堆栈溢出。请花些时间阅读[The Tour](http://stackoverflow.com/tour),并参阅[帮助中心](http://stackoverflow.com/help/asking)中的资料,了解您可以在这里问。 –

+0

解决这些问题的正确工具是您的调试器。在*堆栈溢出问题之前,您应该逐行执行您的代码。如需更多帮助,请阅读[如何调试小程序(由Eric Lippert撰写)](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/)。至少,您应该\编辑您的问题,以包含一个[最小,完整和可验证](http://stackoverflow.com/help/mcve)示例,该示例再现了您的问题,以及您在调试器。 –

+0

你可以用'toupper()'或'tolower()'来减少你的'choice'比较,例如:'toupper(choice)=='Y')'。 –

回答

4

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

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

2)你可能打算重新分配dest,3)你打算分配内存大小为length3(+1 null-terminator)。当*dest是NULL最初

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

4)strlen(*dest)会崩溃。

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

5)我不认为你的for-loop是正确的。它不会连接字符串,您的偏移量计算关闭。

+0

我做了你所说的......但仍然给了我同样的错误 –

+0

通过将realloc()的返回值直接指定给被重新分配的内存指针,可能会失去一个指向数据的指针,内存泄漏。如果'realloc()'失败,则返回一个空指针。您应该将返回值存储在临时指针中,然后检查以确定分配是否成功。 –

1

指针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 
+0

@Stefano Feltre在我的回答中看到示范程序。 –

+0

嗯...不可能使它不使用宏? 此外,为了这个练习,我需要保持函数为: int cat(char ** dest,char * src) 我不能修改这个。 我真的不明白你为什么需要变量* tmp ...我们不能直接使用变量* dest作为realloc吗? Thx –

+0

@StefanoFeltre该宏引入的是名称而不是幻数50.使用命名常量而不是幻数总是更好。如果你的编译器支持可变长度的数组,那么你可以用它来替代一个常量变量。例如const size_t N = 50;你应该用限定符const声明第二个参数。函数和客户端之间使用函数内部的参数不会被更改的函数之间的契约。变量tmp是必需的,因为realloc可以返回NULL。在这种情况下,dest的值将会丢失。 –