Apologies, the way I worded my thread may have caused some misunderstanding, I wasn’t asking anyone to do my coursework for me. I asked for help on how to begin my code and additional guidance so I could understand what was wrong with my own code and learn how to fix it. Im not a school student, I'm a first year EEE student and I could hope you understand how much challenging university is compared to school. I’ve had other very time consuming modules to deal with, which is why I had to leave this for last minute, but that doesn’t mean I’m avoiding my own work. I also haven’t used or copied any code given in this thread since I’ve written my own and asked for help to get it working, in which these kind people actually assisted me. For learning from videos, that’s how a lot of beginners nowadays start, alongside tutorials and forums like this. This is a help forum, so if you don’t want to help then its fine.IMHO, it is ridiculous to try to help someone who “learned” C from a video. And who needs help because he did not allow enough time for the project. Poor abused child. My heart, and I’m sure those of others who actually did their own schoolwork, goes out to you.
While there is some room for legitimate criticism about the TS putting off working on the assignment until the deadline was approaching, that doesn't mean that it is ridiculous for others to try to help them learn. Let's also try to present such criticism in a more civil manner, though it's easy for something to come across as more harsh/rude than it was intended (something we've pretty much all been guilty of). Hopefully the TS recognizes the position they put themselves into because of not managing their time well, but learning those kinds of skills is all part of being a student and hopefully they will do a better job in the future.IMHO, it is ridiculous to try to help someone who “learned” C from a video. And who needs help because he did not allow enough time for the project. Poor abused child. My heart, and I’m sure those of others who actually did their own schoolwork, goes out to you.
I suspect this is a cultural terminology issue, but could you clarify what you mean by not being a school student but yet being a first year EEE student?Im not a school student, I'm a first year EEE student and I could hope you understand how much challenging university is compared to school.
Thank you very much for your help, using your idea I was able to get my code working as needed. I really appreciate guidiance.why do you have two functions that calculate dates? just use one and pass dates as parameter:
totaldays1 = datecalculation(day1,month1,year1);
totaldays2 = datecalculation(day2,month2,year2);
you are dealing with dates. that is a structure that includes days, months and year. but... your calculation only calculates days based on year. so 17/12/2000 and 24/3/2000 have same number of days since you only consider 2000*365 and disregard the rest.
obviously it should also add previous days of current month, and days-per-month of previous months of current year.
Using your valuable knowledge, i was able to get my code working and I really appreciate it. I separated the functions inside main() which was the only reason why the code wasnt working earlier. I also tried to fix my indentation to make the code look more cleaner. I will use your idea of using global variables and fgets() in my future code as I advanced through my knowledge. I decided to keep things simple right now because it felt easier for me to understand and my prof will know that i made the code myself. However though, the information you told me about how fgets() is a better alternative to scanf() was really interesting, and once again i will definitely implement that in my future codes. About the int main(void) and the function below it, i have not really learnt about it yet, so i would not make sense for me to use it now. I really appreciate your guidance.Let's look at the initial code you provided and make sure that you understand what it is doing and why it is behaving the way it is:
The C language standard does not allow functions to be defined within functions. Some compilers, including GCC, allow it as a non-standard extension, but it makes the code very non-portable and should be avoided unless you have a truly compelling reason to do so.Code:#include <stdio.h> #include <math.h> int noofdaysinmonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30 ,31}; /*this array handles the number of days in the month*/ int day1 = 0, month1 = 0, year1 = 0; int day2 = 0, month2 = 0, year2 = 0; int main() { int datecalculation1()/*this function checks if date is valid and calculates the number of days from entered years*/ { if((year1 <= 0 || year1 > 9999) || (day1 > noofdaysinmonth[month1 - 1]|| day1 < 1) || (month1 < 1 || month1 > 12)) { fprintf(stderr, "INVALID DATE"); /*this if statement block checks if the entered date is in the specified range. If not, it prints error using stderr*/ return 0; } else { return 1; int totaldays = 0; totaldays = year1 * 365; /*calculates number of days in the year*/ printf("enter your first date: "); scanf("%d/%d/%d", &day1, &month1, &year1); } } return 0; }
Leaving that aside, your code had very inconsistent indenting and placement of curly braces, making the structure of the code very easy to misinterpret. I've cleaned that up in the code above using the Allman brace/indentation style, which I strongly prefer. It places opening/close braces on their own line and aligned with the controlling construct, while the code they contain is indented one level. Compared to the more typical K&R style, which places the opening brace at the end of the controlling construct, this makes it trivially easy to examine your structure and identify/correct issues with mismatched braces, which are a quite common source of problems.
With the code well formatted, we can see right away that your main is of the following form:
So, it's not surprising that your program does nothing, because all your main() does when it runs is return 0. Functions are not executed when they are defined. Definitions only make them available to be called elsewhere in the code. It may SEEM like main() violates this, but it doesn't. It is just another function that your code is defining so that it is available later. What is happening behind the scenes is that the compiler is inserting additional code that, after all the code is compiled, executes a call to the function named 'main'. This is why your code has to have a function with this name.Code:int main() { // Define a function named datecalculation1() return 0; }
Next, look at where your code that gets user input is located. It is in the 'else' clause of an if()-else construct, which is certainly not what you had intended. Worse, it is located after a return statement, making it unreachable. Do you see how proper indenting and brace management make this glaringly obvious? Paying attention to these details will save you hours and hours of frustration.
So, where should you define your functions?
There are two dominant schools of thought. One is that main() should go first and the other functions that support it should follow. The other school is that support functions should be defined first with main() at the bottom. Most programmers start out in the first school, because it let's them read the code a bit like a novel. But this then requires that they have function prototypes above main() and this invites mismatches between the prototypes and the actual definitions as the code evolves. It also requires more time and effort to manage the prototypes. This is avoided by people in the second school, since functions are defined before they are first called and thus no prototypes are needed, about the only exception being when multiple functions are mutually recursive, something that is usually unintended and will get brought to your attention by the compiler precisely because of the missing prototype. As you might guess, I'm firmly in the second school, though I programmed for years in the first school before I realized the value of the second. Like most students, I didn't even realize that the second approach existed or was an option, because I just did it the way my first C course presented it and I assumed that that was the way it had to be done. After that, it was a habit that I saw no reason to change.
The next point I would offer is that you are using global variables. This is highly frowned upon. Globals have their place, but they are few and far between. Most people that use them are doing so because they are being lazy and don't want to pass the needed information to the functions they call. But doing so largely violates the whole concept of why we define and use functions, which is to create modules of code with well-defined interfaces for our code to interact with. The rule you should adhere to is that globals should only be used if there is a damn good reason and you should be able to articulate what that reason is and why they are justified. I think I have used globals in two programs in the last forty years. Having said that, a reasonable argument could be made, in a program of this scope, that having the array with the number of days in each month be global is not unreasonable (but it should be declared as a const array so that the compiler has a chance to detect attempts to change it). But a better way is to write a function, DaysInMonth(), that takes a month and year and returns the number of days in that month. The array would be a local variable within this function. This does suffer function-calling overhead, but on today's machines, if that's an issue, then the entire approach to writing the program needs to be different. This overhead can be reduced by declaring the array to be static so that it is not reinitialized with every call.
Another thing you should do differently is to use one of the two standards-compliant signatures for main(). These are:
int main(void) {}
int main(int argc, char *argv[]) {}
The first is when you aren't going to use any of the command line arguments, and the second is when you are (though you can always use the second, whether you use them or not).
Finally, you really shouldn't use scanf(), but I'm not going to insist that you don't (if you were one of my students, however, I would). The scanf() family of functions are notoriously ill-behaved and have been the source of countless program bugs, including a large fraction of security vulnerabilities that have resulted in massive data breaches. They should only be used when you KNOW you have complete control over and confidence in the format of the input data. The better approach is to use fgets() to read in the entire input as a string and then parse the string yourself. This gives YOU complete control over what is happening. But it's involved. The way to do it is to write a function that gets the user input as a dynamically allocated string, so that it can expand as needed to match however long the actual input is (this avoids buffer overflow attacks). Then you can write a function to inspect and parse the string as needed before freeing the string to avoid memory leaks. This is they it SHOULD be taught from the beginning, because letting students use scanf() at all leads them to continue using scanf(), which is how massive data breaches happen. But I realize that you, as a student, are limited by what you have been shown, so that's all I'll say on this point.
yes, you’re right that this is partly a cultural terminology difference. Here in the UK (and I believe across much of Europe as well), we usually use “school” to refer to pre-university education, while university is seen as a separate stage, which is why I said I’m not a school student but a first-year EEE student.I suspect this is a cultural terminology issue, but could you clarify what you mean by not being a school student but yet being a first year EEE student?
Here (i.e., on AAC), "homework" refers to ANY assignment that is being done for any kind of credit. We tend to use the same approach even with people that are doing assignment-like tasks even if it's purely on their own initiative, because the goal is not to solve the problem, but to learn how to solve comparable problems in the future.
Most students that ask questions here are university students. The majority are in their first two years of their program. We get some high school students (or whatever the comparable level where you are from are called) as well as some graduate students. We can usually estimate the level by the nature of the problem being worked, but that's not guaranteed, so it can be helpful to know what level the person is actually at.
Using fgets() to gets input requires some understanding of what is going on under the hood and is not something that beginners would find easy figuring out on their own, so unless your instructor or your text covers it, I'd put it on the back burner for now, but it is something you should look at down the road.I will use your idea of using global variables and fgets() in my future code as I advanced through my knowledge. I decided to keep things simple right now because it felt easier for me to understand and my prof will know that i made the code myself.
All the 'void' keyword does is tell the compiler that the function takes no arguments. This is implied by "int main()" and is how the original C language did it. The standards simply required it to be explicit. Most compilers won't complain about not having it unless you configure them to strictly enforce the standard. But you should get in the habit of writing standards-compliant code unless there's a good reason not to (and good reasons do exist).About the int main(void) and the function below it, i have not really learnt about it yet, so i would not make sense for me to use it now. I really appreciate your guidance.
Hi there,Could anyone please help me to create a program in C that calculates the amount of days between to dates, for example the difference between 23/11/1987 and 5/11/1987 should be -18. and the date should print errors using stderr if the format or date range is invalid, but as a beginner i have no idea how to go about this. any help would be appreciated
MOD NOTE: Moved to Homework Help.
@iLEEZi: Now that we are well past the presumed due date, I'll walk through one approach to solving the problem, showing the incremental steps in the development that you might consider. This will be an example of what is known as the "top-down" approach in which the high-level logic is implemented and the low-level details filled in later. This is accomplished by "stubbing out" low level functions so that they return something valid, regardless of whether it is correct or not.Could anyone please help me to create a program in C that calculates the amount of days between to dates, for example the difference between 23/11/1987 and 5/11/1987 should be -18. and the date should print errors using stderr if the format or date range is invalid, but as a beginner i have no idea how to go about this. any help would be appreciated
MOD NOTE: Moved to Homework Help.
#include <stdio.h>
int main(void)
{
// Get the first date from the User
// Get the second date from the User
// Calculate the number of days from the first date to the second
// Print out the result
return 0;
}
#include <stdio.h>
void GetDate(int date[])
{
date[0] = 25;
date[1] = 9;
date[2] = 2007;
}
int DaysBetween(int date1[], int date2[])
{
return 42;
}
int main(void)
{
//
int date1[3], date2[3]; // Date[] = {dd, mm, yyyy}
int days;
// Get the first date from the User
GetDate(date1);
// Get the second date from the User
GetDate(date2);
// Calculate the number of days from the first date to the second
days = DaysBetween(date1, date2);
// Print out the result
printf("%02d/%02d/%04d is %d days after %02d/%02d/%04d.\n",
date2[0], date2[1], date2[2], days, date1[0], date1[1], date1[2]);
return 0;
}
#include <stdio.h>
void GetDate(int date[])
{
int temp;
printf("Enter a date: ");
scanf("%d", &temp);
if (1 == temp)
{
date[0] = 25;
date[1] = 9;
date[2] = 2007;
}
else
{
date[0] = 6;
date[1] = 3;
date[2] = 1974;
}
}
int CompareDates(int date1[], int date2[])
{
// IF Years are different
if (date2[2] != date1[2])
// IF year2 > year1, date2 > date1 ELSE data2 < date1
return (date2[2] > date1[2])? 1 : -1;
// IF Months are different
if (date2[1] != date1[1])
// IF month2 > month1, date2 > date1 ELSE data2 < date1
return (date2[1] > date1[1])? 1 : -1;
// IF Days are different
if (date2[0] != date1[0])
// IF day2 > day1, date2 > date1 ELSE data2 < date1
return (date2[0] > date1[0])? 1 : -1;
// Dates are equal
return 0;
}
int DaysBetween(int date1[], int date2[])
{
return 42 * CompareDates(date1, date2);
}
int main(void)
{
//
int date1[3], date2[3]; // Date[] = {dd, mm, yyyy}
int days;
// Get the first date from the User
GetDate(date1);
// Get the second date from the User
GetDate(date2);
// Calculate the number of days from the first date to the second
days = DaysBetween(date1, date2);
// Print out the result
printf("%02d/%02d/%04d is %d days after %02d/%02d/%04d.\n",
date2[0], date2[1], date2[2], days, date1[0], date1[1], date1[2]);
return 0;
}
int DaysBetween(int date1[], int date2[])
{
// Make a copy of the first date
// Initialize day counter to zero
// WHILE the temp date isn't equal to the second date
// Adjust day counter
// Move temp date one day closer to second date
// Return the day counter
}
void AdjustDate(int date[], int plusMinus)
{
// IF plusMinus is positive
if (1 == plusMinus)
{
// Move the date forward one day
date[0]++;
}
// ELSE
else
{
// Move the date back one day
date[0]--;
}
}
void GetDate(int date[])
{
int temp;
printf("Enter a date: ");
scanf("%d", &temp);
if (1 == temp)
{
date[0] = 23;
date[1] = 11;
date[2] = 1987;
}
else
{
date[0] = 5;
date[1] = 11;
date[2] = 1987;
}
}
Enter a date: 1
Enter a date: 2
05/11/1987 is -18 days after 23/11/1987.
Enter a date: 2
Enter a date: 1
23/11/1987 is 18 days after 05/11/1987.
Enter a date: 1
Enter a date: 1
23/11/1987 is 0 days after 23/11/1987.
void AdjustDate(int date[], int plusMinus)
{
// IF plusMinus is positive
if (1 == plusMinus)
{
// Move the date forward one day
date[0]++;
// Move to start of following month if necessary
if (31 < date[0])
{
date[1]++;
date[0] = 1;
}
// Move to start of following year if necessary
if (12 < date[1])
{
date[2]++;
date[1] = 1;
}
}
// ELSE
else
{
// Move the date back one day
date[0]--;
// Move to end of prior month if necessary
if (1 > date[0])
{
date[1]--;
date[0] = 31;
}
// Move to end of prior year if necessary
if (1 > date[1])
{
date[2]--;
date[1] = 12;
}
}
}
Enter a date: 1
Enter a date: 2
23/11/1887 is -37200 days after 23/11/1987.
Enter a date: 2
Enter a date: 1
23/11/1987 is 37200 days after 23/11/1887.
Enter a date: 1
Enter a date: 2
31/12/9999 is 3719999 days after 01/01/0000.
void AdjustDate(int date[], int plusMinus)
{
// IF plusMinus is positive
if (1 == plusMinus)
{
// Move the date forward one day
date[0]++;
// Move to start of following month if necessary
if (DaysInMonth(date) < date[0])
{
date[1]++;
date[0] = 1;
}
// Move to start of following year if necessary
if (12 < date[1])
{
date[2]++;
date[1] = 1;
}
}
// ELSE
else
{
// Move the date back one day
date[0]--;
// Move to end of prior month if necessary
if (1 > date[0])
{
date[1]--;
date[0] = DaysInMonth(date);
}
// Move to end of prior year if necessary
if (1 > date[1])
{
date[2]--;
date[1] = 12;
}
}
}
int DaysInMonth(int date[])
{
static int daysInMonth[] = {31,28,31,30,31,30,31,31,30,31,30,31};
// Return the number of days in the month with wraparound
// (date[1]-1) accounts for array index being zero-based
// (date[1]-1)+12 moves negative months to positive for rem to be mod
// ((date[1]-1)+12)%12 maps values outside array bounds back in
return daysInMonth[((date[1]-1)+12)%12];
}
int DaysInMonth(int date[])
{
static int daysInMonth[] = {31,28,31,30,31,30,31,31,30,31,30,31};
// SET leapDay to 1 if the date is in February of a leap year (0 otherwise)
int leapDay = (2 == date[1]) && IsLeapYear(date));
// Return the number of days in the month with wraparound
// (date[1]-1) accounts for array index being zero-based
// (date[1]-1)+12 moves negative months to positive for rem to be mod
// ((date[1]-1)+12)%12 maps values outside array bounds back in
return daysInMonth[((date[1]-1)+12)%12] + leapDay;
}
int IsLeapYear(int date[])
{
int year = date[2];
// Years divisible by 400 ARE leap years
if (0 == year%400)
return 0;
// All other century years are NOT leap years
if (0 == year%100)
return 0;
// All other years divisible by 4 ARE leap years
return (0 == year%4);
}
[/QUOTE]
If all years divisible by four were leap years, we would expect this to add 2500 days to our extreme span. Since there are 100 century years, this would be reduced to 2400 by the century-year rule, but the 400-year rule would add back 25 of them. Hence, over 10,000 years, there should be 2425 leap days.
But, when I ran it, I got 3652399 days, which is an additional 2400 days. This implies that the issue is related to the 400-day rule. Sure enough, my IsLeapYear() function is returning 0 in this case when it should be returning 1.
This underscores the value in incremental development and testing. Implement small pieces of code and then perform a meaningful test on them -- don't just run them and call it good if it doesn't crash. If it doesn't produce the expected output, the most likely culprit is going to be the small amount of code you just implemented, which immediately allows you to focus your attention on the correctness of that small piece of code.
Finally, we can turn our attention to getting actual dates from the user and validating them.
First, we modify our GetDate() function as follows:
[CODE]
void GetDate(int date[])
{
int temp;
printf("Enter a date in the forma DD/MM/YYYY: ");
scanf("%d/%d/%d", &date[0], &date[1], &date[2]);
if (!IsValidDate(date))
{
printf("ABORT: Date Entered is not valid.\n");
exit(1);
}
}
int IsValidDate(int date[])
{
// Year must be between 1 and 9999 (inclusive)
if ( !( (1 <= date[2]) && (date[2] <= 9999) ) )
return 0;
// Month must be between 1 and 12 (inclusive)
if ( !( (1 <= date[1]) && (date[1] <= 12) ) )
return 0;
// Day must be between 1 and 12 (inclusive)
if ( !( (1 <= date[0]) && (date[1] <= DaysInMonth(date)) ) )
return 0;
// If no tests above failed, the date is valid
return 1;
}
Thank you so much for taking your precious time and teaching me how to breakdown huge codes like this. I somehow managed to get my codes working through spending a huge load of time on trial and error, and i submitted it just a few days ago. I loved your idea of making psuedo codes and i will definitely give it a try for my next coursework. The way you wrote your code made sense to me. However, i do not get the difference between void functions and normal functions, i dont understand the use of return at the end of the function. From what i understand, i know that return 1 means true and return 0 means false, for example, in your code you used return 42 in the DaysBetween() function, and i dont understand what that does,i also know that return can be used to return a value from the function like you did in DaysInMonth() function. Additionally, function parameters is another concept i couldnt fully grasp. I know the complete basic example of function parameters where you can set a parameter in a function, then assign a value to that parameter when calling that function, but for a more difficult code i do not understand the parameters at all.@iLEEZi: Now that we are well past the presumed due date, I'll walk through one approach to solving the problem, showing the incremental steps in the development that you might consider. This will be an example of what is known as the "top-down" approach in which the high-level logic is implemented and the low-level details filled in later. This is accomplished by "stubbing out" low level functions so that they return something valid, regardless of whether it is correct or not.
The first step is to break the overall problem down into a sequence of smaller steps:
Now, we flesh out the pseudocode steps with code that, once fully implemented, will solve the problem.Code:#include <stdio.h> int main(void) { // Get the first date from the User // Get the second date from the User // Calculate the number of days from the first date to the second // Print out the result return 0; }
At this point, out main() is done. We shouldn't have to touch it again (though we should never say never). This is the stage where we can focus on getting out output just the way we want it without worrying about how we determine what the exact output should be. Later, we can focus on determining the correct output without having to worry about how we want it to look, since that's already done.Code:#include <stdio.h> void GetDate(int date[]) { date[0] = 25; date[1] = 9; date[2] = 2007; } int DaysBetween(int date1[], int date2[]) { return 42; } int main(void) { // int date1[3], date2[3]; // Date[] = {dd, mm, yyyy} int days; // Get the first date from the User GetDate(date1); // Get the second date from the User GetDate(date2); // Calculate the number of days from the first date to the second days = DaysBetween(date1, date2); // Print out the result printf("%02d/%02d/%04d is %d days after %02d/%02d/%04d.\n", date2[0], date2[1], date2[2], days, date1[0], date1[1], date1[2]); return 0; }
Also notice that our pseudocode now serves the purpose of documenting our code. The reader sees what the code is supposed to accomplish, followed the code that accomplishes it. The comments focus on the 'what' in the context of solving the problem, while the code details the 'how'.
The next thing I'm going to do is flesh out the GetDate() function just enough to be able to return two different dates (so that I can start testing the DaysBetween() function). In response to being asked for a date, the user will just enter a single number. If that number is 1, it will return one date, otherwise it will return another. This allows us to run tests very easily instead of having to type in two actual dates each time. If we want to use different dates, we can simply change the dates in this function or add additional dates (a switch() statement would be good if we want to do this).
At the same time, the DaysBetween() function will be given the ability to determine which date comes before the other, or if they are the same. It will then return +42, -42, or 0, accordingly.
Up to this point, we haven't given any thought as to how we will actually determine the number of days between the two dates, but now we are at that point.Code:#include <stdio.h> void GetDate(int date[]) { int temp; printf("Enter a date: "); scanf("%d", &temp); if (1 == temp) { date[0] = 25; date[1] = 9; date[2] = 2007; } else { date[0] = 6; date[1] = 3; date[2] = 1974; } } int CompareDates(int date1[], int date2[]) { // IF Years are different if (date2[2] != date1[2]) // IF year2 > year1, date2 > date1 ELSE data2 < date1 return (date2[2] > date1[2])? 1 : -1; // IF Months are different if (date2[1] != date1[1]) // IF month2 > month1, date2 > date1 ELSE data2 < date1 return (date2[1] > date1[1])? 1 : -1; // IF Days are different if (date2[0] != date1[0]) // IF day2 > day1, date2 > date1 ELSE data2 < date1 return (date2[0] > date1[0])? 1 : -1; // Dates are equal return 0; } int DaysBetween(int date1[], int date2[]) { return 42 * CompareDates(date1, date2); } int main(void) { // int date1[3], date2[3]; // Date[] = {dd, mm, yyyy} int days; // Get the first date from the User GetDate(date1); // Get the second date from the User GetDate(date2); // Calculate the number of days from the first date to the second days = DaysBetween(date1, date2); // Print out the result printf("%02d/%02d/%04d is %d days after %02d/%02d/%04d.\n", date2[0], date2[1], date2[2], days, date1[0], date1[1], date1[2]); return 0; }
There are many ways to do this, but let's do the equivalent of counting on our fingers. It may sound stupid, however, if it's stupid but works, it ain't stupid.
Since now we are having to develop an algorithm, we'll start with the high-level pseudocode for it.
Fleshing this out one more level deep, we have:Code:int DaysBetween(int date1[], int date2[]) { // Make a copy of the first date // Initialize day counter to zero // WHILE the temp date isn't equal to the second date // Adjust day counter // Move temp date one day closer to second date // Return the day counter }
Since we are now calling a new function, AdjustDate(), we need to stub it out in such a way that our program will at least run. To do this, we will modify out GetDate() function to use two dates in the same month and then our AdjustDate() function will simply increment or decrement the day, ignoring the month and year. We might as well use the dates the TS used in their initial post as an example.Code:void AdjustDate(int date[], int plusMinus) { // IF plusMinus is positive if (1 == plusMinus) { // Move the date forward one day date[0]++; } // ELSE else { // Move the date back one day date[0]--; } }
When we run this, we can easily test both directions as well as the special case when the two dates are equal:Code:void GetDate(int date[]) { int temp; printf("Enter a date: "); scanf("%d", &temp); if (1 == temp) { date[0] = 23; date[1] = 11; date[2] = 1987; } else { date[0] = 5; date[1] = 11; date[2] = 1987; } }
Next, we can get our AdjustDate() function to work with the full date, but for now we will assume that there are always 31 days in a month.Code:Enter a date: 1 Enter a date: 2 05/11/1987 is -18 days after 23/11/1987. Enter a date: 2 Enter a date: 1 23/11/1987 is 18 days after 05/11/1987. Enter a date: 1 Enter a date: 1 23/11/1987 is 0 days after 23/11/1987.
This gives us 372 days in a year. For our test days, we'll pick two dates that are a century apart, expecting the result to be ±37200 days.Code:void AdjustDate(int date[], int plusMinus) { // IF plusMinus is positive if (1 == plusMinus) { // Move the date forward one day date[0]++; // Move to start of following month if necessary if (31 < date[0]) { date[1]++; date[0] = 1; } // Move to start of following year if necessary if (12 < date[1]) { date[2]++; date[1] = 1; } } // ELSE else { // Move the date back one day date[0]--; // Move to end of prior month if necessary if (1 > date[0]) { date[1]--; date[0] = 31; } // Move to end of prior year if necessary if (1 > date[1]) { date[2]--; date[1] = 12; } } }
At this point, we can test out whether or not this basic approach of walking one date toward the other one day at a time is too slow by simply changing the dates to the extremes of our data range, which I'm going to assume (based on what the TS seemed to be using) is from 1/1/0000 to 31/12/9999.Code:Enter a date: 1 Enter a date: 2 23/11/1887 is -37200 days after 23/11/1987. Enter a date: 2 Enter a date: 1 23/11/1987 is 37200 days after 23/11/1887.
There was no discernable delay (on my machine) at all between entering the second date and the result being displayed, so we have a perfectly viable approach. If we want to get more elegant later, we certainly can, but the first priority should always be to get something that works and works correctly. Remember, if it's stupid but works, it ain't stupid.Code:Enter a date: 1 Enter a date: 2 31/12/9999 is 3719999 days after 01/01/0000.
The next step is to make our AdjustDate() function aware of how many days are actually in a month, ignoring leap years.
Notice that this change is extremely minor at this level of abstraction, involving only replacing the hardcoded 31 (the prior days in a month) with a call to the function DaysInMonth() that is expected to return the actual number of days in a month.
Now we need to get DaysInMonth() working for the case where leap years are ignored.Code:void AdjustDate(int date[], int plusMinus) { // IF plusMinus is positive if (1 == plusMinus) { // Move the date forward one day date[0]++; // Move to start of following month if necessary if (DaysInMonth(date) < date[0]) { date[1]++; date[0] = 1; } // Move to start of following year if necessary if (12 < date[1]) { date[2]++; date[1] = 1; } } // ELSE else { // Move the date back one day date[0]--; // Move to end of prior month if necessary if (1 > date[0]) { date[1]--; date[0] = DaysInMonth(date); } // Move to end of prior year if necessary if (1 > date[1]) { date[2]--; date[1] = 12; } } }
A subtle point here is that the date that we pass to DaysInMonth() has been modified by either incrementing it or decrementing it, which means that it could be 0 (or as high as 32). Also, when we call it to set the day to the last day of the prior month, the month has been decremented always, so it could be 0. We need to be sure that we allow and account for this.
Running this program, we now expect our 10,000 year span to return 1 day fewer than ±3650000 days, which it does.Code:int DaysInMonth(int date[]) { static int daysInMonth[] = {31,28,31,30,31,30,31,31,30,31,30,31}; // Return the number of days in the month with wraparound // (date[1]-1) accounts for array index being zero-based // (date[1]-1)+12 moves negative months to positive for rem to be mod // ((date[1]-1)+12)%12 maps values outside array bounds back in return daysInMonth[((date[1]-1)+12)%12]; }
The last thing we need to do (other than modifying GetDate() actually get and validate a date from the User) is to account for leap years.
The obvious place to do this is in the DaysInMonth() function. At this level of abstraction, we just need to know if the current year is a leap year and, if so, is the current month February. If so, we simply add 1 to the number of days that gets returned.
Now we need to implement the IsLeapYear() function that is being called. This function falls immediately from the logic of what years are leap years.Code:int DaysInMonth(int date[]) { static int daysInMonth[] = {31,28,31,30,31,30,31,31,30,31,30,31}; // SET leapDay to 1 if the date is in February of a leap year (0 otherwise) int leapDay = (2 == date[1]) && IsLeapYear(date)); // Return the number of days in the month with wraparound // (date[1]-1) accounts for array index being zero-based // (date[1]-1)+12 moves negative months to positive for rem to be mod // ((date[1]-1)+12)%12 maps values outside array bounds back in return daysInMonth[((date[1]-1)+12)%12] + leapDay; }
At this point, our new IsValidDate() function can simply always return 1. This allows use to verify that we are processing valid dates correctly and haven't made a simply mistake like getting the array elements in the wrong order. Plus, at the level of this function, our pseudocode description would be something like:Code:int IsLeapYear(int date[]) { int year = date[2]; // Years divisible by 400 ARE leap years if (0 == year%400) return 0; // All other century years are NOT leap years if (0 == year%100) return 0; // All other years divisible by 4 ARE leap years return (0 == year%4); } [/QUOTE] If all years divisible by four were leap years, we would expect this to add 2500 days to our extreme span. Since there are 100 century years, this would be reduced to 2400 by the century-year rule, but the 400-year rule would add back 25 of them. Hence, over 10,000 years, there should be 2425 leap days. But, when I ran it, I got 3652399 days, which is an additional 2400 days. This implies that the issue is related to the 400-day rule. Sure enough, my IsLeapYear() function is returning 0 in this case when it should be returning 1. This underscores the value in incremental development and testing. Implement small pieces of code and then perform a meaningful test on them -- don't just run them and call it good if it doesn't crash. If it doesn't produce the expected output, the most likely culprit is going to be the small amount of code you just implemented, which immediately allows you to focus your attention on the correctness of that small piece of code. Finally, we can turn our attention to getting actual dates from the user and validating them. First, we modify our GetDate() function as follows: [CODE] void GetDate(int date[]) { int temp; printf("Enter a date in the forma DD/MM/YYYY: "); scanf("%d/%d/%d", &date[0], &date[1], &date[2]); if (!IsValidDate(date)) { printf("ABORT: Date Entered is not valid.\n"); exit(1); } }
// Get a date from the User
// IF the date is not valid, abort the program
At this level, we don't care HOW we determine whether the date is valid. That's deeper in the weeds than we should be thinking at this level, so leave that task to another function.
Note that, to use the exit() function, we need to include <stdlib.h>.
After we are satisfied that we can accept valid dates successfully, we can implement our full IsValidDate() function very easily. Again, note that our code is not too far down in the weeds. We have a function that tells us how many days are in a month, so we use it. This did require me to move the DaysInMonth() and IsLeapYear() functions to the top of the file.
This incremental development process might seem like it would be slow and inefficient, but it's not. You will quickly find that it allows you to develop your program significantly faster that the usual approach that new programmers take in which they try to write the entire program before they start testing it.Code:int IsValidDate(int date[]) { // Year must be between 1 and 9999 (inclusive) if ( !( (1 <= date[2]) && (date[2] <= 9999) ) ) return 0; // Month must be between 1 and 12 (inclusive) if ( !( (1 <= date[1]) && (date[1] <= 12) ) ) return 0; // Day must be between 1 and 12 (inclusive) if ( !( (1 <= date[0]) && (date[1] <= DaysInMonth(date)) ) ) return 0; // If no tests above failed, the date is valid return 1; }
For instance, from the time I sat down and opened a blank source code file until now, which includes all the time spent writing this post, was barely over two hours -- and the vast majority of that time was writing this post!
Trial and error has its uses, but the focus should be on taking a step back and figuring out exactly what you want to accomplish and what needs to happen to accomplish it. Most problems can be broken down into a relatively small handful of smaller problems that can be tackled more or less independently. Each of those problems can the be broken down further, as needed, until you get down to a point where you can say, "Yep, I know how to make that happen."Thank you so much for taking your precious time and teaching me how to breakdown huge codes like this. I somehow managed to get my codes working through spending a huge load of time on trial and error, and i submitted it just a few days ago. I loved your idea of making psuedo codes and i will definitely give it a try for my next coursework. The way you wrote your code made sense to me.
The only difference is that a void function is a function that doesn't return any value at all, while a non-void function is one that does. Don't make it any harder than it is.However, i do not get the difference between void functions and normal functions,
What the return value means depends entirely on what the programmer intends it to mean. If you call the sqrt() function, the return value is the square root of the value that was passed to it as an argument when it was called. If you call the strlen() function, the value that is returned is the number of characters in the string (NUL-terminated character array) that was passed to it. If you need your function to return a true/false result, the 0 is how we represent a FALSE in C, while ANY non-zero value is considered to be TRUE. But YOU decide what a function's return value means, and then you must be sure that you USE that value in a manner consistent with what you decided.i dont understand the use of return at the end of the function. From what i understand, i know that return 1 means true and return 0 means false, for example,
The value returned by DaysBetween represents the number of days by which the second date follows the first. But I wanted to be able to call the function before I tackled how to go about actually figuring out how many days separate two dates. So I "stubbed out" the function by hardcoding it to return a value that was valid, though almost certainly wrong. I just picked 42 out of a hat (well, not quite) as my temporary return value to use until I got around to doing something better.in your code you used return 42 in the DaysBetween() function, and i dont understand what that does, i also know that return can be used to return a value from the function like you did in DaysInMonth() function.
I'm not at all sure what you do and don't understand on this point. You declare parameters for your function and when the function is called, you pass arguments that are used to set the values in those parameters. That's pretty much it. Don't make it harder than it is.Additionally, function parameters is another concept i couldnt fully grasp. I know the complete basic example of function parameters where you can set a parameter in a function, then assign a value to that parameter when calling that function, but for a more difficult code i do not understand the parameters at all.
YESSSSS, the year 2000 was unusual because it was a multiple of 400! ! !you can run it and see if it produces desired results.
maybe start with something like:
Code:#include <stdio.h> // Function to check if a year is a leap year int isLeap(int year) { // Leap year occurs on every year that is exactly divisible by 4, // except for years that are exactly divisible by 100, // but these centurial years are leap years if they are exactly divisible by 400. if (year % 400 == 0) { return 1; } else if (year % 100 == 0) { return 0; } else if (year % 4 == 0) { return 1; } else { return 0; } } // Function to calculate the number of days since the beginning of the year int days_since_beginning_of_year(int day, int month, int year) { // Array of cumulative days before each month in a non-leap year (0-indexed for Jan-Dec) int days_before_month[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; int day_of_year = days_before_month[month - 1] + (day - 1); // If the date is after February and it is a leap year, add an extra day if (month > 2 && isLeap(year)) { day_of_year += 1; } return day_of_year + 1; // Add 1 because Jan 1st is day 1, not day 0 } int main() { int day, month, year, days_elapsed; // Example 1: Non-leap year day = 15; month = 3; // March year = 2023; days_elapsed = days_since_beginning_of_year(day, month, year); printf("Days since beginning of year %d/%d/%d: %d\n", month, day, year, days_elapsed); // Example 2: Leap year (February 29th) day = 29; month = 2; // February year = 2024; days_elapsed = days_since_beginning_of_year(day, month, year); printf("Days since beginning of year %d/%d/%d: %d\n", month, day, year, days_elapsed); // Example 3: Leap year date after Feb day = 1; month = 3; // March year = 2024; days_elapsed = days_since_beginning_of_year(day, month, year); printf("Days since beginning of year %d/%d/%d: %d\n", month, day, year, days_elapsed); return 0; }
It created an interesting situation in that if you were totally naive of anything beyond the "divisible by 4 rule" (which is a very large fraction of people), you got it right, while people that were aware of the "divisible by 100" rule", but not the "divisible by 400" rule (which is a large fraction of the remaining people), you got it wrong. It was basically a case of, sometimes, two wrongs do make a right.YESSSSS, the year 2000 was unusual because it was a multiple of 400! ! !