I don't know much about modularity except it's basically dividing up your code into smaller groups of functions (this question is for the C language).
I'm doing an assignment where I can only see my source code working with one giant nested loop in "main" (no if statements are allowed for the assignment). In other words, if I try and use functions to cut up the code, I don't see how the necessary nested loop will work. But the assignment requires an attempt at modularity.
Thus, can anyone tell me how I might break up the following code into smaller modules without messing up its actual execution?
#include <stdio.h>
int main(void)
{
int counter = 0;
int marknum = 0;
int sectioncode, studentnumber;
int dummyvariable = 0;
int index;
int marks;
int total = 0;
do
{
printf("Enter a Section Code: ");
scanf("%d", §ioncode);
while(sectioncode > 4 || sectioncode < 1)
{
printf("Invalid value entered. Must be 1 to 4, please re-enter: ");
scanf("%d", §ioncode);
}
do
{
printf("Enter the Student's ID: ");
scanf("%d", &studentnumber);
while (studentnumber < 1 || studentnumber > 999999999)
{
printf("Invalid value entered. Must be 1 to 999999999. Please re-enter: ");
scanf("%d", &studentnumber);
}
while (sectioncode != 0)
{
while (counter < 5)
{
counter++;
marknum++;
printf("Enter mark%d: ", marknum);
scanf("%d", &marks);
total = total + marks;
}
printf("%09d's total mark is %d\n", studentnumber, total);
counter = 0;
marknum = 0;
sectioncode = 0;
}
dummyvariable = 1;
} while (dummyvariable = 0);
} while (sectioncode != 0);
return 0;
}
Also, how would I incorporate modularity for this one (same question basically):
#include <stdio.h>
int main(void)
{
int num; //User inputted number
int i; //Increment variable
char ch; //Check for characters variable
do //Begin "do while" loop
{
printf("\nEnter a number:"); //User prompt
scanf ("%d", &num); //Scan for user inputted integer
while ( (ch = getchar()) != '\n') //Scan for character, check for non-numeric input
{
printf("Invalid number entered. Please re-enter: "); //Error message and prompt for invalid user input
scanf ("%d", &num); //Scan for user inputted integer
} //Repeat loop if condition becomes true again
for (i=0; i<num; i++) //Begin "for" loop; condition prints asterisks equal to user number; increment i by 1
{
printf("*"); //Prints a single asterisk each loop until i is less than num
}
} while (num!=0); //Stop "do while" loop if user enters 0
return 0;
}
Normally I'd suggest that you ask your instructor instead of asking homework questions here, but as Daniel points out in the comments, the use of loops and extra variables just to avoid having if statements in the code is stupid, and I'm not sure telling you to get advice from an instructor who thought that was a good idea would be entirely responsible behavior on my part. So, having said that:
What you want to look for in cases like this is multiple chunks of similar code, or chunks of code that conceptually do a single thing. Then see if you can split those chunks out into a function.
In the first example, you display a prompt, read user input, and verify the input for both sectioncode and studentnumber. That process could be split into a separate function. (Everything from printf("Enter ...") through the end of the while loop.) Then in the main function, you just have something like
sectioncode = readval("Enter a Section Code: ", 1, 4);
studentnumber = readval("Enter the Student's ID: ", 1, 999999999);
For the second example, that input/validation code isn't duplicated, but it's still probably worth splitting out into a function, since it does a single well-defined thing, and spans enough lines that splitting it out into a function might help make the logic of the remaining code more clear. But it's less important in this case than in the first.
Also, an unrelated issue: At the end of one of the do-whiles, you have while (dummyvariable = 0);. Note the single equal sign. You're assigning 0 to dummyvariable, not comparing.
In this particular case, it works anyway, since the whole expression evaluates to 0 (i.e. false), just like (dummyvariable == 0) would have. But if that constant had been anything else, or if you hadn't just set dummyvariable to 1 prior to the end of loop, that'd be a bug.
I strongly recommend always putting the constant on the left hand side of expressions like that in order to catch bugs of that sort at compilation. while (dummyvariable = 0) silently does something unexpected; while (0 = dummyvariable) will give an error at compile-time, and you can fix it.
Related
#include<stdio.h>
int main(void)
{
int i=2,number;
printf("Enter another number greater than 5\':");
scanf("%d",number);
while (number>5);
for (; i<=3; i++)
{
printf("Hi\n");
++i;
}
printf("Enter another number greater than 5\' to continue the cycle:");
scanf("%d",number);
printf("finish");
return 0;
}
First, within the two scanf functions you need to add & to the number parameter. It is because &number gets the address of number, and the value entered by the user is stored in that address. Also, your code never leaves the loop. Try like this:
#include <stdio.h>
int main(void){
int number;
printf("Enter a number greater than 5: ");
scanf("%d",&number);
while (number>5){
for (int i = 0; i<1; i++){
printf("Hi\n");
}
printf("Enter another number to continue the cycle: ");
scanf("%d", &number);
}
printf("finish");
return 0;
}
Remember that for loop already increments the counter i by itself, so the i++ statement inside loop is unnecessary if you wanted to print "Hi!" only once (even the for loop is useless if you only wanted to print it once, but I guess you did it because you are learning).
There are several problems:
1 Incorrect usage of scanf
scanf takes format string and then addresses to variables, so it could write to memory, where the variables are located.
So correct usage is
scanf("%d",&number);
Best would be to check also return value of scanf. scanf returns count of successfully loaded arguments. So in you case
if (scanf("%d", &number) != 1) {
// print error message, or something else
}
If there were more arguments, then the condition would be different
if (scanf("%d %f %c %d", &a, &b, &c, &d) != 4) {
// ...
}
2 Infinite while loop
while (number>5); is infinite loop if number is greater then 5.
number is not changed within the loop, so the condition for while loop would be always truthy.
3 Possibly wrong incrementation of i variable in for loop
// int i = 2;
for (; i<=3; i++)
{
printf("Hi\n");
++i;
}
There is suspicious ++i; in the for loops body. This does not change the for loops behaviour, but I assume you are a beginner, so I will explain it anyway.
It will work like this:
for loop starts with no initialization (i is initialized to 2 outside of the loop)
condition i <= 3 gets evaluated to 1 (C does not have boolean [true,false], so there are used numbers instead [0 == false, anything else == true])
printf("Hi\n"); gets evaluated -> "Hi\n" gets printed
++i; gets evaluated -> i gets incremented to 3
update of for loop gets called (which is i++) -> i gets incremented to 4
condition i <= 3 gets evaluated as 0, because 4 (value of i) is greater then 3
for loop gets finished
My point here is that the i is incremented twice every loop.
So it's the same like
for(; i <= 3; i += 2) {
scanf("%d", &number);
}
If user enters a character instead of a number, I want to give him another option to try once more but below code prints out "Invalid. Pls enter a number." forever if user enters a character instead of a number. Why doesn't it wait for the user to enter again? (scanf part is for that I assume)
#include <stdio.h>
long get_long(void);
int main(void) {
long start;
printf("Enter a number: ");
start = get_long();
return 0;
}
long get_long(void)
{
long num = 0;
while (scanf("%ld", &num) != 1)
{
printf("Invalid. Pls enter a number.");
}
return num;
}
This is a common problem with scanf. (The question has been asked many times; there are probably duplicate answers.) The problem is that when you type simething that isn't a valid number, scanf fails (and returns 0), but it leaves the unmatched input on the input stream.
You have to flush the unread input somehow. One way is like this. Write the function
void flush_one_line()
{
int c;
while((c = getchar()) != EOF && c != '\n')
{ /* ignore */ }
}
This function reads and discards one line of input, up to a newline. (That is, it throws away anything and everything that the previous scanf call didn't read.)
Then modify your original program like this:
while (scanf("%ld", &num) != 1)
{
printf("Invalid. Please enter a number.");
flush_one_line();
}
Your logic behind the code was flawed. What you essentially did with your code is ask the while loop to work as long as correct input isn’t made.
while(incorrect input is made) {
print
}
Proper solution would be along the lines of
while(number of tries > 0) {
do operations
check for right input, if it is correct break the loop, else keep on going
decrement number of tries by one
}
like this. it executes while 2 times
int i=1;
while (i!= 2)
{ scanf("%ld", &num) ;
if(num==1)
break;
printf("Invalid. Pls enter a number.");
i++;
}
So I just started learning about structures and my recent question asked me to make a check for the screen time that user enters and the only valid ranges are 2pm, 5pm 8pm and 11pm. I made a long ass code for it and works as intended but I don't think it is efficient? I tried making an array for the valid screen time and entered all 4 values in that array of size 4 and made a loop to check for each value inside the array when user enters their choice but when user enters wrong choice, it displays 4 error messages since it checks all 4 values inside the array so it was wrong. Anyways here is my code:
void main(){
typedef struct{
char movieName[20];
int screenTime;
float price;
int seatNum;
} MovieTicket;
MovieTicket ticket;
//int validTime[4] = {2, 5, 8, 11};
do{
printf("Enter your screen time: ");
scanf("%d", &ticket.screenTime);
if(ticket.screenTime != 2 && ticket.screenTime != 5 && ticket.screenTime != 8 && ticket.screenTime != 11){
printf("Invalid Screen Time! Enter again please!\n");
}
} while(ticket.screenTime != 2 && ticket.screenTime != 5 && ticket.screenTime != 8 && ticket.screenTime != 11);
system("pause");
}
Thank you for your time, I am just a beginner so bear with me :(
Using magic numbers like this is fairly unmaintainable (adding new allowed times isn't clean, you have to edit conditions and such), your version with the array was better. The rest of this answer focuses on getting that solution to work how you wanted it to:
It's perfectly efficient (could make it more efficient by using a std::set or a std::unordered_set, but for such a small number of allowed values it's negligible), that's not an issue. Your current version is just as fast as the version with an array, as you still compare the same amount of numbers.
If your issue is that it prints the error 4 times, then keep a boolean flag to indicate the user's entered a valid value, and check it after the loop:
const unsigned int VALID_TIMES_SIZE = 4;
bool validTime = false;
for (unsigned int i = 0; i < VALID_TIMES_SIZE; ++i)
{
if (ticket.screenTime == validTimes[i])
{
validTime = true;
break;
}
}
if (!validTime)
{
printf("Invalid Screen Time! Enter again please!\n");
// Redo the loop using continue, for example
}
Check the return of scanf to make sure it was successful. If not, the input stream will need to be cleaned of pending characters.
#include <stdio.h>
typedef struct{
char movieName[20];
int screenTime;
float price;
int seatNum;
} MovieTicket;
int main( void) {
MovieTicket ticket;
int result = 0;
int validTime[] = {2, 5, 8, 11};
int limit = sizeof ( validTime) / sizeof ( validTime[0]);
int match = 0;
do{
match = 0;
printf("Enter your screen time: ");
if ( 1 == ( result = scanf("%d", &ticket.screenTime))) {
//check for matching validTime
for ( int each = 0; each < limit; each++) {
if ( validTime[each] == ticket.screenTime) {
match = 1;
break;
}
}
if ( !match) {
printf("Invalid Screen Time! Enter again please!\n");
result = 0;
}
}
else {
//scanf could not parse an int
printf("Enter Screen Time! Enter again please!\n");
while ( '\n' != getchar ( )) { }//clear input stream
}
} while ( result != 1);
system("pause");
}
To make something easy to understand you can avoid using those magic numbers or wrap them in a meaningful structure like checkTime and then use it for this kind of check. That way suppose next time you are asked to build a check time with some different times you won't have to look for hardcoded times and replace with it new things.
For example you can do something like this ,
struct checkTime{
int *checkedTimes;
int length;
int (*checkIfTimeExists)(const struct checkTime* ch, int tm);
};
int checkTimerExistAux(const struct checkTime* ch,int tm)
{
for(int i=0;i<ch->length;i++)
if(ch->checktedTimes[i]==tm) return 1;
return 0;
}
Then you create an `initilaize` function which will allocate memory and do this hardcoded thing. (like putting times and all that)[2,5,8,11] in your case.
initialize(); // here you created the structure `checkTimeInst`
// struct checkTime* checkTimeInstPtr = malloc(sizeof(struct checkTime));
// checkTimeInstPtr->checkedTimes = malloc(sizeof(int)*N);
// checkTimeInstPtr->length = 0;
// checkTimeInstPtr->checkIfTimeExists = checkTimerExistAux;
do{
printf("Enter your screen time: ");
scanf("%d", &ticket.screenTime);
if(checkTimeInstPtr->checkIfTimeExists(checkTimeInstPtr,ticket.screenTime)==0){
printf("Invalid Screen Time! Enter again please!\n");
}
} while(checkTimeInstPtr->checkIfTimeExists(checkTimeInstPtr,ticket.screenTime)==0);
This is not about speed up but it is about maintainability.
Here there is nothing that you can do about speed up. But yes if you are trying to squeeze time even more try to use the reader or scanner yourself using getchar() or similar.
Initially the code posted assumed C++ where as OP asked for C language specific answer or modification. I have modified it to follow that criteria after Paul Griffiths comment.
I need to create an array of ints of an unknown size and pass them all. My code looks like this:
int FillTable(int a[], int max){
int i;
int x = 0;
int m = 0;
for (i = 0; i < max; i++){
printf("Fill the table with integers: ");
scanf("%d", &m);
if (m != "" && m != NULL){
a[i] = m;
}else if (m != "" && m == NULL){
a[i] = 0;
}else{
break;
}
}
printf("\n");
return 0;
}
I know you can pass multiple ints separated by spaces with something like:
scanf("%d %d %d", &var1, &var2, &var3);
But I don't know how to pass a number of integers that I don't know how many will be there. Could I create a string with a bunch of %d and just repeat that for max times? I don't know, but right now, I just ask for ints until the array is full, and I need to be able to have the array be smaller than max if the user doesn't enter in enough values. Does anyone have any ideas as to how I would go about scanning for an unknown number of integers?
Does anyone have any ideas as to how I would go about scanning for an unknown number of integers?
This calls for Dynamic memory allocation!
One way of going with scanning unknown number of integers is, firstly allocate an integer array with size to hold max number of integers.
How to know whether user has ended his input?
If you are only scanning in positive integers from user at array entries then prompt him to end his input by inputting a negative number
or if you are sure about the range of input entries then break out of loop, when user enters input out of range
Example: (considering user inputs only positive numbers)
//creating a large enough array to store user inputs
int *array = malloc(sizeof(int) * max);
//check if memory was allocated or not
if(array == NULL)
{
printf("memory allocation problem!");
exit(1);
}
//integer to make note of size of array or you can use the sizeof() function instead
int size_of_array = 0;
for (i = 0; i < max; i++)
{
printf("Fill the table with integers: ");
if(scanf("%d", &m) != 1) //check for scanf return value
{
printf("wrong input, try again");
char consume; //to consume the character
scanf("%c", &consume);
i--;
continue;
}
if (m > 0) //if positive number, accept
{
array[i] = m;
size_of_array++;
}
else //else break out of scanning
{
break;
}
}
//do the program.....
//don't for get to free the memory at the end
free(array);
here's a working example: https://ideone.com/BHN4sk
You are trying to do something that is not necessary. To predict the size of the array and reallocate the appropriate exact size would be computationally more expensive (in terms of cpu time) so that benefit of saving the memory that you already had allocated is not enough.
The size of the array in c is stored somewhere that not necessarily has anything to do with the array itself. So you simply need to know how many of the array elements are interesting for the program and nothing else.
You could have something like
struct Array {
int *data;
size_t size;
size_t count;
};
where size is the total size of the array, count is the number of elements in the array and data are the elements. I use this pattern a lot and it's useful, specially if combined with realloc() as it saves from unecessarilly reallocating memory too many times which is expensive at the cost of using slightly more memory than actually needed.
But systems today have way more memory than can be used (except if you use Android Studio, which can use as much memory as your computer has).
First, m != "" && m != NULL probably does not do what you think it does. You're probably coming from a different language. What (I think) that statement does is compare the value in the integer variable m to the address of the string literal "" and then compare it to NULL (which evaluates to 0).
Second, scanf by default reads until either a space or a newline.
scanf returns a negative number on failure, so your code should look like this:
for (i = 0; i < max; i++){
printf("Fill the table with integers: ");
if(scanf("%d", &m) > 0) {
a[i] = m;
}
else {
break;
}
}
I left out the a[i] = 0 branch because I don't understand what you wanted there.
Also, you never use the variable x - unless there is more code that you left out.
your problem isn't understand for me properly,however i think this will be helped to you
int arraySize = 200; // Or whatever
int *array_ = malloc(arraySize * sizeof(int));
use this and the pass the *array_ as parameter,first defined array size or get array size as a user input,and run a for loop till size of array
You should decide how the user can stop his input (and include this info in your prompt). A quick-and-dirty way would be "enter anything that is not a number". I chose this way of terminating input, because it's easy to implement (hence, quick and dirty):
printf("Fill the table with integers; terminate with 'x':\n");
for (i = 0; i < max; i++)
{
int result = scanf("%d", &a[i]);
if (result != 1)
break;
}
Note:
The prompt tries to be user-friendly
The scanf function puts the number straight into the array, without using any intermediate variable
The scanf function returns how many numbers it read, which is normally 1; if it's not 1, then the user entered x or anything else
When the code finishes, i holds the number of iterations, which shows how many numbers were read from the user.
Your input function should return the size of the array:
return i; // instead of "return 0"
You might also want to clean the stdin buffer - discard anything that the user entered to terminate the array:
while (getchar() != '\n')
{
// do nothing - keep consuming input until end-of-line
}
I am a math student, and I'm learning the very basics in programming in C. I need a program to read an input consisting in an array, the components of which must have certain requisites; I would like the program to ask the user for the components of the array. The user should then have to enter such components separating them with spaces. The details aren't important to get the main question across; I'll choose a simpler example then the one I am dealing with: let's say I want an array with 6 components not to contain the number 4. So I tried:
#include <stdio.h>
int main(void) {
int a[6];
printf("enter components: ");
int i;
for (i = 0; i < 6; i++) {
scanf("%d", &a[i]);
if (a[i] == 4) printf(" \n\n4 is not allowed, try again\n\n");
}
for (i = 0; i < 6; i++) {
printf("%d ", a[i]);
}
}
If I compile this and run it, and for example enter:
1 2 3 4 5 6
I will get my error message, but only after having pressed enter, that is after having entered all six components (not straight after having pressed space for the fourth time). So here are my questions (I am looking for solutions which don't make use of strings or pointers, unless it is impossible to do without them):
Is there a way to get the program to read a component (and to act accordingly) straight after its subsequent space has been entered? I'm guessing there isn't because scanf only works after the user presses enter, and not space, correct?
If there isn't, is there a way to get the program to read the components all at once after having pressed enter at the end, but letting the user pick up from the last right component? For example, with the above input, I would like the program to display something like this:
4 is not allowed
1 2 3 _
so that the user can correct his/her input (possibly changing the first three digits as well).
Sorry if this question is too dumb! Thank you for your help!!
EDIT: Well, thanks for the great answers, you have all been very helpful! It's a pity I can't accept more than one.
In for loop, after each iteration, the counter add by one automatically. If you get an invalid input, you should prevent the counter increasing. To do this, just add i--; to your code when you give an invalid input.
#include <stdio.h>
int main(void) {
int a[6];
printf("enter components: ");
int i;
for (i = 0; i < 6; i++) {
scanf("%d", &a[i]);
if (a[i] == 4){
printf(" \n\n4 is not allowed, try again\n\n");
i--;
}
}
for (i = 0; i < 6; i++) {
printf("%d ", a[i]);
}
}
Please see the bellow code:
#include <stdio.h>
int main(void) {
int a[6];
int i;
bool hasError = false;
int errorIndex = 0;
do{
hasError = false;
printf("enter components: ");
for (i = 0; i < errorIndex; i++)
printf("%d ", a[i]);
for (i = errorIndex; i < 6; i++) {
scanf("%d", &a[i]);
if (a[i] == 4 && hasError == false){
printf(" \n\n4 is not allowed, try again\n\n");
hasError = true;
errorIndex = i;
}
}
}while(hasError == true);
for (i = 0; i < 6; i++) {
printf("%d ", a[i]);
}
}
This is related to your terminal being in "cooked" mode. Characters aren't even sent to the program until the user presses enter.
You could do something like this:
int i,a[6];
for (int i=0;i<6;i++) {
scan: scanf("%d",&a[i]);
}
for (int i=0;i<6;i++) if (a[i]==4) {
printf("4 is not allowed. re-enter the last %d numbers\n",6-i);
goto scan;
}
note that in most case, it's better to avoid using goto, but in this case I think that it's natural.
If you really want, you can print the first i numbers (before the goto), but it's complicated (and platform-depended) to let the user change those numbers.
Improving on Mir Milad Hosseiny answer (I wrongly identified it as being an out of control infinite loop... it's actually exactly the infinite loop I describe in my comment)...
I would write a small function that has either a "white list" (things you want) or a "black list" things you don't want, and check each value to either belong or not (depending on the approach) to the list. That way you can keep a separate place where your store the values that you are willing to accept or the values you are not, so your primary function doesn't get really messy with exceptions or inclusions in the "if"
so your code would be
if(isAllowed(a[i]){
myList[j] = a[i]; //j is your alternate counter
}