C语言 如果在故障之前或之后没有任何错误,如何调试分段故障?

gmxoilav  于 2023-02-03  发布在  其他
关注(0)|答案(2)|浏览(194)

我遇到的特殊问题是,在我的主函数中,我在调用“bad”函数之前和之后添加了一个print语句。它总是显示before语句,但从不显示after语句。我还在“bad”函数的末尾添加了一个print语句,我可以看到它正确运行到“bad”函数的最后一行,所以它应该正常返回。在函数最后一次打印之后和主函数打印之前,我得到了segfault。有什么想法吗?下面是代码:

int main(int argc, char* argv[])
{
    char myItem[100];
    int i = 0;
    while (i < 100) {

        scanf("%[^\n]", myItem);
        i++;
        if (myItem == EOF) {
            break;
        }
        int c;
        while ((c = getchar()) != '\n' && c != EOF);
        //printf("string read in from user typing: %s\n", myItem);
        printf("i = %d\n", i);
        emailFilter(myItem);
        printf("done with email filter in main\n");
        //printf("item from this pass is:%s\n\n", myItem);
    }
    return 0;
}

和“坏”功能:

void emailFilter(char* mySubject)
{

    printf(" Just entered the emailFilter() .\n");

    char * event_holder[5]; //holds five separate char ptrs
    for (int i = 0; i < 5; i++)
    {
        event_holder[i] = ((char*)malloc(100 * sizeof(char*)));
    }

    char command_type = parseSubject(mySubject, event_holder); //parses subject line and fills event_holder. returns command type, from parsing
    //call proper parsing result

    if (command_type == 'C')
    {
         create(event_holder);
    }
    else if (command_type == 'X')
    {
        change(event_holder);
    }
    else if (command_type == 'D')
    {
        delete(event_holder);
    }

    printf("Leaving emailfilter()...\n");

}

运行这段代码可以为我提供:

$: 
i = 1
 Just entered the emailFilter() .
C, Meeting   ,01/12/2019,15:30,NEB202
Leaving emailfilter()...
done with email filter in main
i = 2
 Just entered the emailFilter() .
Leaving emailfilter()...
Segmentation fault

这表明我总是通过函数,但仍然不能正确返回。
下面是我的整个代码来重现错误。

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

struct node {
   char * event_data[5];
   struct node * next;
};

struct node *head = NULL;
struct node *current = NULL;
char* earliest = NULL;


char* substring (char* orig, char* dest, int offset, int len)
{
    int input_len = strlen (orig);

    if (offset + len > input_len)
    {
        return NULL;
    }

    strncpy (dest, orig + offset, len);
    //add null char \0 to end
    char * term = "\0";
    strncpy (dest + len, term, 1);

    return dest;
}
char * firstItem(char* shortenedSubject)
{
    int i = 0;
    int currentLength = 0;
    int currentCharIndex = 0;
    int eventIndex = 0;

    char * toReturn = (char*)malloc(100);

    while ((shortenedSubject[currentLength] != '\0') && (shortenedSubject[currentLength] != ',') )//50 is my safety num to make sure it exits eventually
    {
        currentLength++;
    }
    if (shortenedSubject[currentLength] == ',') {
        substring(shortenedSubject, toReturn, 0, currentLength);
    }
    return toReturn;
}
char parseSubject(char* subject,char * eventDataToReturn[5]) //returns "what type of command called, or none"
{
    char toReturn;
    char * shortenedSubject  = (char*)malloc(100);
    substring(subject,shortenedSubject,9,strlen(subject)-9);//put substring into tempString

    int currentCharIndex = 0;// well feed into index of substring()
    int eventIndex = 0; //lets us know which event to fill in
    int currentLength = 0;//lets us know length of current event

    int i = 0; //which char in temp string were alooking at
    char * action = firstItem(shortenedSubject);

    if (strlen(action) == 1)
    {
        if ( action[0] == 'C')
        {
            toReturn = 'C';
        }
        else if (action[0] == 'X')
        {
            toReturn = 'X';
        }
        else if (action[0] == 'D')
        {
            toReturn = 'D';
        }
        else
        {
            toReturn = 'N'; //not valid
            //invalid email command, do nothing
        }
    }
    else
    {
        toReturn = 'N'; //not valid
        //invalid email command, do nothing
    }

    char* debug2;
    while ((shortenedSubject[i] != '\0') && (i <= 50) )//50 is my safety num to make sure it exits eventually
    {
        char debugvar = shortenedSubject[i];
        currentLength++;
        if (shortenedSubject[i] == ',')
        {
            //eventDataToReturn[i] =  substring2(shortenedSubject,currentCharIndex,currentLength);
            substring(shortenedSubject,eventDataToReturn[eventIndex],currentCharIndex,currentLength-1);
            debug2 = eventDataToReturn[eventIndex];
            currentCharIndex= i +1;
            eventIndex++;
            currentLength = 0;
            //i++;
        }
        i++;
    }
    substring(shortenedSubject,eventDataToReturn[4],currentCharIndex,currentLength);
    return toReturn;
} 
void printEventData(char* my_event_data[])
{
    //printf("\nPrinting event data...\n");
    for (int i = 1; i < 4; i++)
    {
        printf("%s,",my_event_data[i]);
    }
    //print last entry, no comma
    printf("%s",my_event_data[4]);
   
}

void printEventsInorder()
{
    struct node * ptr = head;
    while (ptr != NULL)//if not empty, check each one and add when ready
    {
        printEventData(ptr->event_data);
        printf("\n");
        ptr = ptr->next;
    }

}
void insertFront(char* my_event_data[5])
{
   struct node *link = (struct node*) malloc(sizeof(struct node));
   link->next = NULL;
   for (int i = 0; i < 5; i++)
   {
       link->event_data[i] = my_event_data[i];
   }
   head = link;
}
int isEarlier(char* event_data_L[5], char* event_data_R[5])
{// will be given like    12:30      12:45,turn timeL into timeL1 and timeL2, and time R1 and timeR2  

    //compare dates for earlier
    int month_L,day_L,year_L;
    int month_R,day_R,year_R;
    char* char_holder;

    substring(event_data_L[2],char_holder,0,2);//extract first half of time
    month_L = atoi(char_holder); //convert first half of time to int
    substring(event_data_L[2],char_holder,3,2);//extract first half of time
    day_L = atoi(char_holder); //convert first half of time to int
    substring(event_data_L[2],char_holder,6,4);//extract first half of time
    year_L = atoi(char_holder); //convert first half of time to int

    substring(event_data_R[2],char_holder,0,2);//extract first half of time
    month_R = atoi(char_holder); //convert first half of time to int
    substring(event_data_R[2],char_holder,3,2);//extract first half of time
    day_R = atoi(char_holder); //convert first half of time to int
    substring(event_data_R[2],char_holder,6,4);//extract first half of time
    year_R = atoi(char_holder); //convert first half of time to int

    int time_L1,time_L2,time_R1,time_R2;

    substring(event_data_L[3],char_holder,0,2);//extract first half of time
    time_L1 = atoi(char_holder); //convert first half of time to int
    substring(event_data_L[3],char_holder,3,2);//extract second half of time
    time_L2 = atoi(char_holder); //convert second half of time to int

    substring(event_data_R[3],char_holder,0,2);
    time_R1 = atoi(char_holder);
    substring(event_data_R[3],char_holder,3,2);
    time_R2 = atoi(char_holder);
    
   //convert to 2 ints, first compare left ints, then right ints


   if(year_L < year_R)
   {
       return 1;
   }
   else if ( year_L == year_R)
   {
        if (month_L < month_L)
        {
            return 1;
        }
        else if (month_L == month_L)
        {
            if (day_L < day_R)
            {
                return 1;
            }
            else if (day_L == day_R)
            {
                if (time_L1 < time_R1)
                {
                    return 1;
                }
                else if (time_L1 == time_R1)
                {
                    if (time_L2 < time_R2)
                    {
                        return 1;
                    }
                    else if (time_L2 == time_R2)
                    {
                        return 2;
                    }
                    else//else, time is greater
                    {
                        return 3;
                    }
                }
                else //left time is greater, return 3
                {
                    return 3;
                }
            }
            else
            {
                return 3;
            }
        }
        else
        {
            return 3;
        }
   }
   else //its left is greater than right so return 3 to show that
   {
       return 3;
   }


}
void create(char* my_event_data[5]) {
    //print required sentence
   char * debugvar2 = my_event_data[3];

    if (head == NULL)//if empty calendar, just add it
    {
        insertFront(my_event_data);
        //printf("EARLIEST bc empty list, \n");
        printf("C, ");
        printEventData(my_event_data);
        printf("\n");
        return;
    }
    else
    {
        struct node *link = (struct node*) malloc(sizeof(struct node));
        link->next = NULL;
        for (int i = 1; i < 5; i++)
        {
            link->event_data[i] = my_event_data[i];
        }

        struct node *ptr = head;
        struct node *prev = NULL;

        if (ptr->next == NULL) //if this is the last node to check against
        {
            if (isEarlier(my_event_data, ptr->event_data) == 1)
            { //check against it
                printf("C, ");
                printEventData(my_event_data);
                printf("\n");

                if (prev != NULL) //if this is first item in linked list...
                {
                    link->next = head; //assign something before head
                    head = link; //move head to that thing
                }
                if (prev != NULL)
                {
                    prev->next = link;
                }

                link->next = ptr;
                return;
            }
            else //else is equal to or later, so tack it on after:
            {
                ptr->next = link;
            }
        }
        else
        {
            while (ptr->next != NULL)//if not empty, check each one and add when ready
            {
                //if next node is later than current, we are done with insertion
                if (isEarlier(my_event_data,ptr->event_data) == 1)
                {
                    if (head == ptr) //if earlier than head... insert and print
                    {
                        //printf("earlier than head!");
                        printf("C, ");
                        printEventData(my_event_data);
                        printf("\n");
                        link->next = ptr;
                        head = link;
                    }
                    else //if earlier than non head, insert, but dont print
                    {
                        if (prev != NULL)
                        {
                            prev->next = link;
                        }
                        link->next = ptr;

                    }
                    return;
                }
                else
                {
                    prev = ptr;
                    ptr = ptr->next;
                }
            }
            if (isEarlier(my_event_data,ptr->event_data) == 1) //while ptr-> is null now
            {
                printf("C, ");
                printEventData(my_event_data);
                printf("\n");
                if (prev != NULL)
                {
                    prev->next = link;
                }
                link->next = ptr->next;
                return;
            }
            else
            {
                prev = link;
                link = ptr;
            }
        }
        return;
    }



    //if it gets here, it is the latest meeting, tack it on the end
    //prev->ptr = link;

}
void change(char* my_event_data[5]) {
   //create a link
   struct node *ptr = head;

    while (ptr->next != NULL)//if not empty, check each one and add when ready
    {
        //if next node is later than current, we are done with insertion
        if (*ptr->event_data[1] == *my_event_data[1])
        {
            for (int i = 1; i < 5; i++)
            {
                ptr->event_data[i] = my_event_data[i];
            }
                printf("X, ");
                printEventData(my_event_data);
                printf("\n");
            return;
        }
        ptr = ptr->next;
    }

    if (*ptr->event_data[1] == *my_event_data[1]) //check final node
    {
        for (int i = 0; i < 5; i++)
        {
            ptr->event_data[i] = my_event_data[i];
        }
        printf("X, ");
        printEventData(my_event_data);
        printf("\n");
        return;
    }
    printf("event to change not found");
    return;
    //if it gets here, nothing matched the title to change
}
void delete(char* my_event_data[5])
{
    struct node *ptr = head;
    struct node *prev = NULL;

    while (ptr != NULL)//if not empty, check each one and add when ready
    {
        //if next node is later than current, we are done with insertion
        if ( strcmp( ptr->event_data[1], my_event_data[1] ) == 0) // if title matches, delete it
        {
            if (prev != NULL)
            {
                prev->next = ptr->next;
            }
           if (ptr == head)
           {
               head = ptr->next;
           }
            free(ptr);
            printf("D, ");
            printEventData(my_event_data);
            printf("\n"); 
            return;
        }
        prev = ptr;
        ptr = ptr->next;
    }
}
void emailFilter(char* mySubject)
{

    if (strlen(mySubject) < 9)
    {
        return;
    }

    char * event_holder[5]; //holds five separate char ptrs
    for (int i = 0; i < 5; i++)
    {
        event_holder[i] = ((char*)malloc(100 * sizeof(char*)));
    }
    char command_type = parseSubject(mySubject, event_holder); //parses subject line and fills event_holder. returns command type, from parsing
    //call proper parsing result
    if (command_type == 'C')
    {
         create(event_holder);
    }
    else if (command_type == 'X')
    {
        change(event_holder);
    }
    else if (command_type == 'D')
    {
        delete(event_holder);
    }

}

int main(int argc, char* argv[])
{
    char myItem[100];
    int i = 0;
    while (i < 100)
    {

        scanf("%[^\n]", myItem);
        i++;
        if ( myItem == EOF )
        {
            break;
        }
        int c;
        while ((c = getchar()) != '\n' && c != EOF);
        printf("i = %d\n", i);
        emailFilter(myItem);
    }
    return 0;
}

另外请注意,当我通过命令行上的“〉”符号使用txt文件作为STDIN时,会发生此错误用途:

Subject: C,Meeting   ,01/12/2019,15:30,NEB202
Subject: C,Meeting   ,01/12/2019,16:30,NEB202
Subject: C,Meeting   ,01/12/2019,11:30,NEB202
8mmmxcuj

8mmmxcuj1#

我试着找点东西来做贡献,这是:
这段代码处理的是日期/时间,下面是一个"目标缓冲区"的声明和使用,字符串的片段被复制到这个缓冲区中:

int isEarlier(char* event_data_L[5], char* event_data_R[5])
{// will be given like    12:30      12:45 // ....
    //compare dates for earlier
    int month_L,day_L,year_L;
    int month_R,day_R,year_R;
    char* char_holder;

    substring(event_data_L[2],char_holder,0,2);//extract first half of time
    month_L = atoi(char_holder); //convert first half of time to int
    //...

注意char_holder并没有指向任何特定的地方。UB ...
虽然它代表了初学者的方法,但实际上看到这样的代码是痛苦的。下面是isEarlier()的一个更简洁的版本(未经测试)。

int isEarlier( char *ed_L[5], char *ed_R[5] ) {
    char l[16], r[16];

    memcpy( l +  0, ed_L[2][6],4 ); // YYYY
    memcpy( l +  4, ed_L[2][0],2 ); // MM
    memcpy( l +  6, ed_L[2][3],2 ); // DD
    memcpy( l +  8, ed_L[3][0],2 ); // hh
    memcpy( l + 10, ed_L[3][3],2 ); // mm

    memcpy( r +  0, ed_R[2][6],4 ); // YYYY
    memcpy( r +  4, ed_R[2][0],2 ); // MM
    memcpy( r +  6, ed_R[2][3],2 ); // DD
    memcpy( r +  8, ed_R[3][0],2 ); // hh
    memcpy( r + 10, ed_R[3][3],2 ); // mm

    int res = memcmp( l, r, 12 );
    return res < 0 ? 1 : res == 0 ? 2 : 3;
}

注:所提供的示例数据表示月和日均为2位数,并且"mm/dd"或"dd/mm"格式不明确。此处使用的偏移值来自OP代码。
减少代码中错误的一种方法是,尽可能少写一些但功能更强的代码,并对你写的代码进行"单元测试"。一次只关注一个函数,不要使用全局变量。另一种方法是尽可能熟悉标准库中 * 已证明 * 的函数功能。

    • 编辑:**

看到这个答案,我突然想到这个函数本身应该被重构:

void reformatDateTime( char *d, char *s[5] ) {
    memcpy( d +  0, s[2][6],4 ); // YYYY
    memcpy( d +  4, s[2][0],2 ); // MM
    memcpy( d +  6, s[2][3],2 ); // DD
    memcpy( d +  8, s[3][0],2 ); // hh
    memcpy( d + 10, s[3][3],2 ); // mm
}

int isEarlier( char *ed_L[5], char *ed_R[5] ) {
    char l[16], r[16];

    reformatDateTime( l, ed_L );
    reformatDateTime( r, ed_R );

    int res = memcmp( l, r, 12 );
    res = res < 0 ? 1 : res == 0 ? 2 : 3;
    printf( "isEarlier() '%.12s' vs '%.12s' result %d\n", l, r, res ); // debug
    return res;
}
sc4hvdpw

sc4hvdpw2#

我可以看到它运行正常
你自相矛盾,你说它有时或总是seg错误。有些C代码在离开函数时崩溃是相当不可能的,因为没有“RAII”,在这种情况下也没有多线程。然而,堆栈损坏可能会破坏函数的返回地址。
最好的调试方法不是关注症状,而是试图找出错误所在。您已经做了很多工作,所以这是大部分调试工作已经完成。
从那里调试的一种方法是步骤#1:盯着函数看一分钟。大约5秒钟后:event_holder[i] = ((char*)malloc(100 * sizeof(char*)));这是一个明显的bug。大约30秒后:等等,谁清理这个内存?也许是delete函数,但为什么它然后有条件地执行?(结果发现delete并没有释放()内存。)该函数会泄漏内存,又一个bug。一分钟后我们意识到parseSubject做了很多事情,如果我们想清除每一个可能的bug,我们需要详细地挖掘这个bug。这将需要更多的时间来找出真相。但我们已经发现了2个明显的错误,只是扫一眼代码。
修正错误,再试一次,问题解决了吗?
再看一下main()中的一个bug,myItem == EOF是没有意义的,不应该编译。这表明你编译时使用了太宽松的警告级别,或者忽略了警告,这两种情况都是非常糟糕的。What compiler options are recommended for beginners learning C?
我们可能会注意到,大量使用“幻数”会使代码难以阅读。这通常也是脆弱代码的一个确定标志。这些59等来自哪里?使用命名常量。我们还将很快注意到,在一些只应该解析而不是更改数据的东西中,缺少常量正确性。等等。
我没有详细阅读代码,但总体上缺乏以下最佳实践和3个错误发现只是简单的一瞥表明有一个整体更多的错误在那里。

相关问题