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;
upperletter;
specialchar;
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);


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

Post a Reply

Replies

- page 2
Oldest  Newest  Rating
0 Eric Hines · January 12, 2016
also, is there something special about "i" in variables? I see it all the time
0 Linguist Llama · January 12, 2016
Interesting... Unfortunately it's much harder for me to ask questions that gauge your understanding (and have you respond to them) than it is for you to ask questions about something you don't understand. Would it help if I prompt for questions occasionally? I shouldn't need to, but I'll give it a try. It's probably in your best interest to quote the sections you don't understand when you ask questions about them. I noticed you're pretty good at this.

Another pressing issue is whether an answer to one question tends to raise ten more. If this is the case, you should probably find a new resource to learn from, as there are more questions going unanswered than answered by the guide you're using.

I prefer not to talk about things that are stylistic. If you offend me slightly with your indentation, for example, I probably won't raise it as an issue unless I think it's caused you to perceive functionality incorrectly.

There is nothing special about the variable 'i'; you're right to observe that it's a commonly used identifier for an integer variable. It's mostly a stylistic choice. A questionable one, mind you, as pervasive use of meaningless identifiers can make code difficult to maintain, hence style can affect functionality as I mentioned earlier... Use with care. That's all I have to say on any matters that are mostly stylistic choices.

You can't (and shouldn't) use scanf to read a character into an int. This is a matter of functionality; it's simply not an appropriate type choice. When scanf sees %c, it expects a pointer to character (e.g. char, signed char or unsigned char). You're giving it a pointer to int. That is bad...

As far as scanf is concerned there's a pointer to a single byte, the single byte gets written to but an int occupies more than one byte, and the rest of the bytes don't get assigned a value...

You might notice in six months time your program doesn't function correctly on some platforms, because it uses uninitialised garbage. Hopefully before then you might have noticed compilation warnings hinting to this problem, but unfortunately you probably won't get an error because scanf is a variadic function... Feel free to ask questions about warnings! Try to avoid ignoring them... They're there for a purpose, you know?

Similarly, isupper or isdigit (or any of the other <ctype.h> functions) are only safe to use on unsigned char values and EOF. Again, you won't get a compiler error if you try to use some negative values that aren't EOF, but you might see runtime errors in six months time... Unfortunately it's not yet common for a compiler to perform this kind of analysis and offer warnings, so you probably won't get a warning about this one.

Either change upperletter, specialchar and number to unsigned char or use getchar/fgetc. getchar/fgetc return int, which is common with the types of variables you have defined...

getchar/fgetc return an `unsigned char` value as an int or EOF which is not an unsigned char value... The `<ctype.h>` functions operate on unsigned char value or EOF. Do you see that it's a perfect match? These functions fuse together well, which means you could write something like: int c = tolower(getchar()); and the return value would be converted to uppercase when it's an alphabet character, or left unchanged when it's not... The output for one function is compatible with the input for the other.

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.



This is a great question, and should be given much design thought! I guess it all boils down to:


  • How will your users use the program? You probably haven't put much thought into this, because Bucky teaches from an IDE and doesn't deviate from that... Consider that it's a console program. This means they're console users used to writing things like `cd other_directory`, `ls -R some_filename`, `cc your_c_code.c` and so on... When you know the console intimately and you know how to program using the console, you'll realise that none of these programs use getchar, scanf, fgets or fread for that matter. They almost always resort to using argv instead. It's simpler for the user and for the programmer.

  • What will you be doing with the user input? In this case it's obvious that you read one password, check it and output some message... then you're done. If you needed to read and check a batch of passwords, then you would need getchar, scanf or stdin... but if you're only checking one, it would be preferable to use argv.



I mentioned argv, which you might not have seen. That would be most unfortunate, but I'm the one who brought up the question so I'll cover it briefly with an example... Feel free to ask questions.

#define UPPERCASE_NEED 1
#define NUMBER_NEED 2
#define SPECIALCHAR_NEED 4 /* ... and so on, doubling each time so our flags don't clash in meaning */

unsigned int password_check(char *str) {
   unsigned int uppercase_need = UPPERCASE_NEED,
number_need = NUMBER_NEED,
specialchar_need = SPECIALCHAR_NEED;
   for (size_t x = 0; str[x] != '\0'; x++) {
       unsigned char c = str[x]; /* str[x] needs to be coerced from a char to an unsigned char
                                  * to safely pass to isupper, etc */
       if (isupper(c))      { uppercase_need = 0;   }
       else if (isdigit(c)) { number_need    = 0;   }
       else if (isgraph(c)) { specialchar_need = 0; }
       /* and so on */
   }
   return uppercase_need + number_need + specialchar_need;
}

int main(int argc, char **argv) {
    /* argc tells you how many elements of argv you can access. Assuming argc is greater than or equal to one, argv[0] is almost always your program name; it's fairly useless. Assuming argc is greater than one, argv[1] will be the first argument... so you can write logic based on the first argument like so: */
    if (argc <= 1) {
        puts("usage:  ./app your_password");
        exit(EXIT_FAILURE);
    }
    int password_needs = check_password(argv[1]);
    if (password_needs >= UPPERCASE_NEED) {
        puts("Your password needs an uppercase letter...");
    }
    else if (password_needs >= NUMBER_NEED) {
        /* ... and so on */
   }
}


I think this is what Laura was getting at; you don't need (and shouldn't use) fgets, scanf, getchar or fread for this program.

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



Of course your program is meant to perform the checking. Note that I've used the term 'string' a number of times, and in fact my code relies upon string processing (hence the '\0' checks). You have a fixed width field that requires the user enter precisely three characters (not including the commas from the format string, which I gather are erroneous). However the exercise speaks about a string. A fixed width field doesn't necessarily contain a string. Do you have any questions?

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



I also noticed it's quite common for Bucky to post-pone coverage of a topic. I hope he managed to complete the series? If not, that's no big deal, as there are books that have stood the test of time... Any questions about this?

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.



fgets is admittedly much more difficult to abuse. It begs the question, rather than leaving the question unasked. Nonetheless, if you ask the same questions about scanf you'll find it can solve the same problems (and then some).
0 Eric Hines · January 13, 2016
Thanks again,

 I do agree with you; there IS a lot I don't know and perhaps finding another source for answers may be best (perhaps even a class) because a lot of my questions are too basic. I'd just end up chasing a white rabbit around and would end up annoying you, so perhaps something a bit more structured like a class would be appropriate.

 And as you mentioned also, one answer just leads to more questions. I may just keep doing as much research on my own as possible and if I'm just absolutely stuck I could resort to the forums. Or maybe if I need help with a general idea of how something works I could stop by here. 


......by the way. I think I've found out the answer of how to go about (at least one way) checking each password character to verify the conditions which I set are met. I have seen a lot of other people using for loop with nested ifs. This is what I'm seeing and this is my interpretation of why it works. (my main concern is whether I'm understanding the a++ rule......

a=0;
for(password[a]; a<=20 ; a++ )                                 
{                                                        
if(isupper(password[a]))         
{                                                    
upperflag = 1; 

.

.

.

.etc                            
                                                     
    it's saying; begin checking with the first character of the password, end with the last character of the password, after each circuit of the loop, increment by one to check the next character in line. In other words, am I right that the for loop can only check one character at a time and it must see if it meets one of the if statements? And if it does, it is assigned a value of 1 which when tested later on and a

 "there are books that have stood the test of time"

 are there any you recommend for someone like me who is just beginning?
0 Eric Hines · January 13, 2016
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#include <string.h>

int main()
{
char password[3];
int i=0;
int upper=0;
int num=0;
int dollarsign=0;

printf("Enter your password. (must be 3 characters, one uppercase, one lowercase, one number)");
scanf("%s",password);


for(i=0;i<=3;i++)
{
    if(isupper(password))
    {
        upper=1;
    }
    if(isdigit(password))
    {
        num=1;
    }
    if(isdigit(password=='$'))
    {
        dollarsign=1;
    }
}
if(upper==1 && num ==1 && dollarsign ==1)
{
    printf("Thank you for entering your password. \n");
}
else
{
    printf("Your password does not meet the minimum requirements. \n");
}
return 0;
}


I thought I wrote it correctly, but I'm just getting the same thing; your password doesn't meet the min requirements. The password I entered was A1$
0 Eric Hines · January 13, 2016
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#include <string.h>

int main()
{
int a,b,c;
int i;
char password[5];

printf("enter password. must contain upper letter, number and, $ sign");
scanf(" %s", password);

for(i=0; i<=5; i++)
{
    if(isupper(password))
    {
        a=1;
    }
    if(isdigit(password))
    {
        b=1;
    }
    if(isdigit(password))
    {
        c=1;
    }
}
if(a==1&&b==1&&c==1)
{
    printf("thank you");
}
else
{
    printf("try again");
}
return 0;
}



It's working now.... how frustrating
0 Linguist Llama · January 13, 2016
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"?

char password[3];
printf("Enter your password. (must be 3 characters, one uppercase, one lowercase, one number)");
scanf("%s",password);



This is wrong. When the user enters three characters, four characters will be written into password... but password can only store three. That's called a buffer overflow. Maybe your code doesn't crash now, but one day you'll be writing something complex and occasionally you'll notice a crash or some strange values... It'll also be nigh impossible to debug, because the code failure will likely occur at a strange, seemingly irrelevant place.

What's the fourth character?

This is a question that the resource you use should have explained already.

K&R 2E has existed for decades, and the people who read it don't tend to have this kind of basic misunderstanding. I'm not trying to be offensive. It's certainly not a book for beginners, but I think you'll get through it. Make sure it's the 2nd edition. Do the exercises as you come across them; don't move on leaving unfinished business. If you have any problems with the exercises, ask here. In fact, if you post your solution to the exercises here we can tell you how you went...

It's a thin book, only 189 pages excluding the reference at the end of the book, but it's dense information. Read it carefully; it tends to take people months to soak it all in.

If cost is an issue, see if you can borrow it from a library. If that's not an option, ask here and we'll see if we can, as a community, find the most accurate, freely available resource on the internet.

Let's raise the bar!
0 Shlesh Tiwari · January 14, 2016
Hi all, 
I am new to programming, so I would like to know if there is a more efficient way of writing the code. It works, but I was just wondering. 

#include <stdio.h>
#include <stdlib.h>

int main()
{
    char password[20];
    int i;
    int upper = 0;
    int lower = 0;
    int num = 0;
    int special = 0;
    printf("Enter a password (max length 20) - Must contain at least one ");
    printf("upper case, lower case, number, and a special character - & , * or $\n");
    scanf(" %s", password);
    for (i=0;i<=20;i++){

        if ( isalpha(password)){
            if ( isupper(password)){
                printf("Upper case\n");
                upper++;
            }else{
                printf("Lower case\n");
                lower++;
            }
        }else if ( isdigit(password)){
            printf("Number\n");
            num++;
        }else if ( (password == '$')||(password == '&') || (password == '*')){
            printf("Special character\n");
            special++;
        }else{

        }
    }
    if ( upper && lower && num && special){
        printf("Password accepted");
    }else{
        printf("Invalid password");
    }
    return 0;
}

Thanks in advance. 
  • 1
  • 2

C

107,222 followers
About

One of the most popular languages of all time.

Links
Moderators
Bucky Roberts Administrator