views:

286

answers:

5

I'm making a small file reading and data validation program as part of my TAFE (a tertiary college) course, This includes checking and validating dates.

I decided that it would be best done with a seperate class, rather than integrating it into my main driver class.

The problem is that I'm getting a segmentation fault(core dumped) after my test program runs. Near as I can tell, the error occurs when the program terminates, popping up after the destructor is called. So far I have had no luck finding the cause of this fault, and was hoping that some enlightened soul might show me the error of my ways.

date.h

#ifndef DATE_H
#define DATE_H

#include <string>
using std::string;

#include <sstream>
using std::stringstream;

#include <cstdlib>
using std::exit;

#include <iostream>
using std::cout;
using std::endl;

class date {

    public:
        explicit date();
        ~date();
        bool before(string dateIn1, string dateIn2);
        int yearsBetween(string dateIn1, string dateIn2);
        bool isValid(string dateIn);
        bool getDate(int date[], string dateIn);
        bool isLeapYear(int year);
    private:
        int days[];

};
#endif

date.cpp

#include "date.h"

date::date() {

    days[0] = 31;
    days[1] = 28;
    days[2] = 31;
    days[3] = 30;
    days[4] = 31;
    days[5] = 30;
    days[6] = 31;
    days[7] = 31;
    days[8] = 30;
    days[9] = 31;
    days[10] = 30;
    days[11] = 31;

}

bool date::before(string dateIn1, string dateIn2) {

    int date1[3];
    int date2[3];

    getDate(date1, dateIn1);
    getDate(date2, dateIn2);

    if (date1[2] < date2[2]) {

        return true;

    } else if (date1[1] < date2[1]) {

        return true;

    } else if (date1[0] < date2[0]) {

        return true;

    }

    return false;

}

date::~date() {

    cout << "this is for testing only, plox delete\n";

}

int date::yearsBetween(string dateIn1, string dateIn2) {

    int date1[3];
    int date2[3];

    getDate(date1, dateIn1);
    getDate(date2, dateIn2);

    int years = date2[2] - date1[2];

    if (date1[1] > date2[1]) {

        years--;

    } 

    if ((date1[1] == date2[1]) && (date1[0] > date2[1])) {

        years--;

    }

    return years;

}

bool date::isValid(string dateIn) {

    int date[3];

    if (getDate(date, dateIn)) {

        if (date[1] <= 12) {

            int extraDay = 0;

            if (isLeapYear(date[2])) {

                extraDay++;

            }

            if ((date[0] + extraDay) <= days[date[1] - 1]) {

                return true;

            }

        }

    } else {

        return false;

    }

}

bool date::getDate(int date[], string dateIn) {

    string part1, part2, part3;

    size_t whereIs, lastFound;

    whereIs = dateIn.find("/");

    part1 = dateIn.substr(0, whereIs);

    lastFound = whereIs + 1;

    whereIs = dateIn.find("/", lastFound);

    part2 = dateIn.substr(lastFound, whereIs - lastFound);

    lastFound = whereIs + 1;

    part3 = dateIn.substr(lastFound, 4);

    stringstream p1(part1);
    stringstream p2(part2);
    stringstream p3(part3);

    if (p1 >> date[0]) {

        if (p2>>date[1]) {

            return (p3>>date[2]);

        } else {

            return false;

        }

        return false;

    }

}

bool date::isLeapYear(int year) {

    return ((year % 4) == 0);

}

and Finally, the test program

#include <iostream>
using std::cout;
using std::endl;

#include "date.h"

int main() {

    date d;

    cout << "1/1/1988 before 3/5/1990 [" << d.before("1/1/1988", "3/5/1990")
        << "]\n1/1/1988 before 1/1/1970 [" << d.before("a/a/1988", "1/1/1970")
        <<"]\n";

    cout << "years between 1/1/1988 and 1/1/1998 [" 
        << d.yearsBetween("1/1/1988", "1/1/1998") << "]\n";

    cout << "is 1/1/1988 valid [" << d.isValid("1/1/1988") << "]\n" 
        << "is 2/13/1988 valid [" << d.isValid("2/13/1988") << "]\n"
        << "is 32/12/1988 valid [" << d.isValid("32/12/1988") << "]\n";

    cout << "blerg\n";

}

I've left in some extraneous cout statements, which I've been using to try and locate the error.

I thank you in advance.

+1  A: 
int days[];

This is a non-standard extension. You must specify a size for the array, such as:

static const MonthCount = 12;
int days[MonthCount];

To actually have an array to use. Otherwise you have a "zero-sized array" (not standard!). Your program is tromping over memory every time you use any element of your current array.

GMan
Ok, cool, Thanks for the answers, They work, huzzah.I thank you all,
+3  A: 

Change:

private:
    int days[];

to:

private:
    int days[12];
Paul R
+3  A: 

The problem is that you never actually initialize the days field in the type date. This means that when you are setting the values in the constructor you are accessing uninitialized memory.

You need to explicitly initialize the days value in some way. The easiest fix is to use a vector for the type or to hard code the size of the array to 12.

private:
  int days[12];

Or

private:
  std:vector<int> days;

...
date::date() {
  days.push_back(31);
  days.push_back(28);
  ...
}
JaredPar
A: 

I agree with the previous answers to this question, but I would add the rationale for their correctness:

Segmentation faults are caused whenever you attempt to access memory you are not allowed to access.

http://en.wikipedia.org/wiki/Segmentation_fault

You were not allowed to access "days[0]" through days "[11]" because the computer had not given the "days[]" variable that you declared enough memory to hold any elements, thus when you tried to access said elements, it threw a segfault.

Any variables not declared with the "new" operator are placed on the "stack," which is a contiguous chunk of memory the computer has sectioned away for use by the program. In order to keep everything stored in the stack contiguous, the computer will only give exactly the amount memory you require for you to use whenever you request it, so that if you request to create an int, for example, it will only give you enough memory to store that single int.

When you wrote the line int days[]; the computer attempted to evaluate how much memory it would require, assessed it as an empty array, and gave you enough memory to store said empty array. Because the computer did not give your array any extra space beyond what was needed for an empty array, it knew that the memory you tried to access in that array had not been assigned to it, so it threw a segmentation fault and crashed.

If you have not yet learned about the "stack" and "heap" in your computer science class, then sorry if this is a bit overwhelming, but I perhaps overcomplicated things, and I think you likely soon will.

eipxen
Thank you all kingly for the help, it is much appreciated.
+1  A: 

You don't say which compiler you are using, but if I compile this code using g++ with the -Wall and -pedantic flags:

struct S {
    int a[];
};

int main() {
    S s;
}

I get the warning message:

warning: ISO C++ forbids zero-size array 'a'

The moral is that you should always compile using as many compiler warnings as possible - it can save you mountains of time and result in more correct code.

anon
+1 for encouraging the use of `-Wall`. The compiler is a developer's best friend and one should pay attention to its rants!
Matthieu M.