Need help making my code efficient - c

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.

Related

new to c problem with simple game problem with function

im new to c i try to make a little and very simple game of hangedman and i dont know why doesent work get error in gcc "expected declaration or statement at the end of input"
im new to c and ii try very hard to learn it.
im missing something? my function is not right? some advice to learn alghorytmically thinking?
thanx in advance for the hel you gonna give me
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//function to find letter in string
int findletter(char y)
{
char c;
int i;
char secret[] = "outcast";
i = 0;
scanf("%c", &c);
while (c != secret[i] && i < strlen(secret))
i++;
if(c == secret[i])
return (1);
else
return (0);
}
//confirmation letter
int guessed(char a)
{
int z;
char guess [6] = {0};
z = 0;
while(findletter(guess[z]) != 1 && findletter(guess[z]) < 6)
{
z++;
if(findletter(guess[z]) == 1)
return 1;
else
return 0;
//word guessed
int tryguess(char v)
{
int x;
x = 0;
while(findletter(guess[x]) == 0)
{
x++;
if(findletter(guess[x] == 1))
return 1;
else
return 0;
}
}
int main()
{
char secret[] = "outcast";
char letter;
int lives;
char guess [6] = {0};
int i;
lives = 10;
i = 0;
printf("welcome to the hanged man\n");
while(i < 6)
{
if((findletter(secret[i] == 1)))
printf("%c", secret[i]);
else
printf("*\n");
i++;
}
return 0;
}
Correction to your code...
int guessed(char a)
{
int z;
char guess [6] = {0};
z = 0;
while(findletter(guess[z]) != 1 && findletter(guess[z]) < 6)
{
z++;
if(findletter(guess[z]) == 1)
return 1;
else
return 0;
} // you forgot closing while loop here
} // function closing parenthesis
//word guessed
Advice:
I don't know how much you had practice and how much you had learned yet..but on observing your mistake above I would like to suggest that whenever you create function or loop always write its prototype first, let's say you want to create a function for adding two numbers..
STEP 1: write prototype
int add(int x, int y)
{
//then do your stuff here...
return 0;
}
This will eliminate you chances of making error of parentheses...
There are a lot of issues with this program, from both a syntax standpoint and a logical one.
General issues include:
Function guessed and its while loop are not closed (missing }).
There is a lot of unused code (functions and variables).
The line if((findletter(secret[i] == 1))) compares the character value of secret[i] with 1 and passes that result to findletter. This doesn't matter though since you don't use this argument, and take user input within the function.
You have hardcoded strings and lengths, which makes your program less dynamic and harder to change in the future.
Using while loops as guards (in the unused functions tryguess and guessed), that are always exited on the first iteration.
findletter simply checks if secret contains the character c, returning on the first occurrence.
It could be more clearly expressed as:
int findletter(char unused) {
char secret[] = "secret",
c;
scanf(" %c", &c);
for (size_t i = 0; i < strlen(secret); i++)
if (secret[i] == c)
return 1;
return 0;
}
With that said, findletter would be better if you passed both the secret and c as arguments, so that you can use it more generically, and decouple user input from the function itself.
(Or you could simply use the standard library function strchr which achieves a very similar goal.)
The pattern of
if (a == b)
return 1;
else
return 0;
can simply be reduced to
return a == b;
Aside from the issues above, the structure of your program doesn't make much sense. If our program worked, you'd basically be asking the player to guess a word of unknown length, one character of the word at a time. They can also simply guess any letter to display the current one. One could 'solve' the entire word "secret" by simply inputting 's' repeatedly.
The structure of a very basic hangman program is:
Select the word to be guessed. Select number of lives.
Create a blanked version of word to track progress. Display this blanked version, which indicates the length to the player.
Ask the player to guess a letter. Skip those already guessed.
Update all positions in the blanked version where letter appears in the word.
Decrement lives on miss, end game if out of lives.
Check if the amount of characters changed in the blank version matches the length of word.
Win condition, or return to step 3.
There are many different ways to achieve this, and there are likely thousands of examples online.
Here is a rough program that is about as simple as it gets. This showcases the usual structure and flow of a game of hangman.
#include <stdio.h>
#include <string.h>
size_t update_all(char *to, const char *from, size_t len, char g) {
size_t changed = 0;
for (size_t i = 0; i < len; i++)
if (from[i] == g) {
to[i] = g;
changed++;
}
return changed;
}
void play_hangman(const char *word, unsigned lives) {
size_t word_length = strlen(word),
blanked_length = 0;
char blanked[word_length + 1],
guess = '\0';
for (size_t i = 0; i < word_length; i++)
blanked[i] = '*';
blanked[word_length] = '\0';
while (lives) {
printf("The word: [%s]\n"
"(Lives = %u) Enter a guess: ",
blanked,
lives);
scanf(" %c", &guess);
if (strchr(blanked, guess)) {
printf("[%c]: Already guessed!\n", guess);
continue;
}
size_t found = update_all(blanked, word, word_length, guess);
blanked_length += found;
if (!found) {
printf("[%c]: NOT FOUND!\n", guess);
lives--;
} else
printf("[%c]: FOUND!\n", guess);
if (!lives)
puts("Out of lives! You lose!");
else if (blanked_length == word_length) {
printf("You win! Word is [%s].\n", word);
return;
}
}
}
int main(void) {
play_hangman("secret", 10);
}
Note that this program is far from perfect, as it doesn't fully keep track of guessed letters, so the player can guess the same wrong letter multiple times, and lose a life every time. To fix this, we would need even more state, collecting each guess the player makes, and use that data instead of the naive if (strchr(blanked, guess)).
It also makes use of the '*' character as a sentinel value, which would cause confusion if our word contained '*'. To fix this, we could use an array of boolean values indicating the correctly guessed letters in the word thus far, and use this to print our word character-by-character. Or we could restrict character inputs with functions like isalpha.
This program simply serves as an example that for a proper approximation of the typical "Hangman" you need to handle more game state than you have.
(Error handling omitted for brevity throughout this answer.)

How to make this program more efficient at expressing its purpose? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I wrote a simple program to find greatest among three numbers. But it seems that I wrote it in a way that makes it slightly confusing - hard to understand. What would be the way to improve this program to make it better at expressing its purpose and operation, and to remove the obvious repetition?
main()
{
int a,b,c;
printf("Enter three numbers: ");
scanf("%d %d %d",&a,&b,&c);
if (a==b && b==c)
printf("all are equal....:)");
else if(a>b)
{
if(a>c)
printf("%d is greatest among all",a);
else
printf("%d is greatest among all",c);
}
else
{
if(b>c)
printf("%d is the greatest among all",b);
else
printf("%d is the greatest among all",c);
}
getch();
}
#include <stdio.h>
int max(int a, int b)
{
return (a > b) ? a : b;
}
int main(void)
{
int a, b, c;
printf("Enter three number: ");
scanf("%d %d %d", &a, &b, &c);
if ((a == b) && (b == c))
{
printf("all are equal.");
}
else
{
printf("%d is the greatest among all", max(a, max(b, c)));
}
return 0;
}
I'd like to offer an example of the development process that could lead to figuring it out. I've linked relevant documentation to fill in the likely gaps in knowledge, and attempted to explain what led me to that documentation. Variants of such thought process are common among developers, yet the process is regrettably often not explained in educational settings, and must be acquired by "sweat and tears". I figure: let's shed some light on it!
A decent reference to the C language and standard library is https://en.cppreference.com/w/c (yes, it's cppreference, but it's not just for C++!).
The clarity of this program matters, and it certainly could be improved.
But people also interpret efficiency to mean speed. But: The speed of this particular program is irrelevant. The slowest element is the human, by more than 6 orders of magnitude vs. the machine. If you wished for this code to be faster when e.g. working on a large array of triples of numbers, then you could edit the question and instead provide an example that works through a big array of triples. As it stands, the program could be written in a shell script file and you wouldn't be able to tell the difference. It's a good idea to optimize what matters - and maintainability often matters more than raw performance :)
Output Skeleton
You only need to print the largest number - so let's start with a way to print it, and assume that the selection of the largest number, and the detection whether they are all equal, has been taken care of:
#include <stdio.h>
int main() {
char all_equal;
int max;
// to be done
if (all_equal) printf("All numbers are equal.\n");
else printf("%d is the greatest among the numbers entered.\n", max);
}
Thus, we have a skeleton for the output of the program. That's our goal. It helps to start with the goal expressed in the language of the problem domain - here in C - for it can focus and guide the implementation. We know exactly where we're going now: we have to get the input, and then process it to obtain the max value, and the all_equal predicate (a boolean: zero means falseness, anything else means truth).
We could actually turn this goal into a functional program by providing some "fake" data. Such data could be called test data, or mock data, depending on who you ask :)
#include <stdio.h>
int main() {
char all_equal = 0;
int max = 10;
if (all_equal) printf("All numbers are equal to %d.\n", max);
else printf("%d is the greatest among the numbers entered.\n", max);
}
We can run this, then change all_equal to 1 and see how the output is changed. So - we have some idea about what results should be fed into the output section of the program.
Processing
Getting the input from the user is on the opposite end of the goal, so let's work on something that builds up directly on the goal: let's compute those all_equal and max values! The processing code should replace the mock char all_equal = 0; int max = 10; section.
First, we need some numbers - we'll use mock data again, and from them we need to select the largest number:
int numbers[3] = {-50, 1, 30}; // mock data
char all_equal = 0; // mock data
int max = numbers[0];
for (int i = 1; i < 3; i++) {
if (numbers[i] > max) max = numbers[i];
}
Modern compilers will usually unroll this loop, and the resulting code will be very compact. The i loop variable won't even appear in the output, I'd expect.
We can foresee a potential for mistakes: if we ever decide to change the quantity of numbers, we can easily miss a place where such quantity is hardcoded (here as the literal 3). It'd be good to factor it out:
#define N 3
int numbers[N] = {-50, 1, 30}; // mock data
char all_equal = 0; // mock data
int max = numbers[0];
for (int i = 1; i < N; i++) {
if (numbers[i] > max) max = numbers[i];
}
You'll run into such #define-heavy code. It has its place back when C compilers were poor at optimizing code. Those times are now thankfully well over, but people continue doing this - without quite understanding why it is that they do it.
For numeric constants, it's usually unnecessary and counterproductive: the preprocessor does string substitution, so it has no idea what the code it modifies actually means. Preprocessor definitions "pollute" the global context - they leak out from wherever we define them, unless we explicitly #undef-ine them (How many people do that? You'll find that not many do.)
Instead, let's express it as a constant. We try:
const int N = 3;
int numbers[N];
But: this doesn't compile on some compilers. Huh? We read up on array declarations, and it seems that iff variable-length arrays (VLAs) are not supported by our compiler, then the number of elements must be a [constant expression][constexpr]. C doesn't consider a const int to be a constant expression (how silly, I know!). We have to kludge a bit, and use an enumeration constant to get the constant expression we need:
enum { N = 3 };
int numbers[N] = {-50, 1, 30}; // mock data
char all_equal = 0; // mock data
int max = numbers[0];
for (int i = 1; i < n; i++) {
if (numbers[i] > max) max = numbers[i];
}
We start by assigning the value of the first number in the list (index 0!) to max. Then we loop over subsequent numbers (starting with index 1!), compare each to the maximum, and update the maximum if a number happens to be greater than the maximum.
That's half of the goal: we got max, but we also need all_equal! The check for quality can be done in the same loop as the selection of the maximum (greatest number), thus:
enum { N = 3 };
int numbers[N] = {-50, 1, 30}; // mock data
char all_equal = 1;
int max = numbers[0];
for (int i = 1; i < N; i++) {
all_equal = all_equal && numbers[i] == max;
if (numbers[i] > max) max = numbers[i];
}
// here we have valid all_equal and max
We start with the assumption that the numbers are all equal (all_equal = 1). Then, for each subsequent number (indices 1 onwards), we check if the number is equal to the maximum, and we use a logical conjunction (logical and - &&) to update the all_equal proposition. Proposition is what we call a boolean: simply a statement that can be either true (here: non-zero) of false (zero). The conjunction applied repeatedly to all_equal has the effect of a one-way valve: once all_equal goes false, it will stay false.
Logicians would state it as ∀ p ∈ ℙ : (false ∧ p) = false. Read: for all ℙropositions p, false and p is false.
We merge this into our skeleton, and get a slightly more useful program. It still uses mock data, but it performs all the "interesting" parts of the problem.
#include <stdio.h>
int main() {
enum { N = 3 };
int numbers[N] = {-50, 1, 30}; // mock data
char all_equal = 1;
int max = numbers[0];
for (int i = 1; i < N; i++) {
all_equal = all_equal && numbers[i] == max;
if (numbers[i] > max) max = numbers[i];
}
if (all_equal) printf("All numbers are equal.\n");
else printf("%d is the greatest among the numbers entered.\n", max);
}
We can run this code, tweak the mock data, and verify that it appears to do what we want it to! Yay!
Input
Finally, we should get those numbers from the user, to get rid of the final bit of mock data:
#include <stdio.h>
int main() {
enum { N = 3 };
int numbers[N];
printf("Enter %d numbers:\n", N);
for (int i = 0; i < N; i++) scanf("%d", &numbers[i]);
char all_equal = 1;
int max = numbers[0];
for (int i = 1; i < N; i++) {
all_equal = all_equal && numbers[i] == max;
if (numbers[i] > max) max = numbers[i];
}
if (all_equal) printf("All numbers are equal.\n");
else printf("%d is the greatest among the numbers entered.\n", max);
}
We run it, and hey! It seems to work! This program is equivalent to the one you wrote. But. There's always a "but".
Handling Invalid Input
Software testing now begins in earnest. We "play" some more with the program (we "exercise" it), and notice that the program doesn't react any differently when a non-number is entered. It sometimes seems to ignore the invalid number and still provide sensible result based on other, correct, numbers, but sometimes it just spits out a random value. Random value - hmm. Could perhaps one of the numbers be uninitialized?
We read the documentation of scanf() and notice that it won't modify its output argument if it fails! Thus the program has undefined behavior: here it results in "random" output.
Reading on, we find that the return value of scanf() can be used to check if it was successful. We'd like to handle invalid input and provide some feedback:
int main() {
enum { N = 3 };
int numbers[N];
printf("Enter %d numbers.\n", N);
for (int i = 0; i < N; /*empty!*/) {
printf("#%d: ", i+1);
if (scanf("%d", &numbers[i]) != 1)
printf("Sorry, invalid input. Try again.\n");
else
i++;
}
...
}
We [try again][run4], and the program goes into an infinite loop as soon as invalid input is provided. Hmm. Something weird is going on. We read up on the issue of parsing user input in C, and realize that scanf() alone won't work correctly if any input errors are present, since the input remains in the input buffer - it will keep "tripping" subsequent scanf's. We must remove that input before retrying.
To find a function that may do that, we read up on the C input/output library and find the getchar() function. We use it:
int main() {
enum { N = 3 };
int numbers[N];
printf("Enter %d numbers.\n", N);
for (int i = 0; i < N; /*empty*/) {
printf("#%d: ", i+1);
if (scanf("%d", &numbers[i]) != 1) {
printf("Sorry, invalid input. Try again.\n");
char c;
while ((c = getchar()) != '\n'); // remove input until end of line
} else
i++;
}
...
}
We try it again, and things seem to work. But! We can do something else: let's try closing the input stream (^Z,Enter on Windows, ^D on Unix). The program goes into an infinite loop - again.
Aha! End of input - EOF! We must explicitly handle the EOF (end of file/read error) condition, and the best we can do then is to stop the program. How? We read up about C program utilities, and find a perfect function that would abort the program: abort(). Program utilities are utilities that used to manage "other" tasks that a program might do - tasks that don't fall under other categories (are not I/O, math, etc.).
#include <stdlib.h> // added for abort()
int main() {
enum { N = 3 };
int numbers[N];
printf("Enter %d numbers.\n", N);
for (int i = 0; i < N; /*empty*/) {
int c;
printf("#%d: ", i);
c = scanf("%d", &numbers[i]);
if (c == EOF) abort();
if (c != 1) {
printf("Sorry, invalid input. Try again.\n");
while ((c = getchar()) != EOF && c != '\n');
if (c == EOF) abort();
} else
i++;
}
...
}
We try yet again, and this time things really seem to work fine. No matter what we throw at the program, it behaves reasonably: if the input is still available, it asks the user to enter the number again. If the input won't be available anymore (EOF indicates that), we abort().
In even more deviousness, we try to surround the numbers with spaces and tabs in our input - after all, it's just whitespace, and to a human it'd look like valid input in spite of the spaces. We try it, and no problem: scanf() seems to do the "right thing". Hah. But why? We read into the scanf() documentation, and discover that [emphasis mine]:
All conversion specifiers other than [, c, and n consume and discard
all leading whitespace characters (determined as if by calling
isspace) before attempting to parse the input. These consumed
characters do not count towards the specified maximum field width.
Complete Program
We now got all the pieces. Put them together, and the complete program is:
#include <stdio.h>
#include <stdlib.h>
int main() {
enum { N = 3 };
int numbers[N];
printf("Enter %d numbers.\n", N);
for (int i = 0; i < N; /*empty*/) {
printf("#%d: ", i+1);
int c = scanf("%d", &numbers[i]);
if (c == EOF) abort();
if (c != 1) {
printf("Sorry, invalid input. Try again.\n");
while ((c = getchar()) != EOF && c != '\n');
if (c == EOF) abort();
} else
i++;
}
char all_equal = 1;
int max = numbers[0];
for (int i = 1; i < N; i++) {
all_equal = all_equal && numbers[i] == max;
if (numbers[i] > max) max = numbers[i];
}
if (all_equal) printf("All numbers are equal.\n");
else printf("%d is the greatest among the numbers entered.\n", max);
}
Try it out!.
Useful Refactoring
This works fine, but the input processing dominates over main, and seems to obscure the data processing and output. Let's factor out the input, to clearly expose the part of the program that does "real work".
We decide to factor out the following function:
void read_numbers(int *dst, int count);
This function would read a given count of numbers into the dst array. We think a bit about the function and decide that a zero or negative count doesn't make sense: why would someone call read_numbers if they didn't want to get any input?
We read up on error handling in C, and discover that assert() seems a good candidate to make sure that the function was not called with incorrect parameters, due to a programming bug. Note that assert() must not be used to detect invalid program input!!. It is only meant to aid in finding program bugs, i.e. the mistakes of the developer of the program, not mistakes of the user. If user input had to be checked, it has to be done explicitly using e.g. the conditional statement (if), or a custom function that checks the input - but never assert!
Note how assert is used:
It tells you, the human reader of the program, that at the given point in the program, count must be greater than zero. It helps reasoning about the subsequent code.
The compiler generates the code that checks the asserted condition, and aborts if it doesn't hold (is false). The checks usually are only performed in the debug build of the program, and are not performed in the release version.
To keep the program flow clear, we forward-declare read_numbers(), use it in main(), and define (implement) the function last, so that it doesn't obscure things:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
void read_numbers(int *dst, int count);
int main() {
enum { N = 3 };
int numbers[N];
read_numbers(numbers, N);
char all_equal = 1;
int max = numbers[0];
for (int i = 1; i < N; i++) {
all_equal = all_equal && numbers[i] == max;
if (numbers[i] > max) max = numbers[i];
}
if (all_equal) printf("All numbers are equal.\n");
else printf("%d is the greatest among the numbers entered.\n", max);
}
void read_numbers(int *dst, int count) {
assert(count > 0);
printf("Enter %d numbers.\n", count);
for (int i = 0; i < count; /*empty*/) {
printf("#%d: ", i+1);
int c = scanf("%d", &dst[i]);
if (c == EOF) abort();
if (c != 1) {
printf("Sorry, invalid input. Try again.\n");
while ((c = getchar()) != EOF && c != '\n');
if (c == EOF) abort();
} else
i++;
}
}
You can try this program out in onlinegdb.com - just click this link!
In my opinion, main() looks very clear now. The input function stands on its own and can be analyzed in isolation: note how it's a pure function, and has no global state. Its output only acts on the arguments passed in. There are no global variables! There shouldn't ever be, really.
That's where I'd stop. We have a clear program that handles both valid and invalid input.
Moar Refactoring !!111!!
But you could say: how about we factor out data processing and final output as well? We can do that, sure. Yet, in a simple program like ours, it perhaps will slightly obscure what's going on. But at least let's see how it could look then, play with it and decide for ourselves :)
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char all_equal; // true if all numbers were equal
int max; // the greatest number
} Result;
void read_numbers(int *dst, int count);
Result find_greatest(int *numbers, int count);
void output_result(const Result *r);
int main() {
enum { N = 3 };
int numbers[N];
read_numbers(numbers, N);
const Result r = find_greatest(numbers, N);
output_result(&r);
}
void read_numbers(int *dst, int count) {
assert(count > 0);
printf("Enter %d numbers.\n", count);
for (int i = 0; i < count; /*empty*/) {
printf("#%d: ", i+1);
int c = scanf("%d", &dst[i]);
if (c == EOF) abort();
if (c != 1) {
printf("Sorry, invalid input. Try again.\n");
while ((c = getchar()) != EOF && c != '\n');
if (c == EOF) abort();
} else
i++;
}
}
Result find_greatest(int *numbers, int count) {
assert(count > 0);
Result r = {.all_equal = 1, .max = numbers[0]};
for (int i = 1; i < count; i++) {
r.all_equal = r.all_equal && numbers[i] == r.max;
if (numbers[i] > r.max) r.max = numbers[i];
}
return r;
}
void output_result(const Result *r) {
if (r->all_equal) printf("All numbers are equal.\n");
else printf("%d is the greatest among the numbers entered.\n", r->max);
}
Note how the Result local variable is initialized using struct initialization with designated initializers:
Result r = {.all_equal = 1, .max = numbers[0]};
If you needed to perform the initialization independently of the declaration of the variable, you would wish to use compound literals - a lesser known, but very important notational shortcut in modern C:
Result r;
// some intervening code
r = (Result){.all_equal = 1, .max = numbers[0]};
or perhaps:
void function(Result r);
void example(void) {
function((Result){.all_equal = 1, .max = numbers[0]});
}
Your code is fine; in fact, Bentley, McIlroy, Engineering a sort function, 1993, realised by, among others, BSD qsort, employs the same sort of mechanism in the inner loop for finding the median of three values. Except,
If all values are the same, it doesn't, "find the greatest among three numbers," but rather short-circuits and finds how many times the maximal value occurs, (three.) This is a separate problem.
It generally pays to separate one's logic from the output.
I've taken your code and put it in a function, stripped the output, and stripped of the equality.
#include <assert.h>
static int hi3(const int a, const int b, const int c) {
/* Same as `return a > b ? a > c ? a : c : b > c ? b : c;`. */
if(a > b) {
if(a > c) {
return a;
} else {
return c;
}
} else {
if(b > c) {
return b;
} else {
return c;
}
}
}
int main(void) {
/* Permutations of 3 distinct values. */
assert(hi3(1, 2, 3) == 3
&& hi3(1, 3, 2) == 3
&& hi3(2, 1, 3) == 3
&& hi3(2, 3, 1) == 3
&& hi3(3, 1, 2) == 3
&& hi3(3, 2, 1) == 3);
/* Permutation of 2 distinct values. */
assert(hi3(1, 2, 2) == 2
&& hi3(2, 1, 2) == 2
&& hi3(2, 2, 1) == 2);
assert(hi3(1, 1, 2) == 2
&& hi3(1, 2, 1) == 2
&& hi3(2, 1, 1) == 2);
/* All the same value. */
assert(hi3(1, 1, 1) == 1);
return 0;
}
Your code tests all the combinations of relative magnitudes successfully.

How can I integrate modularity into my code in C?

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", &sectioncode);
while(sectioncode > 4 || sectioncode < 1)
{
printf("Invalid value entered. Must be 1 to 4, please re-enter: ");
scanf("%d", &sectioncode);
}
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.

End while loop with ctrl+d, scanf?

I want the user to be asked "how many circles" they wanna write until the user decides to end it with (Ctrl+d) which is EOF?
extra question: if I write a letter for example "k" it will spam out circles. How do I change that?
#include <stdio.h>
int main ()
{
int i;
int x;
printf("\nHow many circles do you want to write?\n");
scanf("%d", &x);
while(x != EOF)
{
for (i = 1; i <= x; i = i + 1)
{
putchar('o');
}
printf("\nHow many circles do you want to write?"
"(to end program click ctrl+d at the same time!))\n");
scanf("%d", &x);
}
printf("\n\n Bye! \n\n");
return 0;
}
The biggest problem with your program is that scanf will not read an EOF into a variable. However, fixing just this problem is not going to make your program entirely correct, because there are other issues in your code:
Your code repeats itself - when possible, you should unify the code that deals with the first iteration vs. subsequent iterations.
Your code will not handle invalid input - when an end-user enters non-numeric data, your program goes into an infinite loop.
Your code follows the old style of C - declaring all variables at the top has not been required for more than fifteen years. You should declare your loop variable inside the loop.
Here is how you fix all these shortcomings:
int x;
for (;;) {
printf("\nHow many circles do you want to write? (to end the program click Ctrl+D at the same time!)\n");
int res = scanf("%d", &x);
if (res == EOF) break;
if (res == 1) {
... // Draw x circles here
} else {
printf("Invalid input is ignored.\n");
scanf("%*[^\n]");
}
}
printf("\n\n Bye! \n\n");
return 0;
As per the man page, scanf() will return EOF, not scan EOF to x as a value.
Return Value
These functions return the number of input items successfully matched and assigned, which can be fewer than provided for, or even zero in the event of an early matching failure.
The value EOF is returned if the end of input is reached before either the first successful conversion or a matching failure occurs......
Also,
if I write a letter for example "k" it will spam out circles, how do I change that?
In case of input of one char value, it causes matching failure, in your case, scanf() returns 0, instead of 1.
So, altogether, you've to collect the return value of scanf() and check check that value for the required condition. You can change your code as
int retval = 0;
while ((retval = scanf("%d", &x))!= EOF && (retval == 1))
if you're allowed to #include , there are two convenient functions bool kbhit() and char getch().
So you can write
char c=0;
if(kbhit()) c = getch();
if(c== whatever code ctrl+d returns) x=EOF;
Hint: Take a look at what scanf(%d,&x) returns when you enter a letter instead of a number.
You can read char by char input :
#include <stdio.h>
int main ()
{
int i;
int x = 0;
int nb = 0;
while(x != EOF)
{
printf("\nHow many circles do you want to write?\n");
nb = 0;
for (x = getchar(); x != '\n'; x = getchar()) {
if (x == EOF)
goto end;
if (x >= '0' && x <= '9') {
nb = nb * 10 + x - '0';
}
}
for (i = 0; i < nb; i++)
{
putchar('o');
}
}
end:
printf("\n\n Bye! \n\n");
return 0;
}

linear searches in 2d arrays

#define NUMLEG 7
int Search_type(char leg_type[6][10], int travel_int[6][15], int trip_num);
c = Search_type(type,leg_type, travel_int, trip_num);
if (c == -1)
printf("Destination not found.\n");
else
printf("Destination Found \nDestination Name:%s", travel_name[c]);
int Search_type( char type, char leg_type[6][10], int travel_int[6][15], int trip_num)
{
int i, n;
char target;
getchar();
printf("Please enter travel type you wish to search by:");
scanf("%c", &target);
for (i =0; i <trip_num; i++)
{
for ( n = 0; n < travel_int[i][NUMLEG]; n++)
{
if( leg_type[i][n] == target)
{
return i;
}
}
return -1;
}
}
This is a rip out of a function I'm currently working on from a project and for some strange reason I can't seem to get it to search properly, I know that its not the rest of the code but this area since all the other function works properly. So I decided to break this out of the code and test it.
I can't seem to get it to search after 1 row:
travel_int[i][NUMLEG] contains the number of legs
leg_type is where i store the char of each leg
I want it to search through the leg_type till it finds the specified character. Without pointers.
EDIT
For example
Trip#:1
Destination Name:Austin
Leg(#1):A
Leg(#2):T
Trip#:2
Destination Name:Florida
Leg(#1):S
Leg(#2):F
so
trip number = 2
travel_int stores 2 trips and each trip has 2 legs
leg type stores in the 2 leg types at the trip number
When I put in A or T it prints out Austin like it should but when I input S or F it return destination not found =/. Am I doing the search wrong, or is it the print statement that is wrong. Or is it where I'm place my return -1;.
(I would put up the code but its a bit long..)
In your nested loops you are returning -1 without doing complete search in the entire arrays.
Place return -1 outside all the for() loops.
for (i =0; i <trip_num; i++)
{
for ( n = 0; n < travel_int[i][NUMLEG]; n++)
{
if( leg_type[i][n] == target)
{
return i;
break;
}
}
} return -1
This would definitely make your program run correctly.And this is the only reason why you get the correct results when you search for trip #01.

Resources