#include <stdio.h>
int main(){
int num1, num2;
char op;
float answer;
printf("This is a simple calculator. Input '0q0' to quit.\n");
printf("Enter the arithmatic operation ('num1''op''num2'): ");
scanf("%d%c%d", &num1, &op, &num2);
switch(op){
case '+' : answer = num1 + num2;
break;
case '-' : answer = num1 - num2;
break;
case '*' : answer = num1 * num2;
break;
case '/' : answer = (float)num1 / num2;
break;
case 'q' : return 0;
break;
default: printf("Invalid Operand");
break;
}
printf("Answer is : %.2f", answer);
fflush(stdin);
main();
}
This is my code. Instead of using a while loop, I called main() function again at the end to make the program loop. Is this a good practice or a bad practice?
Once the program executes it's first round, main() is called again. So when it happens, will it use the old variables 'num1', 'num2', 'op' or does the program create new variables for the next round while the old variables are still there?
I wanted to use this method for one of my college projects but if duplicate variables are created every loop, it'll be a disaster. Cause I need to use about 200 - 500 structures with 13 variables in each.
Is this a good practice or a bad practice?
Using recursion for something that can be easily solved with a loop is bad. If your implementation uses a stack (most do), you'll put more on the stack for each call and because the stack has a limited size, there's a limit to how deep the recursion can go before it runs out of stack space and most probably crashes.
will it use the old variables 'num1', 'num2', 'op' or does the program create new variables for the next round while the old variables are still there?
It will create a new set of variables for each call and the old ones are still there so to speak.
I wanted to use this method for one of my college projects but if duplicate variables are created every loop, it'll be a disaster.
Then, it's a disaster. Use a loop:
int main(void) {
for(;;) {
// your current code except the call to `main()`
}
}
Notes:
fflush(stdin); is only defined to work in some implementations. In other implementations it'll have undefined behavior.
Always check the result of your scanfs.
if (scanf("%d%c%d", &num1, &op, &num2) == 3) {/* ok input */}
else {/* erroneous input */}
It is bad practice, as it is not recommended to do it this way.
Try and place your code in a function on it's own and call the function from main()
With recursion, usually, a lot of memory get used since variables will get allocated for each function call, and they will get released only when the function ends it's execution.
Related
I want to write a function that will take two integers, three times, and then return them ordered by the first integer and (for now) print them in main (though eventually I plan/hope to switch to a file-based structure to store and organize data), but I think I might have an issue with my pointers cause even when I skip concatenations (which looks like might also be another separate issue), everything Ive tried has main print a string (or no string) which never matches the input, but the print statements suggest all the looped assignments are working properly.
#include <stdio.h>
#include <string.h>
const char * entry()
{
int n;
int level;
char habit1entry[6];
char habit2entry[6];
char habit3entry[6];
for (int c = 0; c< 3; c++){
printf("Habit #\n");
scanf("%d", &n);
printf("Level:\n");
scanf("%d", &level);
switch (n)
{
case 1:;
sprintf(habit1entry, "|%d|%d|\n", n,level);
printf("n = %d\n",n);
printf("%s\n",habit1entry);
continue;
case 2:;
sprintf(habit2entry, "|%d|%d|\n", n,level);
printf("n = %d\n",n);
printf("%s\n",habit2entry);
continue;
case 3:;
sprintf(habit3entry, "|%d|%d|\n", n,level);
printf("n = %d\n",n);
printf("%s\n",habit3entry);
continue;
}
}
strcat(habit2entry,habit3entry);
printf("%s\n",habit2entry);
strcat(habit1entry,habit2entry);
printf("%s\n",habit1entry);
char *fullEntry=habit3entry;
printf("%s\n",fullEntry);
return strdup(&fullEntry[0]);
}
int main(){
const char * dataEntry = entry();
//strcpy(dataEntry,entry());
printf("Data:\n%s",dataEntry);
}
heres an example of the output(after the correct prints inside the switch cases) for an input of 3 2 1 1 2 2:
"
|2|2|
|1|1|
|2|2|
|2|2|
|��
|2|2|
|��
* stack smashing detected *: ./a.out terminated
Aborted (core dumped) "
p.s. Sorry if this all sounds silly, this is my first C project (and first real stack overflow post, plz b gentl) coming from jumping around between java, python and clojure and I would like to take an operating systems class that allows you to start without knowing C but expects you to pick it up on your own and its hard finding material that explains C concepts in a scope that matches my background knowledge and current learning constraints in terms of time available for taking deep dives through explanations that for me have ended up mostly being either hopelessly esoteric, incredibly case-specific or overly-simplistic/redundant/unhelpful explanations of programming concepts I picked up in other languages. Dont mean to complain or harp on and its probably good to get practice with different methods of asking questions and finding answers for problems like these, but the learning curve for understanding things like this (setting up the compiler/json files involved spending hours only to discover that mcafee was deleting my exes which I became convinced was a symptom of a virus, only to have the behavior stop after I restarted for a minor routine windows update and I have no idea why) outside of a traditional framework sometimes seems more like a wall and I'm worried that maybe I should revise my approach to avoid wasting too much of my time banging my head against a series of very sturdy walls. any and all advice is greatly appreciated.
Abstracting form the logic of the program, you have plenty of issues there:
You do not provide enough space for the strings
Your switch is not very related to your for loop
Names of the variables do not matter for you - but they matter for the program . Be more careful.
probably more but I forgot already
#include <stdio.h>
#include <string.h>
const char * entry()
{
int n;
int level;
char habit1entry[21] = "";
char habit2entry[14] = "";
char habit3entry[7] = "";
for (int c = 1; c < 4; c++){
printf("Habit #\n");
scanf("%d", &n);
printf("Level:\n");
scanf("%d", &level);
switch (c)
{
case 1:;
sprintf(habit1entry, "|%d|%d|\n", n,level % 10);
printf("n = %d\n",n);
printf("He1: %s\n",habit1entry);
continue;
case 2:;
sprintf(habit2entry, "|%d|%d|\n", n,level % 10);
printf("n = %d\n",n);
printf("He2 = %s\n",habit2entry);
continue;
case 3:;
sprintf(habit3entry, "|%d|%d|\n", n,level % 10);
printf("n = %d\n",n);
printf("He3 = %s\n",habit3entry);
continue;
}
}
strcat(habit2entry,habit3entry);
printf("H2 + H3 = %s\n",habit2entry);
strcat(habit1entry,habit2entry);
printf("H1 + H2 = %s\n",habit1entry);
char *fullEntry=habit1entry;
printf("FE: %s\n",fullEntry);
return strdup(fullEntry);
}
int main(){
const char * dataEntry = entry();
//strcpy(dataEntry,entry());
printf("Data:\n%s",dataEntry);
}
Welcome to the weird and wonderful world of C.
I have not actually compiled and run your program yet, just had a quick read through and though I give you my first thoughts.
The way your program is written is primed to generate stack overflows. You have three (very little) character arrays defined on the stack habitxentry, so your sprintf's will most certainly blow your stack unless both the Habit and Level inputs are less than 10. Habit is alright because your switch only allows 1, 2 or 3. Your switch does nothing if Habit is anything else.
As a side note: sprintf is not really the function to use in our security minded world. snprintf is a better choice. Not really an issue here per se as you are not passing in user supplied data but still, it's not a good habit to cultivate.
Next you strcat your character arrays together, virtually guarantying a stack violation, but lets assume this works; you are concatenating 2 and 3 into habit2entry and then 1 and 2 into habit1entry.
Next you are creating a pointer to habit3entry (not habit1entry) and returning a duplicate.
By doing so you are allocating heap in a mildy obscure manner. The callee will be responsible for freeing this memory.
I always preferred to explicitly malloc the memory and then strcpy (or memcpy) the data in.
Now when you grep your code, you only have to look for malloc.
Also, someone using the function will notice the malloc, see you have returned to pointer and realize that freeing it will now be his problem.
In order to avoid these problems some programmers leave it to the caller to supply a buffer to the function. The reasoning is that a function is supposed to do one thing and one thing only. In this case you are doing two things, you allocate memory and you fill that memory.
In your switch statement I noticed that each of your case labels are followed by an empty statement.
the semicolon at the end of that line is not necessary: write "case 1:" not "case 1:;"
You also use continue at the end of each block. This is allowed but "break" is more appropriate.
In this case it will have the same effect but normally you have more statements after the switch.
Now the difference will become apparent. Continue will jump straight to the top of the loop, break will break out of the switch and continue executing there.
Hope this gives you some insight.
Good luck.
I am in process of rewriting my CS50 credit solution to use functions.
Stumbled upon error when defining readCardNumber().
long long readCardNumber()
{
do
{
long long result = get_long_long("Enter card number to verify: \n");
if (result < 0)
{
printf("Retry: \n");
}
}
while (result < 0);
return result;
}
I am using the CS50.h https://reference.cs50.net/cs50/get_long_long to get number. I cannot compile this solution because of :
error: use of undeclared identifier 'result'
Can someone experienced please explain whats the issue here? I did declare function at beginning of my code and declared and initialised result in function.
What would be a better way to validate that number?
https://docs.cs50.net/2018/x/psets/1/credit/credit.html - Spec for solution I am trying to rework.
The result variable is declared inside of the do...while block. It is not visible outside of the block, which includes the condition of the while.
You need to move the variable definition outside of the loop:
long long result;
do
{
result = get_long_long("Enter card number to verify: \n");
if (result < 0)
{
printf("Retry: \n");
}
}
while (result < 0);
It is a matter of scopes. In C, a scope is defined by { and }. A variable ceases to exist at end of the scope it is declared within. (Well, static variables don't but they get unaccessible. Unless you have pointers to them.)
What you need to do is to move the declaration outside the loop.
I do however want to emphasize that it is a VERY good practice to declare variables within the scope that they are used. It reduces the risk of bugs significantly. If a variable is only used inside a loop, then declare it inside the loop. It is basically the same reason as why you should not use globals unless necessary.
You do return result; when result is out of scope. But your code contains redundancy: you test result < 0 twice. I would recommend changing the structure to avoid this, with bonus side-effect of fixing the original problem:
long long readCardNumber(void)
{
for (;;)
{
long long result = get_long_long("Enter card number to verify: \n");
if (result >= 0)
return result;
printf("Retry: \n");
}
}
This is one of those inconvenient or even "unnatural" things about do/while statement: everything you declare inside the cycle body will not be visible to the condition in the while portion of the statement. The scope of those identifiers ends at the } before the while portion.
This often forces the user to declare the variable(s) before the cycle
long long result;
do
{
result = get_long_long("Enter card number to verify: \n");
if (result < 0)
printf("Retry: \n");
}
while (result < 0);
The price to pay for this solution is
unnecessary extension of result's scope beyond the cycle
it is no longer possible to meaningfully initialize the variable at the point of declaration
For many of us this is so unpleasant that we prefer to switch to the do/while(1) approach with break in the middle to terminate the cycle
do
{
long long result = get_long_long("Enter card number to verify: \n");
if (result >= 0)
break;
printf("Retry: \n");
}
while (1);
This is also not perfect, obviously. Chose for yourself, which approach you like better.
In the latter case do/while(1) serves as an "idiomatic" way to express a cycle with exit from the middle. Other people might prefer for (;;) or while(1) for that purpose, thus avoiding do/while statement altogether.
how is your day :),
Take a look at the below program, the program written below is to calculate the sum of first n natural numbers, the problem is that i get the sum of n-1 natural numbers, can anybody explain why ?
and can anybody also explain why a-- instead of --a.
#include<stdio.h>
main()
{
int a,sum;
printf("Enter a number.");
scanf("%d",&a);
sum=sumnat(a);
printf("Sum of the first %d natural numbers is %d.",a,sum);
}
sumnat(a)
{
int b;
if(a==0)
{
return 0;
}
else
{
b=a+sumnat(--a);
return(b);
}
}
There were several errors, the greatest of which was undefined behaviour in the expression which uses a and also a modified value of a. You should also define your function properly, not rely on default values provided by the compiler.
#include <stdio.h>
int sumnat(int a); // function prototype
int main(void) // correct signature
{
int a, sum;
printf("Enter a number. ");
scanf("%d", &a);
sum = sumnat(a);
printf("Sum of the first %d natural numbers is %d.", a, sum);
return 0;
}
int sumnat(int a) // function has a return type and argument type
{
if(a == 0)
{
return 0;
}
return a + sumnat(a - 1); // there was no need to decrement `a`
}
Program session
Enter a number. 5
Sum of the first 5 natural numbers is 15.
Your program works for me, using gcc on Mac OSX. However, it will not work everywhere, because of this line:
b=a+sumnat(--a);
--a decrements a, but if it does so before the addition, then your result will be wrong. I'm not sure C is required to evaluate expressions strictly left-to-right (I don't think it is). At any rate, since you don't use a after that line, you could fix things this way:
b=a+sumnat(a-1);
As #self says, you should fix the program to handle negative values, and it would be a good idea to consider what is the largest natural number whose sum you can compute this way (and why that is).
There is a difference between them. One first subracts from a and than goes in the function while the other frist goes in... so it never gets subracted and you go to inifinit stack.
"and can anybody also explain why a-- instead of --a"
When you use the prefix operator --a the decrease is done before anything else, while the postfix operator a-- happens after the rest of the expression is resolved, so, lets say, while debugging your code, in a particular moment, a = 5
since the line
b=a+sumnat(--a);
is using the prefix version of the operator, the decrement would happen immediately, making a=4 and then the function sumnat would be called with argument 4
b=a+sumnat(a--);
in this case the postfix operator is being used, so first the function sumnat would be called with the argument 5, since that is the value of a in that moment, then, only when the function returns a value (which would never happen in your example, since it would be called multiple times with the same value, never reaching 0) the decrement would happen
This question already has answers here:
Why does scanf get stuck in an infinite loop on invalid input? [duplicate]
(2 answers)
Closed 8 years ago.
This is a bit of a long post so bear with me. The gist of what I am trying to do is get some user input at the beginning of a loop, do some things depending on that input and possibly repeat. Simple enough right? Here is what I have.
void displayMain(Album albums[], int num_albums){
int break_condition = 1;
int user_choice;
//keep looping until we decide to quit
while (break_condition){
user_choice = displayMenu();
switch(user_choice){
case 0 :
return; //does this need to be return 0 or can it just be return (or is it the same thing?)
case 1 :
displayAlbums(albums, num_albums);
break;
case 2 :
printf("display tracks\n");
break;
default :
printf("Invalid choice!\n\n");
break;
}
}
return;
}
The function displayMenu() basically grabs some user input and returns an int.
int displayMenu(void){
printf("Display Menu\n1. Display all albums\n2. Display the tracks in an album\n0. Return to the mainmenu\nPlease enter the choice:\n");
//grab and return the choice
int choice;
scanf("%d", &choice);
return choice;
}
So when I finally run all of this code, if I enter a "bad" choice, ie: one that takes the default path in the switch statement, the code enters an infinite loop. Repeating the contents
Display Menu
1. Display all albums
2. Display the tracks in an album
0. Return to the main menu
Please enter the choice:
Invalid choice!
I am pretty much a newb at C, so I believe I know why this is. user_choice is not ever being reset, even though the function displayMenu is clearly being called (because of the infinite loop output)
Why isn't displayMenu asking the user for input anymore after one instance of "bad" input is entered? Is it because it isn't an integer? When I tried debugging and printing out user_choice, it was some large number, so likely a register or something like that.
Please help, as I said, I suck at C, so an experienced C programmer could probably lend a hand :)
Running your code in Visual Studio 2012 (in the Cygwin Terminal executing it as a .c extension that I also tested , it works just fine) the only error that I got was that I should use the safe version of scanf( scanf_s ) instead.
If you don't want doing that the whole time when you already have declared more than one scanf in your code and the specific error pops up, you could add to your preprocessor definitions the _CRT_NO_SECURE_WARNINGS sentence . Still, I don't know the exact implications of using
the scanf_s over scanf and the opposite and how it affects the input of a program.
[ Right Click on your project--> Properties--> C/C++ -->Preprocessor Definitions and you add the sentence . ]
All the above I guess only apply if your IDE is Visual Studio and that is your case.
As for the return statement in case 0. This will force the loop to terminate and thus, it will exit the function . You can use the break statement to end processing of a particular case within the switch statement and to branch to the end of the switch instead, so the loop can be continued and display your menu again.
Returning a 0 value type will not match your function type ( as long as you have it declared as void ).
This question already has answers here:
Variable definition inside switch statement
(5 answers)
Closed 5 years ago.
Code:
int main()
{
int a=1;
switch(a)
{
int b=20;
case 1:
printf("b is %d\n",b);
break;
default:
printf("b is %d\n",b);
break;
}
return 0;
}
Output:
It prints some garbage value for b
when does the declaration of b takes place here
Why b is not initialized with 20 here???
Because memory will be allocated for int b but when the application is run "b = 20" will never be evaluated.
This is because your switch-statement will jump down to either case 1:1 or default:, skipping the statement in question - thus b will be uninitialized and undefined behavior is invoked.
The following two questions (with their accepted answers) will be of even further aid in your quest searching for answers:
How can a variable be used when it's definition is bypassed? 2
Why can't variables be declared in a switch statement?
Turning your compiler warnings/errors to a higher level will hopefully provide you with this information when trying to compile your source.
Below is what gcc says about the matter;
foo.cpp:6:10: error: jump to case label [-fpermissive]
foo.cpp:5:9: error: crosses initialization of 'int b'
1 since int a will always be 1 (one) it will always jump here.
2 most relevant out of the two links, answered by me.
Switch statements only evaluate portions of the code inside them, and you can't put code at the top and expect it to get evaluated by every case component. You need to put the b initialization higher in the program above the switch statement. If you really need to do it locally, do it in a separate set of braces:
Code:
int main()
{
int a=1;
/* other stuff */
{
int b=20;
switch(a)
{
case 1:
printf("b is %d\n",b);
break;
default:
printf("b is %d\n",b);
break;
}
}
/* other stuff... */
return 0;
}
The switch directly jumps to case 1:, never executing the assignment.
Presumably because switch functions like a goto - if a == 1, it jumps straight to case 1: and bypasses initialization of b.
That is: I know switch jumps straight to the case label, but I'm very surprised the compiler doesn't complain about the missed initialization.
It's a pretty bad idea to initialize B under the switch statement and outside a case statement. To understand what's going on here, you have to know that the switch makes a jump to the correct case/default statement.
because when switch(a) statement executes control goes directly to the statement case 1: without executing statement int b=20, taht's why it gives garbage value as answer. If u want to print a then either u have to initialise in case 1: block or u have to initialise to before switch(a) statement.
Because that line is never reached. When C hits a switch(a) statement, it branches to the case that matches the condition of the variable you're switching on. The statement that initialises b isn't in any of the cases. I suppose the compiler is free to write 20 into the location, but the language doesn't require it to do so and in this case it doesn't: it's also free to leave initialisation until it actually executes an assignment.