new to c problem with simple game problem with function - c

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.)

Related

Code in C where it prints from 0 to 100 then asks if user wants to see it again

I'm trying to make a program in C where it prints from 0 to 100 then asks the user to print it again. It's not working.
#include <stdio.h>
#include <string.h>
int main()
{
int num;
char resposta[0];
resposta[0] = 's';
num = 0;
do
{
for (int i = 0; i < 100; i++)
{
num += 1;
printf("%i\n", num);
printf("Repetir?s/n\n");
scanf("%c", resposta[0]);
}
} while ((strcmp(resposta[0], "S") == 0) || (strcmp(resposta[0], "s") == 0));
return 0;
}
when declaring an array in C, the number in the brackets [] is not the index number, it is the total number of elements contained within the array. Setting char resposta[0]; means that resposta contains no elements.
This is different from when accessing elements, which uses a zero-index. So running scanf("%c", resposta[0]); is trying to access the first element, but there are zero elements within resposta, so that's likely why this isn't working.
I also figure I should add that it is entirely possible to just create a char variable that is not an array, since it seems you only need the one character. Just do char resposta; without the brackets.
Some more info on Arrays if you're interested: W3Schools
There are many issues.
You have an array that can contains 0 elements which is rather pointless, you want char resposta[1].
But you don't want an array of chars anyway. You're using the %c specifier you need a single char instead of an array of chars.
You're mixing up strings and chars. Change strcmp(resposta[0], "S")==0 to resposta == 'S', provided you have declared char resposta;.
You're asking the user if he wants to start over again in the for loop which is probably not what you want.
Basically you want this (untested code):
int main() {
char resposta;
resposta = 's';
int num = 0;
do {
num = 0;
for (int i = 0; i < 100; i++) {
num += 1;
printf("%i\n", num);
}
printf("Repetir?s/n\n");
scanf("%c", &resposta); // not the useage of the & operator here
} while (resposta == 'S' || resposta == 's');
}
One of the reasons it is not working is that the program stops to ask the user to continue inside the loop. Another failure is that the counter being used is not reset to zero for the second and subsequent runs. Another failure is the scanf format specifier that does not clear the newline out of the input buffer. The next 'get input' will not find an S/s to continue, but will pickup a LF and terminate the loop.
Things can be simplified if they are separated. The interaction with the user is very clearly defined and should be factored-out into its own function:
#include <stdio.h>
int again(void) {
puts( "Repetir? " );
char ch;
return scanf( " %c", &ch ) == 1 && (ch == 'S' || ch == 's' );
}
// Edit: Just noticed the OP title says "0 to 100"
void out(void) {
int i = 0;
while( i <= 100 ) printf( "%i\n", i++ );
}
int main() {
do out(); while( again() );
return 0;
}

Hello i got an assignment in c programing and i dont really understand the c/malloc function i think

Hello i got an assignment in c programing and i dont really understand the c/malloc function i think,
they told us that we need to do the free function after using this function, but every time i do free it breaks the program
The assignment is :
collect an input string.
every upper case letter to lower
every lower case letter to upper
if there is number do series of numbers from '9' until the input number but with out it (for '6' do '9','8','7'. (with out 6))
if there is other stuff don't add it in to the out put.
input example : A$q6#G4
output example : aQ987g98765
it is not allowed to change the input string.
in the input allowed to be every thing.
the output sting needs to be exactly in the array size
(if 123 = the size of will be input[2])
photo of the error
the error : wntdll.pdb contains the debug information required to find the source for the module ntdll.dll
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
/* Function declarations */
char Ex1FNumbers(char);
char Ex1FLetters(char);
/* ------------------------------- */
//
int main()
{
system("cls"); //delete when send
int select = 0, i, all_Ex_in_loop = 0;
printf("Run menu once or cyclically?\n(Once - enter 0, cyclically - enter other number) ");
if (scanf_s("%d", &all_Ex_in_loop) == 1)
do
{
for (i = 1; i <= 3; i++)
printf("Ex%d--->%d\n", i, i);
printf("EXIT-->0\n");
do {
select = 0;
printf("please select 0-3 : ");
scanf_s("%d", &select);
} while ((select < 0) || (select > 3));
switch (select)
{
case 1: //Ex1
{
int size, i, n, counter = 0;
char inPut[] = "";
char outPut[] = "";
char* Ptr_inPut_address, * Ptr_outPut_address, num;
printf("Please enter a string :\n");
scanf("%s", inPut);
size = strlen(inPut);
Ptr_outPut_address = &outPut;
Ptr_inPut_address = (char*) calloc(size+1 , sizeof(char));
Ptr_outPut_address = (char*) calloc(0, sizeof(char));
if (!Ptr_inPut_address || !Ptr_outPut_address)
{
break;
}
for (i = 0; i < size; i++)
{
Ptr_outPut_address = (char*)realloc(Ptr_outPut_address, counter);
if (inPut[i] >= 'a' && inPut[i] <= 'z' || inPut[i] >= 'A' && inPut[i] <= 'Z')
{
if (inPut[i] >= 'a' && inPut[i] <= 'z')
{
outPut[counter++] = inPut[i] - 32;
}
else
{
outPut[counter++] = inPut[i] + 32;
}
}
else if (inPut[i] <= '9' && inPut[i] >= '0')
{
n = '9' - inPut[i];
Ptr_outPut_address = (char*)realloc(Ptr_outPut_address, counter + n);
for (n; n > 0; n--) // "o" of 8 and not "n" because 8 is the max num for this "for".
{
outPut[counter++] = inPut[i] + n;
}
}
}
Ptr_outPut_address = (char*)realloc(Ptr_outPut_address, counter);
outPut[counter] = '\0';
Ptr_outPut_address = &outPut;
printf("%s\n", Ptr_outPut_address);
if (Ptr_outPut_address != NULL)
{
free(Ptr_outPut_address);
}
if (Ptr_inPut_address != NULL)
{
free(Ptr_inPut_address);
}
} break;
case 2: //Ex2
{
}break;
case 3: //Ex3
{
}break;
}
} while (all_Ex_in_loop && select);
system("pause");//delete when send
main();//delete when send
//return 0; // return when send
}
char inPut[] = "";
char outPut[] = "";
This declares two arrays that contain exactly one char, initializing them to '\0'. That's what the above means in C. This does not mean that these two arrays will have infinite size and can store any string. That's not how this works. But then, immediately afterwards:
scanf("%s", inPut);
This is guaranteed to overflow the array, since it is capable of holding only one char. Any string this reads will have at least two chars: the single read character, followed by '\0'. This results in memory corruption and undefined behavior.
There are several other bugs in the shown code. One more example:
Ptr_outPut_address = &outPut;
This has the effect of setting this variable to the starting address of a char array that was declared earlier.
Ptr_outPut_address = (char*)realloc(Ptr_outPut_address, counter);
You can only realloc something that was malloced, realloced, or calloced. No exceptions. You cannot realloc anything else. The char array was not malloced, realloced, or calloced. C does not work this way.
Several other problems exists in the shown code. Looks like this entire program was written all at once, before an attempt was made to test everything. This approach is very unlikely to succeed, and will likely produce many different kinds of bugs, such as the one that I've described. This makes it difficult to analyze and fix everything, since you're not looking for just one bug, but an unknown number of bugs. Plus it is likely that there will be an eventual realization that some or most of what was written need to be rewritten from scratch since the shown approach turned out to be fundamentally wrong.
Which is what you should probably do: start from scratch, write only a few lines of code, before testing them, and making sure that they work correctly before proceeding to write more code. If you attempt to fix just the problems that I explained it's likely that this will just create other problems, additionally, there are other problems as well, I just didn't mention them. The entire approach that was used here needs to be changed, fundamentally.

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.

Program runs too slowly with large input - C

The goal for this program is for it to count the number of instances that two consecutive letters are identical and print this number for every test case. The input can be up to 1,000,000 characters long (thus the size of the char array to hold the input). The website which has the coding challenge on it, however, states that the program times out at a 2s run-time. My question is, how can this program be optimized to process the data faster? Does the issue stem from the large char array?
Also: I get a compiler warning "assignment makes integer from pointer without a cast" for the line str[1000000] = "" What does this mean and how should it be handled instead?
Input:
number of test cases
strings of capital A's and B's
Output:
Number of duplicate letters next to each other for each test case, each on a new line.
Code:
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
int main() {
int n, c, a, results[10] = {};
char str[1000000];
scanf("%d", &n);
for (c = 0; c < n; c++) {
str[1000000] = "";
scanf("%s", str);
for (a = 0; a < (strlen(str)-1); a++) {
if (str[a] == str[a+1]) { results[c] += 1; }
}
}
for (c = 0; c < n; c++) {
printf("%d\n", results[c]);
}
return 0;
}
You don't need the line
str[1000000] = "";
scanf() adds a null terminator when it parses the input and writes it to str. This line is also writing beyond the end of the array, since the last element of the array is str[999999].
The reason you're getting the warning is because the type of str[10000000] is char, but the type of a string literal is char*.
To speed up the program, take the call to strlen() out of the loop.
size_t len = strlen(str)-1;
for (a = 0; a < len; a++) {
...
}
str[1000000] = "";
This does not do what you think it does and you're overflowing the buffer which results in undefined behaviour. An indexer's range is from 0 - sizeof(str) EXCLUSIVE. So you either add one to the
1000000 when initializing or use 999999 to access it instead. To get rid of the compiler warning and produce cleaner code use:
str[1000000] = '\0';
Or
str[999999] = '\0';
Depending on what you did to fix it.
As to optimizing, you should look at the assembly and go from there.
count the number of instances that two consecutive letters are identical and print this number for every test case
For efficiency, code needs a new approach as suggeted by #john bollinger & #molbdnilo
void ReportPairs(const char *str, size_t n) {
int previous = EOF;
unsigned long repeat = 0;
for (size_t i=0; i<n; i++) {
int ch = (unsigned char) str[i];
if (isalpha(ch) && ch == previous) {
repeat++;
}
previous = ch;
}
printf("Pair count %lu\n", repeat);
}
char *testcase1 = "test1122a33";
ReportPairs(testcase1, strlen(testcase1));
or directly from input and "each test case, each on a new line."
int ReportPairs2(FILE *inf) {
int previous = EOF;
unsigned long repeat = 0;
int ch;
for ((ch = fgetc(inf)) != '\n') {
if (ch == EOF) return ch;
if (isalpha(ch) && ch == previous) {
repeat++;
}
previous = ch;
}
printf("Pair count %lu\n", repeat);
return ch;
}
while (ReportPairs2(stdin) != EOF);
Unclear how OP wants to count "AAAA" as 2 or 3. This code counts it as 3.
One way to dramatically improve the run-time for your code is to limit the number of times you read from stdin. (basically process input in bigger chunks). You can do this a number of way, but probably one of the most efficient would be with fread. Even reading in 8-byte chunks can provide a big improvement over reading a character at a time. One example of such an implementation considering capital letters [A-Z] only would be:
#include <stdio.h>
#define RSIZE 8
int main (void) {
char qword[RSIZE] = {0};
char last = 0;
size_t i = 0;
size_t nchr = 0;
size_t dcount = 0;
/* read up to 8-bytes at a time */
while ((nchr = fread (qword, sizeof *qword, RSIZE, stdin)))
{ /* compare each byte to byte before */
for (i = 1; i < nchr && qword[i] && qword[i] != '\n'; i++)
{ /* if not [A-Z] continue, else compare */
if (qword[i-1] < 'A' || qword[i-1] > 'Z') continue;
if (i == 1 && last == qword[i-1]) dcount++;
if (qword[i-1] == qword[i]) dcount++;
}
last = qword[i-1]; /* save last for comparison w/next */
}
printf ("\n sequential duplicated characters [A-Z] : %zu\n\n",
dcount);
return 0;
}
Output/Time with 868789 chars
$ time ./bin/find_dup_digits <dat/d434839c-d-input-d4340a6.txt
sequential duplicated characters [A-Z] : 434893
real 0m0.024s
user 0m0.017s
sys 0m0.005s
Note: the string was actually a string of '0's and '1's run with a modified test of if (qword[i-1] < '0' || qword[i-1] > '9') continue; rather than the test for [A-Z]...continue, but your results with 'A's and 'B's should be virtually identical. 1000000 would still be significantly under .1 seconds. You can play with the RSIZE value to see if there is any benefit to reading a larger (suggested 'power of 2') size of characters. (note: this counts AAAA as 3) Hope this helps.

How to identify specific digits of an integer input in C?

I need to get the number of digits containing the number 1. I know in java I can take the input as a String and use charAt, but I understand there is no implicit String function in C. How can I accomplish this?
Division and modulus are your friends.
#include "stdio.h"
int main(){
int digits[] = {0,0,0,0,0,0,0,0,0,0};
int i = 11031;
while(i > 0){
digits[i % 10]++;
i = i / 10;
}
printf("There are %d ones.\n", digits[1]);
}
Homework?
You'd read it into a char* using the fread() function, and then store how many bytes were read in a separate variable. Then use a for loop to iterate through the buffer and count how many of each byte are present.
Try something like...
int digit = 0;
int value = 11031;
while(value > 0)
{
digit = value % 10;
/* Do something with digit... */
value = value / 10;
}
I see this as a basic understanding problem, which inevitably everyone goes through switching from one language to the next.
A good reference to go through to understand how string's work in C when you've started familiarity with java is look at how string.h works. Where as in java string's are an Object and built in, strings in C are just integer arrays.
There are a lot of tutorials out there, one that helped me when I was starting earlier in the year was http://www.physics.drexel.edu/students/courses/Comp_Phys/General/C_basics/ look at the string section.
Sometimes asking a question speeds up learning a lot faster than pouring through the text book for hours on end.
If you have just the number, then you can do this:
int val; //Input
...
int ones = 0;
while(val != 0) {
ones += ((val % 10) == 1) ? 1 : 0;
val /= 10;
}
If you have a string (char*), the you'd do something like this:
while(*str != '\0') {
if(*str++ == '1') {
ones++;
}
}
It's also worth noting that c does have a charAt function, in a way:
"java".charAt(i) == "c the language"[i];
By indexing into the char*, you can get the value you want, but you need to be careful, because there is no indexOutOfBounds exception. The program will crash if you go over the end of a string, or worse it may continue running, but have a messed up internal state.
Something along the lines of:
int val=11031;
int count=0;
int i=0;
char buf[100];
sprint(buf, "%d", val);
for(i=0; (i < sizeof(buf)) && (buf[i]); i++) {
if(buf[i] == '1')
count++;
}
int count_digit(int nr, int digit) {
int count=0;
while(nr>0) {
if(nr%10==digit)
count++;
nr=nr/10;
}
return count;
}
This sounds like a homework problem to me. Oh well, it's your life.
You failed to specify the type of the variable that contains the "input integer". If the input integer is an integral type (say, an "int") try this:
int countOnes(int input)
{
int result = 0;
while(input) {
result += ((input%10)==1);
result /= 10;
}
return result;
}
If the "input integer" is in a string, try this:
int countOnes(char *input)
{
int result = 0;
while(input && *input) {
result += (*input++ == '1');
}
return result;
}
Hope this helps. Next time, do your own homework. And get off of my lawn! Kids, these days, ...
int countDigit(int Number, int Digit)
{
int counter = 0;
do
{
if( (Number%10) == Digit)
{
counter++;
}
}while(Digit>0)
return counter;
}
Something along the lines of this:
#include <stdio.h>
main() {
char buf[100];
char *p = buf;
int n = 0;
scanf("%s", buf);
while (*p) {
if (*p == '1') {
n++;
}
p++;
}
printf ("'%s' contains %i ones\n", buf, n);
}
This will do it. :-)
int count_digits(int n, int d) {
int count = 0;
while(n*10/=10) if (n%10==d) count++
return count;
}
For all those who refer to the question as the homework question: I have to say, most of you provided a homework answer.
You don't do division/modulus to get the digits in production code, firstly because it's suboptimal (your CPU is designed for binary arithmetics not decimal) and secondly because it's unintuitive. Even if it's not originally a string, it's more optimal to convert it to one and then count the characters (std::count is the way to go in C++).

Resources