Use gets on a stack buffer, but don't enable stack canaries and make the stack both writable and executable. Your users will thank you.
comments
you could also use getchar() to only take the first char out of the buffer, if you didn't know already ;)
Rust made the same mistake that C++ made, otherwise it might've been a viable option. Now instead debugging a memory issue for 3 days, I think a month on how to architect my software, just to scrap that 5 months later and rewrite it from scratch.
Thanks very much! I had no idea there is such a thing as "unbuffered I/O". I'll look into it. :)
Thanks! I'll look 'em up! :)
I'm too lazy to confirm, but I think scanf (as well as many other I/O functions) works on top of the buffered input. I.e. when you scanned only one character on the first run of scanf but there are more left in the buffer, then subsequent calls will read from that buffer, e.g.
printf("your prompt>")
/* you type 123 */
/* buffer contains: "123" */
scanf(...)
/* you scanned 1 char ("1") */
/* buffer contains: "23" */
printf("new prompt>");
/* the prompt has been printed AFTER your previous input but the buffer hasn't been reset! */
/* buffer contains: "23" */
/* you type 456 */
/* buffer contains: "23456" */
scanf(...)
/* scanf returns "2" */
/* buffer contains: "3456" */
In general you have two options:
- drain the buffer after / before each loop cycle
- use unbuffered I/O
For both cases, do your research on how to do it :) That K&R from above should be a great start indeed.
read #1.5 and #7: K&R
or just find examples of getchar() or fgets()
At some level of abstraction, the end of an infinite loop is just a point where the program counter will never move past. In hardware, this is a kind of branch instruction causing the program counter register to point back to the top of the loop.
Metaphysically, best not to think about it too hard.
Thanks! I'll look into it! ๐
You could do a do while loop on the condition of != \n.
Thanks! I'll look into getline() tomorrow! ๐๐ฉต
I'm going to avoid commenting on anything else in your code and just say that you should be using getline() to read a line of input from the user. Also note that yor extra call to getchar() is throwing away the user's first character of input after a command is executed.
If you moved the printfs back, you'd end up with 6 printfs, and you can just hardcode in the wasiswill value in the format string.
As it happens, the strcpys are unnecessary anyway, you can just assign "will be" to a char pointer:
char* williswas = "";
if (year == 2026) {
williswas = "is";
}
...
The strings are already in your read only memory, you just need a pointer to them.
Making the GOTO targets unique makes a stronger argument for just dropping the gotos entirely, because the execution flow for each of the 6 cases is now getting intertwined. You may understand it today, but in 3 months time you'll hate reading it.
Thank you very much for your input! ๐ How clumsy of me not to make the GOTO targets unique... Then I'll reverse the code to what I had initially written, namely, exactly what you said.
Could you clarify what you mean by "save the strcpys"?
Cool insight! Also, this reminds me, where I work, we have a piece of really old, probably early 2000's, software with which we book and document interviews with our clients (convicts on parole, infact). When I once accidentally tried to enter a future date as the date of an interview, it gave me an error message saying "you can only book interviews between 1753 and present day". After looking into it, I found that the Gregorian calendar, in my country, was officially adopted as late as 1753 because of religious opposition. I laughed so hard at this, still wondering if this was left there as an Easter egg by a developer or if it's using a library that behaves this way.
This is not a good way to use GOTO :(.
For starters: Your GOTO targets aren't unique, NLEAP1 and NLEAP2 are the same, and LEAP and NLEAP3 are the same.
I would probably just put the printfs where the gotos are and save the strcpys. Just make sure to return out after the printfs. Sometimes being verbose in your code is better than being clever with gotos.
Your returns are returning the return value from the printfs.
I.e: same as:
int ret = printf(...);
return ret;
Printfs returns an int with the number of bytes it wrote out.
I honestly kinda love date/time manipulation stuff while coding because it's very nature is reliant on exceptions and edge cases. While it can be annoying it is far from boring especially if your day to day is just managing ledgers or APIs. Even with my odd endeavor into date/time shenanigans though it's almost always better to use a library as the complexity will quickly grow out of scope.
I don't think that you were off point at all by pointing out the factual errors by initializing at 0. I appreciate it as much as your advice on the code itself! This is just my lack of common sense and me sucking at math.
Thank you very much for the input on how to tidy up the code! I'll give it a shot! ๐
You can do the ordinal comparison once at the start instead of for both the positive and negative case. Then track a isLeap variable and just do one printf at the end with the combination of your different variables.
Your code feels repetitive now because it is. You're comparing years multiple times and printing "[not?] a leap year" multiple times.
The reason returning printf works is because the return type of printf is an int containing the count of characters printed.
I recognize that this is besides the point, but It doesn't make a ton of sense to attribute a Gregorian interpretation to years before the adoption of the Gregorian system, and thus to truly be honest in the response different leap rules ought to be used for pre-Gregorian years. In any case this will also return the wrong result for 0 or negative years as there aren't negative years or a year 0.
It's in case you have to change those values later, you will probably miss one instance and get issues down the line. It also makes it more readable for other people (or yourself in a few months lol)
Cool! Thanks! I did read somewhere that if I'm certain that a variable isn't going to have to change its value throughout the program, then it's better to make it a constant. Out of security concerns? Anyway, thank you so much! ๐ฅฐ
Did you know that
int history[10] = {0,0,0,0,0,0,0,0,0,0};
is the same thing as
int history[10] = {0};
And an idom in C would be to define a constant for the size like this
enum { HISTORY_MAX = 10};
int history[HISTORY_MAX] = {0};
int history_limit = HISTORY_MAX;
scanf gives me shivers. Please use -Werror=format compiler flag to catch dumb errors that will make your code crash in mysterious ways.
That's fair enough, and a good way to learn. Still, in this day and age with Claude etc, I respect the effort you're putting in.
In the interests of proposing alternatives, you could also make the pin list a global variable. I'll let you work out why its a bad idea though :)
Thanks for the pointers. ๐
Oh, I just meant that I'm experimenting, evaluating and thinking instead of "just" searching for a straight answer on the world wide web.
Edit: Also meaning, that when I post a question here, I have already writhen (spelling?) in pain for a few hours or a day without getting anywhere.