1
\$\begingroup\$

The primary purpose of this program is to practice catching if the user enters data that does not correspond to expected input. Note if the user enters their name as "123", this is valid as it can be used as an identity, however if users enter only spaces (i.e. " ") or just a newline char then their input will be rejected. Also note preceding and trailing spaces are OK as long as there is some form of symbol present in the entire name.

#include "stdafx.h"
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

#define BUFFSIZE 10

/*Checks to see if name contains actual characters and not just whitespace*/
bool isThereChars(const char * name)
{
    bool charCheck = false;
    for (size_t index = 0; index < BUFFSIZE; ++index) {
        if (name[index] != ' ' && name[index] != '\0')
            charCheck = true;
    }
    return charCheck;
}

void retreiveName(char* name)
{
    for (;;) {
        if (fgets(name, BUFFSIZE, stdin) != NULL) {
            name[strcspn(name, "\n")] = '\0';
            if (name[0] == '\0' || isThereChars(name) == false) {
                printf("Invalid name entered please try again...\n");
            }
            else
                return;
        }
        else
            printf("Memory could not be allocated please try again...\n");
    }
}

int main(void)
{
    char name[BUFFSIZE];

    printf("Enter Name: ");
    retreiveName(name);

    printf("Your name is: %s\n", name);
    printf("Press enter to continue...\n");
    getchar();

    return 0;
}
\$\endgroup\$
1
\$\begingroup\$

Nitpick

void retreiveName(char* name)

This should be

void retrieveName(char* name)

Bugs

        if (name[index] != ' ' && name[index] != '\0')

What happens if I enter tabs or unprintable characters? This code would pass a string of all tabs. Is an all numbers string valid?

        if (isalpha(name[index]))

Now we check to see if there is a letter rather than excluding only two bad values.

The <ctype.h> header has this and other functions that you can use. E.g.

        if (isprint(name[index]) && !isspace(name[index]))

This might be what you want if you have a really liberal definition of name.

Second call

What happens if you call retrieveName twice with the same character array? In particular, let's give it an input of "Christopher".

#define BUFFSIZE 10

Christopher has eleven letters, so it overwhelms the buffer size. That's OK the first time, but the second call would get the remainder of the first call, not a new name. So the second time you'd be processing the string "r\0ristophe". That includes a letter, so it will return true. Not great, since we wanted a new name.

Now let's change that a bit. Instead of Christopher, enter " Chris ". Again, that's eleven characters. It overwhelms the buffer. The second call results in the string " \0Chris ". Now, you

    for (size_t index = 0; index < BUFFSIZE; ++index) {

When index is 2, it finds a C, which is neither a space nor a null. So the function returns true on a string that everything else will interpret as " ". Oops!

And note that this could also happen with two calls that don't overwhelm the buffer. E.g. the strings "Chris" and " " will call it with " \0ris\0\0\0\0" the second time, assuming the character array was zero-initialized.

Consider instead

    for (size_t index = 0; index < BUFFSIZE && name[index] != '\0'; ++index) {

Then the loop will stop at the end of the buffer or the end of the string, whichever comes first.

What about other lengths?

bool isThereChars(const char * name)

This only works with one size of buffer. It would make more sense to allow arbitrary length buffers.

bool hasLetter(const char * name, int bufferLength)

This allows the caller to specify a buffer length.

I also like the name hasLetter better than isThereChars. Your mileage may vary. Another option is hasAlpha, to match isalpha.

No need for a flag variable

    bool charCheck = false;
    for (size_t index = 0; index < BUFFSIZE; ++index) {
        if (name[index] != ' ' && name[index] != '\0')
            charCheck = true;
    }
    return charCheck;

I'd like this better as something like

    for (size_t index = 0; index < BUFFSIZE && name[index] != '\0'; ++index) {
        if (isalpha(name[index])) {
            return true;
        }
    }

    return false;

This also has the side effect of returning as soon as you find an alphabetic character rather than processing all the other characters even though they won't change the answer.

\$\endgroup\$
  • \$\begingroup\$ Thank you for taking the time ans going through and catching those errors, and also explaining what happens with examples! Big help because now i see whats going on! \$\endgroup\$ – chris360 Sep 8 '16 at 17:52
  • \$\begingroup\$ I am trying to rerun the code using the isalpha function like you showed and Microsoft Visual Studio is aborting the program with a debug assertion error, any idea as to why this is happening? I've looked around for a good hour and tested several times and am still unsure as to why its throwing the assertion error. Thanks! \$\endgroup\$ – chris360 Sep 8 '16 at 18:58
  • \$\begingroup\$ Note I found out what causes this error! passing a variable x, to any ctype function must be an unsigned char or the MACRO value of the EOF. Therefore changing my char array to that of type unsigned mad the program execute without errors! \$\endgroup\$ – chris360 Sep 8 '16 at 19:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.