1

I made a C program that takes a string input from the user and prints it. To store the string I created a character array. But, even if the size of the character array is less than the size of the input I give, the program works fine. I tried with gets() and puts() but, there is no change in result.

name.c

#include <stdio.h>

int main()
   {
       char name[2];
       printf("Enter Your Name\n");
       scanf("%s", name);
       printf("%s", name);
   }

In this program (name.c), the name array can store a string with a length of more than 2 characters. This only happens when we are taking input in the program.

name2.c

#include <stdio.h>

int main()
    {
        char name[2] = "Steve";
        printf("Your name is %s", name);
    }

However, in the program (name2.c) compiler gives warning

[Warning] initializer-string for array of chars is too long

Why does this happen?

  • 1
    `char name[2];` can validly store a single character string (plus `'\0'`). `char name[2] = "Steve";` initializes `name` to `"St"` -- it is no longer a valid string (it's just a character array holding 2-characters at this point) and your `printf()` invokes *Undefined Behavior* -- *Don't Skimp on Buffer Size* Instead, as a 2nd line `#define MAXNAME 64` and then declare `char name[MAXNAME];` and all will be well. (you can also do `char name[sizeof "Steve"] = "Steve";` but I wouldn't recommend it. – David C. Rankin May 29 '21 at 03:33
  • @DavidC.Rankin I understand the part that you explained but my question was on the first program. There `char name[2]` can store string with multiple characters though it is supposed to store just one character. – Kshitij Bhandari May 29 '21 at 03:52
  • 1
    You are seeing *Undefined Behavior*. Your program can appear to work normally or SegFault or everything in between. – David C. Rankin May 29 '21 at 03:56
  • 1
    This is undefined behavior, it means that the language doesn't define what should happen, so you don't know what will happen, it may store the characters or may cause an error, the C language is intrinsically insecure, most of the responsibility for making safe code is on the programmer. – isrnick May 29 '21 at 03:59

1 Answers1

1

Let's summarize your first attempt:

#include <stdio.h>

int main()
{
    char name[2];                       /* can hold 2-characters */
    printf ("Enter Your Name\n");
    scanf ("%s", name);                 /* no check of return, no field-width */
    printf ("%s", name);                /* UB if more than 1 char entered */
}

If more than 1 character is entered by the user, you invoke undefined behavior on your call to scanf() overwriting your array bounds and on printf() (depending on the implementation) as name is not a nul-terminated string. See: Undefined, unspecified and implementation-defined behavior and What is indeterminate behavior in C++ ? How is it different from undefined behavior? and Undefined behavior

The rule is Don't Skimp on Buffer Size!, the rule of thumb is take your longest expected input and at minimum double that. For names, there are a few that can reach 32-characters, so start with a size for your name array of 64.

Now let's look at a minimal way to avoid problems (not a great approach, but better):

#include <stdio.h>

#define MAXNAME 64      /* if you need a constant, #define one (or more) */

int main()
{
    char name[MAXNAME];                 /* can hold 63-character name */
    printf ("Enter Your Name\n");
    if (scanf ("%63s", name) != 1) {    /* validate return, use field-width */
        puts ("(user canceld input)");
        return 0;
    }
    printf ("%s\n", name);              /* guaranteed no UB */
}

Above a constant is defined for the maximum name length MAXNAME and it is used to initialize name. If you later need to change the length, there is one simple and convenient place to do it.

You cannot use any input function correctly unless you Check The Return. Here you need to protect against the user generating a manual EOF by pressing Ctrl + d (Ctrl + z on windows).

You cannot use scanf() to read into an array correctly unless you provide a field-width modifier to prevent writing beyond your array bounds if the user enters more characters than your array can hold. Without a field-width modifier, scanf() into a character array is no safer than gets(), Why gets() is so dangerous it should never be used!. So you limit the number of characters scanf() will read with "%63s" (you must use an integer literal, a variable or macro will not do)

Also note, you can only read up to the first whitespace with "%s"

A better approach is to read the input with fgets() and then use strcspn() to trim the '\n' at the end of the buffer filled by fgets(). You will then be able to read names with whitespace, like "Mickey M. Mouse" and you will consume the entire line of input (up to 63 characters, including the '\n' character generated by the user pressing Enter). With scanf() and "%s" the '\n' is left in stdin unread and if your next attempt is with a line-oriented input function like fgets() or POSIX getline(), the input will not read anything except the '\n' left in stdin.

#include <stdio.h>
#include <string.h>

#define MAXNAME 64      /* if you need a constant, #define one (or more) */

int main()
{
    char name[MAXNAME];                     /* can hold 63-character name */
    
    fputs ("Enter Your Name: ", stdout);
    if (!fgets (name, MAXNAME, stdin)) {    /* reads up to 63 chars */
        puts ("(user canceld input)");
        return 0;
    }
    
    name[strcspn (name, "\n")] = 0;         /* overwrite \n with \0 */
    
    printf ("%s\n", name);                  /* guaranteed no UB */
}

Look things over and let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • I'm not sure what you are saying, if you have `char a[100]` and `if (scanf ("%99s", a) != 1) return 1;` will most certainly cancel input when `Ctrl + d` is pressed on Linux and when `Ctrl + z` is pressed on windows (though you may have to do that twice on windows) This creates what is called an *Input Failure*. Specifically see [7.21.6.2 The fscanf function(p4)](http://port70.net/~nsz/c/c11/n1570.html#7.21.6.2p4) – David C. Rankin May 29 '21 at 04:24
  • Rephrasing: When you type something and you press `Ctrl + D` (or `Ctrl + Z` on Windows) it doesn't cancel the input, `scanf` will still return 1 and a value will be stored in the variable/array, the only difference to pressing `Enter` at the end is that no newline character '\n' is added to the input. – isrnick May 29 '21 at 04:32
  • Yes, correct, if you type something and then press `ctrl+d` or `ctrl+z`, you will read up to the point that `EOF` is generated. I got you. You no longer have an *input failure* because input was read before `EOF` was encountered. – David C. Rankin May 29 '21 at 04:38