Jump to content

How to fix segmentation fault?

- - - - -

  • Please log in to reply
4 replies to this topic

#1
Moe45673

Moe45673

    Newbie

  • Members
  • Pip
  • 7 posts
School assignment to do with ISBN codes. An ISBN is basically 4 parts: An Area code, publisher code, title code, and check digit. My code checks if the ISBN is valid and if it's registered. To make it less confusing, think of a phone number. It's valid if it's in this format "555-555-5555" but noone in the world might have that number, meaning it's not registered. Same thing with ISBN


Two classes: ISBN is 4 strings...... full string, and its segmented parts (area, publisher, title). ISBNPrefix is basically a FILE pointer to a txt file that has a list of acceptable area codes and the publisher ranges for each area code. My problem: I'm getting a segmentation fault on the ISBNPrefix destructor (which basically closes the file). I'm guessing the file pointer goes out of scope but I can't figure out where.

My test main file:
#include <new>
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;


#define PREFIX "prefixRanges.txt"  // file of ISBN Prefix Ranges


#include "ISBNPrefix.h"
#include "ISBN.h"


int main()
{
    ISBNPrefix list(PREFIX);




    ISBN sample("0003194876", list);


    //sample.testdisplay();




    cout << "Signing Off....." << endl;
    return 0;
}


what this file does is creates a new ISBNPrefix (basically a file pointer), a new ISBN (with an ISBN input and the ISBNPrefix instance to compare it to to make sure it's valid and separate the string into the three area, publisher, and title strings). The testdisplay() method works fine, ISBN sample constructs A-OK. The empty destructor for ISBNPrefix is automatically called.


Header files:

class ISBNPrefix{


    FILE* fp;


public:


    ISBNPrefix(const char* filename);
    
    bool isRegistered(int area) const;


    int minNoDigits(int area) const;


    bool isRegistered(int area, const char* publisher) const;


    ~ISBNPrefix();
};




Methods are: constructor (open the file for read only);
isRegistered(int area) - is the area registered;
minNoDigits - based on the area, minimum num of digits the publisher can be;
isRegistered(int, const char*) - is the publisher registered based on the given area
~ISBNPrefix() - destructor (closes file)


class ISBNPrefix;

class ISBN
{


    char strisbn[11];
    char area[6];
    char publisher[8];
    char title[7];


    bool isRegistered(const ISBNPrefix&);


public:


    ISBN();


    ISBN(const char*, const ISBNPrefix&);


};


int isValid(const char*);

Methods are:
private isRegistered - takes the instance strisbn and separates it into area, publisher, and title if it's registered. This is called by the non-empty constructor and itself calls many of the ISBNPrefix methods.
Empty constructor - set all strings at [0] to '\0'
Constructor - set ISBN, check if it's valid and registered against the list provided as second argument, otherwise does same thing as empty constructor.
isValid - accepts a string and returns if it's in proper ISBN format.

Btw, an Area can be up to 5 characters, a publisher up to 7 characters, and a title up to 6 characters. A full ISBN is always 10 characters..... the segfault is not from too many characters in the array. I think.

ISBNPrefix definition file:
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;


#include "ISBNPrefix.h"








ISBNPrefix::ISBNPrefix(const char* filename)
{
    fp = NULL;
    fp=fopen(filename, "r");


    if (fp == NULL)
        cout << "Invalid file!" << endl;
}






// Function checks if the area passed is in the prefix list
bool ISBNPrefix::isRegistered(int area) const
{


    bool check=false;


    if (fp != NULL)
    {
        char area2[6]; // read file line into this array
        int i, j, k, val; // check is the value returned from the function.




        rewind(fp);


        fscanf(fp, "%d %*s %*s", &val);




        while (check == false && !feof(fp))
        {




            //cout << "val = " << val << endl;
            if (area == val)
                check = true;


            fscanf(fp, "%d %*s %*s", &val);
        }  //end while loop
    }


    return check;


} //end isRegistered()


// Function checks the minimum number of digits in a publisher field


int ISBNPrefix::minNoDigits(int area) const
{
    int isreg; //check if area exists in file, returned if false
    char line[20], lineinput[20];
    int numarray[5], i, j, k, check = 0, val, counter = 0; // numarray is the array that will hold the numeric digits read in from the file. check is the while terminator. counter is returned


    if (fp != NULL)
    {


        rewind(fp);
        fgets(lineinput, 20, fp);


        isreg = isRegistered(area);


        if (isreg == 0)
            return isreg;


        else
        {
            rewind(fp);
            fgets(lineinput, 20, fp);


            while (check != 1 && !feof(fp))
            {


                val = 0; //val is the number used to turn the char values of area section read from the file into an int




                for (i = 0; lineinput[i] != ' '; i++)   //loop through until lineinput i == whitespace (ie, only area field of line)
                {
                    lineinput[i] = lineinput[i] - '0'; //turns individual char into its numeric int
                    numarray[i] = lineinput[i];        // now have an int to work with, made up solely of the area
                    lineinput[i] = lineinput[i] + '0';
                }


                i--; //i is now the maximum number of digits that area is minus 1 (count from zero)


                for (j = 0; j <= i; j++)  //turn the individual numbers into an actual number. Say the number is 972 so i = 2 ((number of digits in 972) - 1)
                {
                    k = pow(10, i - j);  //10 to the power of 2 - 0 (on first loop) = 100


                    numarray[j] = numarray[j] * k;  // 9 * 100
                    val += numarray[j];    // val == 900. On the next pass, the 7 will become 70 and will be added to val, so 970. On the 3rd pass, 2*1 will be added.




                }




                if (area == val)
                    check = 1;


                strcpy(line, lineinput); //line array holds the values of the publisher fields before lineinput gets updated
                fgets(lineinput, 20, fp);
            }  //end while loop


            i += 2; // line[i] now equals the first character of the publisher field


            while (line[i] != ' ')
                    {
                  counter++;
                  i++;
            }


            return counter;


        } //end else
    }


    else
        return 0;


}  // end minNoDigits()


bool ISBNPrefix::isRegistered(int area, const char* publisher) const
{
    bool check = false;
    if (fp != NULL)
    {




    char line[20][20], area2[6], publow[11], pubhigh[11], pubnum[11];
    int i = 0, j, check1 = 0, count, mindigits, compare;










        check = isRegistered(area);


        mindigits = minNoDigits(area);




        if (check == 1)
        {
            rewind(fp);
            check = false;
            while (check == false && !feof(fp))
            {


                fscanf(fp, "%s %s %s", area2, publow, pubhigh);


                if ((strlen(publisher) == strlen(publow)) && atoi(area2) == area)
                {
                    if ((strcmp(publisher, publow) >= 0) && (strcmp(publisher, pubhigh) <= 0))
                    {
                        check = true;
                    }
                }


            }


        }
    }


    return check;
}


ISBNPrefix::~ISBNPrefix()
{
    rewind(fp);
    if (fp != NULL)
    {
        fclose(fp);
    }
}



ISBN definition file:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;


#include "ISBNPrefix.h"
#include "ISBN.h"


ISBN::ISBN()
{
    strisbn[0]='\0';
    area[0]='\0';
    publisher[0]='\0';
    title[0]='\0';
}


ISBN::ISBN(const char* str, const ISBNPrefix& list)
{
    int check = 0;
    bool check1 = false;
    strcpy(strisbn, str);




    check = isValid(strisbn);


    if (check)
    {
        check1 = isRegistered(list);
    }


    if (!check1)
    {
        strisbn[0]='\0';
        area[0]='\0';
        publisher[0]='\0';
        title[0]='\0';
    }


}


void ISBN::testdisplay()
{
    cout << "strisbn = " << strisbn << " and area = " << area << " and publisher = " << publisher << " and title = " << title << endl;
}


bool ISBN::isRegistered(const ISBNPrefix& list)
{
    int numarea, i = 0, j, k, check1 = 0;
    char temp[11];
    int check2 = 0, check3 = 0;








    check1 = 0;


    if (strisbn[0] == '0')
    {
        strcpy(area, "0");
        numarea = 0;
        check1 = 1;


    }


        //cout << "after testing if first digit is 0, check1 = " << check1 << endl;


    if (strisbn[0] != '0')
    {


        for (i = 5; i >= 1 && check1 == 0; i--) //counts down from max strlen of area
        {
                   
            strncpy(area, strisbn, i);
            area[i] = '\0';
            numarea = atoi(area);
            check1 = list.isRegistered(numarea);
            //cout << "when i = " << i << ", check1 = " << check1 << endl;
            if (check1 == 0)
                  area[0]='\0';
                


        }


    }


    if (check1 != 0) // area is found in list, extract publisher code if within acceptable range
    {
        check1 = 0;
        i = strlen(area);
        int max = 10;
        j = (max+1) - i;
        strcpy(temp, &strisbn[i]);
            
        int min = list.minNoDigits(numarea), comp = 1, valpub = 0;
        // i ends up as the amount of digits in area, j is the amount of digits in publisher, str[k] is where title starts, and l is the length of title
        //  i has an i--; immediately in the while loop to bring it to 5
        //cout << "min = " << min << endl;


        while (check1 != 2 && j > 0)
        {
            j--;
            k = j + i; //the last possible space that could be a publisher digit
            //strncpy(temp, &str[i], j);
            temp[j] = '\0';
            
            valpub = list.isRegistered(numarea, temp);


            if (valpub != 0)
            {
                check1 = 2;


                strcpy(publisher, temp);
            }


                
                //cout << "check1 if valpub = 0" << endl;




        }
            
    }


    if (check1 == 2)
    {
        strcpy(title, &strisbn[k]);
        title[strlen(title) - 1] = '\0';
    }
    //cout << "area = " << numarea << " and publisher is " << publisher << " and title is " << title << endl;




    if (check1 == 2)
        return true;
    else
        return false;
}                            






int isValid(const char* str)
{
    int check = 0, i, num[10], val = 0, mod = 1, length = 0;


    if (str == NULL)
        check = 0;




    else if (strlen(str) == 10) //10 characters in the string
    {
        check = 1;
        for (i = 0; i < 9; i++) //go through first 9 characters, make sure they're a digit. If they are valid, propagate num array with numerical values
        {
            if (str[i] < '0' || str[i] > '9')
                check = 0;


            else
                num[i] = str[i] - '0';




        }


        if (check != 0) //the first 9 characters are digits, make sure 10th char is either a digit or letter X (10). Propagate 10th num array position with numerical value.
        {
            if (str[9] < '0' || str[9] > '9' && str[9] != 'X') //if last char is not a digit between 0 and 9 and is not X


                check = 0;


            else if (str[9] == 'X') 
                num[9] = 10;






            else                             //in other words, last char is a digit between 0 and 9
                num[9] = str[9] - '0';


        }


        if (check != 0) //do the math to make sure it's all modulo 11
        {


            for (i = 0; i < 10; i++)
            {
                val += num[i] * (10 - i);
            }


            mod = val % 11;




            if (mod != 0)
                check = 0;
        }


    } // if statement about there being 10 characters
    return check;
}



Help please? I'm guessing fp goes out of scope after the ISBN non-empty constructor wraps up but I can't figure out how! The ISBNPrefix destructor works fine if I comment out the ISBN constructor in the main file.

Edited by Moe45673, 26 October 2011 - 07:23 AM.


#2
WingedPanther

WingedPanther

    A spammer's worst nightmare

  • Moderators
  • 16,831 posts
  • Location:Upstate, South Carolina
  • Programming Language:C, C++, PL/SQL, Delphi/Object Pascal, Pascal, Transact-SQL, Others
  • Learning:Java, C#, PHP, JavaScript, Lisp, Fortran, Haskell, Others
There are a number of things that jump out at me in the above.

1) Why are you using cstrings instead of std::string? You have a lot of logic in there that is keyed off a low-level string manipulation. That will also protect you against segfaults. Similarly, why fscanf instead of a filestream?
2) You mentioned a segfault, but you haven't given specific details of how to reproduce it. If you have specific files and inputs that duplicate the errors, we can then run it locally in a debugger to see exactly what is happening. Do you know what line is getting the error?
Programming is a branch of mathematics.
My CodeCall Blog | My Personal Blog

#3
Moe45673

Moe45673

    Newbie

  • Members
  • Pip
  • 7 posts
Thanks for taking the time!

1) We haven't learned about C++ strings and files. This course builds off C and we're only learning classes at the moment (instructions specifically state "this method takes a C-Style null terminated string", for example).

2) Basically, any valid/registered ISBN I create using the constructor in my tester main file will cause the segfault. Here is the text file "prefixRanges.txt" to be called into the ISBNPrefix object.
https://scs.senecac....refixRanges.txt.

The logic behind this txt file is that the first column is the list of all available area codes (0, 1, 2, 3, 4, 5, 6, 7, 80, 81, 82, 83............, 92, 950, 951, etc. In other words, if the first digit of the ISBN is 7, the area code is 7 but if the first digit is 8, the area code cannot be one digit). The second column is, based on the area code, the minimum value the publisher can be and the third is the maximum value the publisher can be. So for example, if the area code is 0 the publisher code can be between (first line of txt file) 00 and 19 or between (2nd line) 200 and 699 or between 7000 and 8499, etc. Similarly, if the area code is 9981, the publisher code can be between 00 and 09 or between 100 and 159 or between 20 and 79, etc.

The error occurs on the first usage of the fp file pointer in the ISBNPrefix destructor, whether I choose to do a rewind or fclose or what have you. Again, this only occurs when it's run through the ISBN non-empty constructor (assuming said constructor has a valid/registered ISBN). The three registered ISBNs I am using to test are: 0003194876, 9070002043, 9972000001

3) FYI, an ISBN is valid if
  • it has 10 characters
  • the first 9 digits are between 0 and 9 and the last (check) digit is between 0 and 9 or X (which equals 10).
  • You then add up all the digits multiplying each one by 10--; so for 0003194876 it would be (0 x 10) + (0 x 9) + (0 x 8) + (3 x 7) + (1 x 6)....... + (7 x 2) + (6 x 1) = "whatever". You then do "whatever" modulus 11 and if that equals 0 it's valid.


I'm pretty sure my isValid method isn't screwing it up, it's more the ISBN::isRegistered() one.

#4
WingedPanther

WingedPanther

    A spammer's worst nightmare

  • Moderators
  • 16,831 posts
  • Location:Upstate, South Carolina
  • Programming Language:C, C++, PL/SQL, Delphi/Object Pascal, Pascal, Transact-SQL, Others
  • Learning:Java, C#, PHP, JavaScript, Lisp, Fortran, Haskell, Others
What happens if you put rewind(fp) inside your if statement?
Programming is a branch of mathematics.
My CodeCall Blog | My Personal Blog

#5
Moe45673

Moe45673

    Newbie

  • Members
  • Pip
  • 7 posts
fp does not equal null so segfault on the rewind whether in or out of the if statement. If I remove rewind(fp) entirely, I get the segfault on the fclose(fp) statement.

Interestingly, I don't get the segfault in MinGW but I do get it when using g++ in bash.




1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users