(char *)malloc(sizeof(char))导致分段错误,如何?

时间:2014-02-22 20:05:23

标签: c segmentation-fault malloc

代码在大多数输入上都能正常工作,但是对于userID来说,很长时间我会遇到分段错误。我的问题是,malloc如何导致分段错误?简单地分配内存不应该导致这种情况。我通过使用printf()语句找到了问题区域,似乎我的read_line()函数中的malloc是问题所在,因为第二个“read_line”没有打印,而是在malloc之前的第一个。

谢谢你。 - 克里斯

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#define DELIM " " /* the delimiter */
#define MAX_CHANGE (10.0/86400.0)       /* 10kg/day */
/* seconds in a day is 24 hours * 60 minutes * 60 seconds */
#define MEM_OUT printf("%s","out of memory");

/* Defines Node structure. */
struct Node{
    char *id;
    float weight;
    int time;
    struct Node *next;
} *head, *p, *t, *last;

/* Constructor which returns a pointer to a new node. */
struct Node * new_node(int time, char *id, float w)
{   /*note malloc returns a pointer */
    struct Node *node = (struct Node *)malloc(sizeof(struct Node));
    node->time = time;
    node->id = (char *)malloc( (strlen(id) + 1) * sizeof(char));
    strcpy(node->id, id); //duplicate the id, so new node has own copy.
    node->weight = w;
    node->next = NULL;
return node;    
}

/* reads in line of characters until either a EOF or '\n' is encountered
    then places a the terminator '\0' at the end */
char * read_line(FILE *stream)
{
    printf("read_line");
char * temp = (char*)malloc(sizeof(char));
    printf("read_line");
char * line = (char*)malloc(sizeof(char));
    char c;
    *line = '\0';
    int i = 1; 
    //strchr()

    while( (c = getc(stream)) != EOF && c != '\n')
    {
        //if(c == EOF) return NULL;
        //realloc(line,++i);
        strcpy(temp,line);
        line = malloc(++i * sizeof(char));
        strcpy(line,temp);
        temp = malloc(i * sizeof(char));
        *(line + (i-1)) = '\0'; 
        *(line + (i-2)) = c; 
    }
free(temp);
if( i == 1) return NULL;
return line;
}

main() {

    int lasttime = 0, timestamp, duration, tokens;
    char * userID = NULL;
    char * lastuserID = NULL;
    char * line = NULL;
    float weight,lastweight,change,changePerTime;
    head = new_node(0,"",0.0);
    last = head;

    FILE *fp = fopen("C:\\Users\\chris\\Desktop\\School\\York\\cse\\2031 Software Tools\\Labs\\6\\input.txt","r");

    while( (line = read_line(fp)) != '\0') {
        printf("%s\n",line);
        //free(userID);
        line = strtok(line, " \n");
        if (line == NULL || sscanf(line,"%d",&timestamp) < 1 || timestamp == 0){
            printf("%s\n","Invalid time");
            continue;
            }
        line = strtok(NULL, " \n");
        if(line == NULL || isdigit(line[0]) || line[0] == '.') {
            printf("Illegal userID");
            //free(line);
            continue;
            }
        userID = (char * )malloc( (strlen(line)+1) * sizeof(char));
        strcpy(userID,line);
        strcat(userID," ");
        do{
            line = strtok(NULL," \n");      
        if(line != NULL && !isdigit(line[0]) && line[0] != '.'){    
            strcat(userID,line ); // adds ' ' and '\0'
            strcat(userID," "); 
            }
        }while(line != NULL && line[0] != '.' && !isdigit(line[0]) );
        userID[strlen(userID)-1] = '\0'; //erases the tailing space.
        if(strlen(userID) > 179){
            printf("Illegal userID\n");
            printf("mid"); 
            continue;
            printf("%s\n","after" );
            } 
        if(line != NULL)
            tokens = sscanf(line,"%f", &weight);
        if(line == NULL || tokens < 1 || weight < 30.0 || weight > 300.0)
            {printf("Illegal weight\n"); continue; }
        if (lasttime >= timestamp){
            printf("Nonmonotonic timestamps\n"); 
            continue;
            }
            lasttime = timestamp;

            // record is valid apst this point.
            /* t = last occurence of this userID, p = last userID*/
            for(p = head, t = NULL; p != NULL; p = p->next)
            {   
                if(strcmp(userID,p->id) == 0)
                    t=p;    
                last = p; // set last to last p.
            }
            if(t == NULL)
                printf("OK newuser\n");         
            else if(t != NULL)
            {
                duration = timestamp - t->time;
                change = weight - t->weight;
                changePerTime = change / duration;
                if(changePerTime < -MAX_CHANGE || changePerTime > MAX_CHANGE)
                    printf("Suspiciously large weight change\n");
                else
                    printf("OK\n");
            }
            /* add new node to end of list */
            last->next = new_node(timestamp,userID,weight);
            /* update lastnode */
            last = last->next;      
        free(line); 
        }
       fclose(fp);

/* count sum of id's for last valid user*/
    int count=0;
    for(p = head->next; p !=NULL; p=p->next)
    {
        if(strcmp(last->id,p->id) == 0)
            count++;
    }
//fclose(f);  // use if input from file is uncommented
// adds 1 to both demensions to hole axis
int tHeight = 11;
int tWidth = count + 1;
int qHeight = 10;
int qWidth= count;


/* builds the barchart */
    char bc[tHeight][tWidth];  // + 1 for y-axis

    /* draws axis and orgin */
    int a,b;
    for(a=0; a<tHeight; a++)
    {
        for(b=0;b<tWidth; b++)
        {
            if(a == qHeight && b == 0)
                bc[a][b] = '+';
            else if(a < tHeight && b == 0)
                bc[a][b] = '|';
            else if(a == qHeight && b > 0)
                bc[a][b] = '-';
        }
    }       

    /* prints the bars */
    int j=1, i, k, bh;
    for(p = head; p != NULL, j < tWidth; p=p->next)
    {               
        if(strcmp(last->id,p->id) == 0)
        {
            for(i = 9, k=0, bh = (int)(p->weight / 30);i >= 0; i--)
            {
                if(k < bh) 
                {
                    bc[i][j] = '*'; 
                    k++; // barheight
                }
                else 
                    bc[i][j] = ' ';
            }
        j++;
        }                                           
    }
/* prints the barchart */
    int m, n;
    for(m=0; m < tHeight; m++)
    {           
        for(n=0; n < tWidth; n++)
        {           
            printf("%c",bc[m][n]);
        }
        printf("%c",'\n');
    }
}

2 个答案:

答案 0 :(得分:1)

malloc次呼叫未导致分段错误。但是你以后可以使用它们。

一些注意事项

  • 您的printf("read line")语句在调用时不会立即打印出来,因为输出是缓冲的。如果您希望立即打印,请执行printf("read line\n")。然后,您将看到执行和使用您分配的微小缓冲区的代码都会导致崩溃。

  • while循环中,您正在执行更多malloc次调用,并将返回值分配给变量,例如templine,而不释放先前的内存templine持有的指针,从而导致一些内存泄漏。您评论的realloc是更好的思考过程:line = realloc(line, ++i * sizeof(*line));。同样适用于temp

<小时/> 内存分配问题

这里有一个非常棘手的问题:

    userID = (char * )malloc( (strlen(line)+1) * sizeof(char));
    strcpy(userID,line);
    strcat(userID," ");

userID可以保持linestrlen(line))中字符串的长度加上一个字节。但是null终结符需要多一个字节。您的strcat(userID, " ")将超出分配缓冲区的长度userID一个字节。

答案 1 :(得分:0)

我解决了这个问题!问题根本不是read_line函数,而是userID字符串的内存分配。将用户的malloc()移动到循环的开头可以解决问题。

为该行的userID部分分配的内存量基于整行的长度。例如:malloc(strlen(line)+2)。然而,这是在线上调用strtok()几次之后完成的,这将分配比整行长度短的内存块。这是因为strtok()在行中指定分隔符的每个实例处放置了空终止符'\ 0',而strlen()只计算从传递的字符指针到它遇到的第一个'\ 0'的长度。

无论如何,谢谢你的帮助!

-Chris

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#define DELIM " " /* the delimiter */
#define MAX_CHANGE (10.0/86400.0)       /* 10kg/day */
/* seconds in a day is 24 hours * 60 minutes * 60 seconds */

/* Defines Node structure. */
struct Node{
    char *id;
    float weight;
    int time;
    struct Node *next;
} *head, *p, *t, *last;

/* Constructor which returns a pointer to a new node. */
struct Node * new_node(int time, char *id, float w)
{   /*note malloc returns a pointer */
    struct Node *node = (struct Node *)malloc(sizeof(struct Node));
    node->time = time;
    node->id = malloc(strlen(id) + 1);
    strcpy(node->id, id); //duplicate the id, so new node has own copy.
    node->weight = w;
    node->next = NULL;
return node;    
}

/* reads in line of characters until either a EOF or '\n' is encountered
    then places a the terminator '\0' at the end */
char * read_line(FILE *in)
{
    int i = 1;
    char * s = NULL;
    char c;
    do{
    s = realloc(s,i);  //strlen does not work on NULL strings
    if(s == NULL || s == "")
    {
        printf("%s\n","out of memory");
        exit(1);
    }
    *(s + (i-1)) = '\0'; // ensures null terminated
    if(i > 1) 
    *(s + (i-2)) = c;
    i++;
    }
    while( (c = getc(in)) != EOF && c != '\n' );
    if (c == '\n')
        return s;
    else if(c == EOF)
        return NULL;
}

main() {

    int lasttime = 0, timestamp, duration, tokens;
    char * userID = NULL;
    char * lastuserID = NULL;
    char * line = NULL;
    float weight,lastweight,change,changePerTime;
    head = new_node(0,"",0.0);
    last = head;

    FILE *fp = fopen("C:\\Users\\chris\\Desktop\\School\\York\\cse\\2031 Software Tools\\Labs\\6\\tests\\04.in","r");

    while((line = read_line(fp)) != NULL) {
        userID = malloc(strlen(line)+2); // max userID length is line length** 
        line = strtok(line, " \n");
        if (line == NULL || sscanf(line,"%d",&timestamp) < 1 || timestamp == 0){
            printf("%s\n","Invalid time");
            continue;
            }
        line = strtok(NULL, " \n");
        if(line == NULL || isdigit(line[0]) || line[0] == '.') {
            printf("%s\n","Illegal userID");
            //free(line);
            continue;
            }
        strcpy(userID,line);
        strcat(userID," ");
        do{
            line = strtok(NULL," \n");      
        if(line != NULL && !isdigit(line[0]) && line[0] != '.'){    
            strcat(userID,line ); // adds ' ' and '\0'
            strcat(userID," "); 
            }
        }while(line != NULL && line[0] != '.' && !isdigit(line[0]) );
        userID[strlen(userID)-1] = '\0'; //erases the tailing space.
        if(strlen(userID) > 179){
            printf("Illegal userID\n"); 
            free(userID);
            free(line);
            continue;
            } 
        if(line != NULL)
            tokens = sscanf(line,"%f", &weight);
        if(line == NULL || tokens < 1 || weight < 30.0 || weight > 300.0)
            {printf("Illegal weight\n"); continue; }
        if (lasttime >= timestamp){
            printf("Nonmonotonic timestamps\n"); 
            continue;
            }
            lasttime = timestamp;

            // record is valid apst this point.
            /* t = last occurence of this userID, p = last userID*/
            for(p = head, t = NULL; p != NULL; p = p->next)
            {   
                if(strcmp(userID,p->id) == 0)
                    t=p;    
                last = p; // set last to last p.
            }
            if(t == NULL)
                printf("OK newuser\n");         
            else if(t != NULL)
            {
                duration = timestamp - t->time;
                change = weight - t->weight;
                changePerTime = change / duration;
                if(changePerTime < -MAX_CHANGE || changePerTime > MAX_CHANGE)
                    printf("Suspiciously large weight change\n");
                else
                    printf("OK\n");
            }
            /* add new node to end of list */
            last->next = new_node(timestamp,userID,weight);
            /* update lastnode */
            last = last->next;  
            free(line); 

        } // end of input loop
       fclose(fp);

/* count sum of id's for last valid user*/
    int count=0;
    for(p = head->next; p !=NULL; p=p->next)
    {
        if(strcmp(last->id,p->id) == 0)
            count++;
    }
//fclose(f);  // use if input from file is uncommented
// adds 1 to both demensions to hole axis
int tHeight = 11;
int tWidth = count + 1;
int qHeight = 10;
int qWidth= count;


/* builds the barchart */
    char bc[tHeight][tWidth];  // + 1 for y-axis

    /* draws axis and orgin */
    int a,b;
    for(a=0; a<tHeight; a++)
    {
        for(b=0;b<tWidth; b++)
        {
            if(a == qHeight && b == 0)
                bc[a][b] = '+';
            else if(a < tHeight && b == 0)
                bc[a][b] = '|';
            else if(a == qHeight && b > 0)
                bc[a][b] = '-';
        }
    }       

    /* prints the bars */
    int j=1, i, k, bh;
    for(p = head; p != NULL, j < tWidth; p=p->next)
    {               
        if(strcmp(last->id,p->id) == 0)
        {
            for(i = 9, k=0, bh = (int)(p->weight / 30);i >= 0; i--)
            {
                if(k < bh) 
                {
                    bc[i][j] = '*'; 
                    k++; // barheight
                }
                else 
                    bc[i][j] = ' ';
            }
        j++;
        }                                           
    }
/* prints the barchart */
    int m, n;
    for(m=0; m < tHeight; m++)
    {           
        for(n=0; n < tWidth; n++)
        {           
            printf("%c",bc[m][n]);
        }
        printf("%c",'\n');
    }
}