C challenge #1

+1 Eric Hines · January 11, 2016
I'm up to Bucky's tutorial #33. 

Here's my code, but only part of it works. I get the else statement to print. I can't seem to get the password to work....

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>

int main()
int upperletter;
int specialchar;
int number;

printf("Enter your password. The first entry must be an upper case letter. The second must be a special character. The last must be a number.\n");
scanf("%c,%c,%c\n", &upperletter,&specialchar,&number);

    printf("Thank you. %c%c%c is a valid password.\n",upperletter,specialchar,number);
    printf("Your password does not meet the requirements.\n");
return 0;

Post a Reply


- page 1
Oldest  Newest  Rating
+2 Laura Lee · January 11, 2016
Scanf might be useful for formatted strings, but it is recommended to input all data as strings and convert them yourself.
I try to avoid it whenever I can and now I never really have the need for it. Scanf does not provide a buffer size for input and could cause beginners to buffer overflow. Scanf also does not clean the input stream. There can be cases where fgets also can't clean the stream in cases where the input provided exceeds the size of the buffer, but that is fairly common sense. I have a habit of clearing the input stream myself each time I use it.
+1 Linguist Llama · January 11, 2016
Greetings. I don't entirely agree that scanf should be avoided entirely in favour of fgets, as there are operations that scanf can perform that fgets can't. However, I do agree that in this case fgets is the more suitable tool for the job.

Having said that, it's also important to face the issues you're facing when you use scanf. First and fore-most, and this goes for the majority of C-standard functions (e.g. `fopen`, `fgets`, `malloc`), your logic needs to CHECK THE RETURN VALUE... For example:

int n = scanf(...);

If you use scanf to read one value, you can expect a successful call to scanf to return 1, so n will be 1 in that case. If you use scanf to read two values, n should be 2, or else you can't rely upon both of the values to be determinate. Thus your logic should probably look something like this:

unsigned char upperletter, specialchar, number;
int n = scanf("%c,%c,%c", &upperletter, &specialchar, &number);
if (n == 3) { /* You're good to go! All three characters can be used safely */ }
if (n == 2) { /* Only upperletter and specialchar can be used safely */ }
if (n == 1) { /* Only upperletter can be used safely */ }
if (n == 0 || n == EOF) { /* None of those variables can be used safely. */ }

About the commas in your format string "%c,%c,%c\n", that probably doesn't mean what you think it does. This is telling scanf to expect that the user will enter a comma between the characters, so you'll probably notice scanf returns 1 when you enter input like "abc" instead of "a,b,c".

The \n doesn't mean what you might think it means, either. A single whitespace character of any kind (space, tab, newline, etc) will cause scanf to read and consume as many whitespace characters as possible, so you might notice \n also causes spaces to be discarded.

Secondly, take note of warnings. Your compiler is likely warning you that the type of your scanf arguments are conflicted. On the one hand, when you give `%c` to scanf you tell scanf the argument will be a character pointer. On the other hand, all of your arguments are int pointers. As the C standard says at < http://port70.net/~nsz/c/c11/n1570.html# >

> ... If this object does not have an appropriate type, or if the result of the conversion cannot be represented in the object, the behavior is undefined.

The appropriate types in this case are char, unsigned char or signed char. Not int.

I'd suggest using `unsigned char`, if you continue to use scanf for this for whatever crazy reason. The reason is that isupper and isdigit are only defined to operate on unsigned char values and the EOF value; any other values will produce undefined behaviour. It's much more compatible with getchar.

It is unfortunate that most people habitually use any and all of these input/output functions incorrectly. Read the manuals and be well informed...
+1 Linguist Llama · January 11, 2016
Hi, Laura. I hope you will let me help you find some food for thought today... As you're reading this, try to understand that I wouldn't spend so long writing something up if I didn't think it was for a good reason. I can see potential in you, and I don't mean to seem rude when I say this... Teachers who are unwilling to try new things and to learn new things are not near as useful as those who realise, life is one big lesson and we don't stop learning simply because we've graduated from university or been working for ten years or whatever. I think you're great, but you could be soooo much better!

edit: I'm aware that the formatting is shitpoor. I tried :( but the editor isn't exactly WYSIWIG if you know what I mean.

it is recommended to input all data as strings and convert them yourself

The only people you'll find who recommend reading input this way tend to do so either because:

  • They don't know how to use `fscanf` properly; these people made a mistake, found out the hard way (probably a bug in production) and only went so far as to conclude that they weren't using `fscanf` correctly (or as they'll put it, `fscanf` is to blame). By this logic if we chop off our legs by abusing a circular saw, the saw is to blame...

  • They went one step further and learnt how to use `fscanf` properly (or at least semi-properly), but use the fact that people find it easy to misuse `fscanf` to support their argument. By this logic, people shouldn't use C because it's too easy to mess up; stick to Python or Java.

Either way, this is appealing from ignorance. I'd rather not use such a citation, partially because it makes me look ignorant, as though I've just read some advice on a forum and blindly followed it without looking deeper into the issues and forming an educated understanding.

I never really have the need for it.

Again, you're not going to find a citation to support that, because it's speculative. By this logic I could say you don't need computers, to support a recommendation that you shouldn't use computers.

Like computers, we don't need many aspects of C; they're purely there for our convenience. It is convenient to be able to convert a sequence of decimal digit characters straight from a stream to an int or what-not, without an intermediate "buffer" (which makes it more optimal, by the way). The "continue;" expression is another example of convenience.

Scanf does not provide a buffer size for input and could cause beginners to buffer overflow.

This is really two statements disguised as one. Those statements need to be addressed separately.

Scanf does not provide a buffer size for input...

According to C11, section paragraph 3...

Each conversion specification is introduced by the character %. After the %, the following appear in sequence:

  • An optional assignment-suppressing character *.

  • An optional decimal integer greater than zero that specifies the maximum field width (in characters).

The emphasis is mine; technically speaking, `fscanf` (and `scanf`, by consequence) gives you an option to provide the size of the array, and then it won't cause an overflow.

I assume you're talking about use of `%s` here, as people who learn about `%[` tend to also learn about maximum field widths along-side. An important point to note is that `%s` serves a subtly different purpose to `fgets`; if you were to use `%511s` to read into an array of 512 char, you would probably find that `fscanf` reads a single whitespace-delimitered token, as opposed to a newline-delimitered token. This forms a unique usecase that `fgets` can't solve on it's own.

... could cause beginners to buffer overflow.

If your argument is that people shouldn't use functions without first fully reading and understanding the manual, you're right. C isn't a very beginner-friendly language, unfortunately. Beginners who aren't willing to do a lot of reading should probably stick to a language that doesn't have undefined behaviour, such as Python or Java. Alternatively, reading the manual prior to using a function serves to be colossally useful, even in languages such as Python and Java; by doing so we can:

  • Avoid common pitfalls caused by ignorant "newbies", because we've avoided ignorance by being proactive about our understanding.

  • Learn about immensely useful nuances of functions that we wouldn't have learnt about otherwise, particularly if we just packed that function into the "too hard" box and left it there as so many people do :(

Scanf also does not clean the input stream. There can be cases where fgets also can't clean the stream in cases where the input provided exceeds the size of the buffer, but that is fairly common sense. I have a habit of clearing the input stream myself each time I use it.

My response to this may surprise you... It's good that you've correctly understood that `fgets` also doesn't solve this problem fully either; you need to resort to a loop, don't you? No...

Well, surprise surprise, you can solve this problem using `fscanf` (and by consequence `scanf`) alone... Observe:


This also forms a unique usecase for `scanf`. Writing such a `getchar`/`fgetc`-loop is, by contrast, error-proned and tedious. Unfortunately most uses of `getchar`/`fgetc` are as incorrect as most uses of `scanf`/`fscanf`, particularly when used in conjunction with `<ctype.h>` functions (which is rather unfortunate, since `getchar`/`fgetc` are fusional with `<ctype.h>` functions). and the same goes for `fgets`. People tend to think these functions just do whatever is intuitive in their minds, but that which is intuitive is subjective, at least initially.

I hope I've opened your mind, Laura. I spent a long time writing this because I can see that you can be an AWESOME teacher. You just need to keep your mind open, think for yourself and do research to support your beliefs. I know, it can be hard to admit when we're wrong, particularly at first... but it gets easier over time. Trust me, stick in there and step outside of your comfort zone and it'll become second nature to you. :)
0 Laura Lee · January 17, 2016
There are far more efficient ways of writing the code Shlesh. But according to Linguistic Llama, I have implicitly signed an N.D.A in which I can not offer anything more than the statement above.
0 Eric Hines · January 17, 2016
So I've got a different project I'm working on. I'm using the boolean to turn the LED on/off. 

But instead of that I'd like for it to blink using a loop. 

I'd like to retain the ability to use a single button to start the loop and when pressed a second time to stop the loop.

void loop()
  if (irrecv.decode(&results))
    if (results.value == buttonA)
     if (millis() - last1 > 200) 
      lightState1 =!lightState1;
      last1 = millis();
    if (results.value == buttonB)
      if (millis() - last2 > 200)
      lightState2 =!lightState2;
      last2 = millis();  

    if (results.value == buttonC)
       if (millis() - last3 > 200) 
       lightState3 =!lightState3;
       last3 = millis();
0 Linguist Llama · January 18, 2016
Do you wonder why this code needs to be more efficient? Is it not processing passwords as quickly as it needs to? Unfortunately, you've missed the point of the exercise.

Are you trying to learn C? If so, then solve the exercises and move on.

Are you trying to learn how to improve the efficiency of a system? I strongly recommend taking a course on system analysis, if that's the case.
0 Eric Hines · January 18, 2016
Language Lama,

 To whom are you referring exactly?
0 Linguist Llama · January 18, 2016
Eric: I'm happy to engage with you, when you demonstrate that you wish to engage with me. Please answer these questions I asked you earlier:

1. Given an array of 1 character, the size is 1 but the only character is array[0]. Should you loop <= 1 or just < 1?
2. Given an array of 20 characters, the size is 20 but the only characters are [0..19] (count them; the first index is 0 and the last index is 19). Should you loop <= 20 or just < 20?
3. Given an array of 20 characters, if the user enters only 5 characters should you loop < 20 or < 5?
4. Do you remember when I wrote "isupper or isdigit (or any of the other <ctype.h> functions) are only safe to use on unsigned char values and EOF"?

In addition to those questions: Have you had a chance to look at K&R?
0 Laura Lee · January 11, 2016
You do not need all those headers.
You do not need to use scanf on your conditions, that doesn't make sense.
You are meant to retrieve a string passcode from the user then check all the conditions yourself.
Also avoid using scanf and use fgets instead.
Checking every condition at once isn't such a good idea, since you could not specifically provide under which condition the passcode failed.
0 Eric Hines · January 12, 2016
I appreciate all the help. But since I know practically nothing of C or programming (programming is a lot like my logic class in college, but I wasn't the best at it, so I don't think I'll make programming a career choice), I think I'll need some simpler explanation. Some things on my mind;

1). How do I write my programs so they are based on user input how do I make them interactive? More specifically;
Bucky's tutorials are awesome, but typically he starts out declaring the values of variables outright. for example; 

int main()
int tuna='1';

printf("%c is a letter", tuna);
        printf("%c is a number", tuna);

return 0;

He set tuna ='1'  in order to demonstrate what the rest of the program does for the sake of the student learning, and I understand this. But, I want to get away from it. I'd like to prompt the user/ make the program interactive. So my question is, how do I leave the variable "open" if you will, so the user can input whatever they choose/ program reads their input/ the program reacts based on that input.

"you do not need to use scanf on your conditions, that doesn't make sense."/ "You are meant to retrieve a string passcode from the user then check all the conditions yourself."

.....it wasn't clear that I was personally to be the one to check the code. I assumed the point of the lesson/challenge was to prompt the user with instructions/ write code to validate they've followed them/ then inform them if they have or haven't followed the directions for a password. 

"you do not need all those headers"  Yes. However, Bucky did recommend we start getting used to them albeit they may not be necessary yet. 

"checking every condition at once isn't such a good idea...."  I agree. I did think about this, but I stayed positive and motivated in an attempt to figure out if I could pull it off. 

 I suppose in that sense scanf is what automatically came to mind. Which leads to my next problem

2). Not quite getting the benefit of fgets over scanf a). in general (aside from what Bucky said about accepting spaces which scanf won't)  and  b). for this particular exercise.

So again, this is where I'm leaning...

"password must be min of x characters. It must have at least 1 uppercase letter, 1 special character, and 1 number. Enter your password now.
User inputs their password.
program checks it
informs user if they're good to go or not (because of x reason why).

Thanks again for the help. Usually my cries for help in forums are passed over, so it's cool seeing the conversation being generated. Also, I love constructive criticism. It helps me grow and learn. And, I have thick skin, so lay it on (professionally, however).  Just be ready for a lot of questions  : D  because I'm having fun and I want to improve. 

Thank you again. 

"the only stupid question is one that is never asked/ the worst they can do is say no"
  • 1
  • 2



One of the most popular languages of all time.

Bucky Roberts Administrator