1

I am extremely new to working in C so forgive me if I have just made a simple error but I am extremely confused. My code is as follow:

#include <stdio.h>
#include <string.h>
int
main ()
{
  char first[10],last[10],address[20],city[10],state[3],zipcode[5];
  printf("\n\nPart 3:\n");
  printf("Please enter your name and address:\n");
  printf("First Name: ");
  scanf("%s", first);
  printf("Last Name: ");
  scanf("%s", last);
  printf("Address: ");
  fgetc(stdin);
  fgets(address, 20, stdin);
  address[strcspn(address,"[/r/n]")] = 0;
  printf("City: ");
  scanf("%s", city);
  printf("State: ");
  scanf("%s", state);
  printf("Zipcode: ");
  scanf("%s", zipcode);
  printf("%s %s\n%s%s, %s,  %s",first,last,address,city,state,zipcode);

  return 0;
}

Everything works except when the final print statement is read first is not output, it just begins with last name. Example:

For the input

Please enter your name and address:
First Name: Ben
Last Name: Johnson
Address: 1234 Smith St
City: Townsville
State: CA
Zipcode: 12345

I get the following output:

 Johnson
1234 Smith St
Townsville, CA, 12345

Just not sure where it is going wrong as my first is being inputted the exact same way as my other variables that are working.

Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
  • I have fixed the formatting of the output for you. However, is the space in front of the "Johnson" supposed to be there? If not, please remove it. – Andreas Wenzel Sep 12 '22 at 01:59
  • Yes it is, that is the output I am getting. Thank You – Sierra Walker Sep 12 '22 at 01:59
  • Please specify the exact input that you entered. – Andreas Wenzel Sep 12 '22 at 02:03
  • In this case it would be first: Ben last: johnson address: 1234 Smith St city: townsville state:CA zipcode: 12345 – Sierra Walker Sep 12 '22 at 02:04
  • 3
    The array `zipcode` only has room for 5 characters. However, if you enter `12345`, it requires 6 characters (including the terminating null character). This means that you are writing to the array out of bounds, which causes [undefined behavior](https://en.wikipedia.org/wiki/Undefined_behavior). – Andreas Wenzel Sep 12 '22 at 02:05
  • This fixed it! any way you can explain why the zip code not having enough room effects first? does it loop back around? – Sierra Walker Sep 12 '22 at 02:06
  • My guess is that your compiler is storing `first` directly after `zipcode` in memory, so that when `scanf` writes the terminating null character to `zipcode[5]` out of bounds, it is actually writing that terminating null character to `first[0]`. Since `first` now has a null character as the first character, the string is considered empty, which explains the output you are getting. – Andreas Wenzel Sep 12 '22 at 02:12
  • Note that you have the same problem with the array `city`. The string `Townsville` requires `11` characters including the terminating null character, but you are only giving it `10`. This means that you are also writing to `city` out of bounds. – Andreas Wenzel Sep 12 '22 at 02:19
  • @Sierra Walker, Using `"%s"` without a width is worse than [`gets()`](https://stackoverflow.com/q/1694036/2410359). Use a _width_. Even better, do not use `scanf()`. Use `fgets()`. – chux - Reinstate Monica Sep 12 '22 at 03:07
  • 2
    You have: `address[strcspn(address,"[/r/n]")] = 0;`. You need: `address[strcspn(address,"\r\n")] = '\0';` (where you can write just `0` but you're assigning a character). You probably don't want to zap square brackets and you do need to use backslashes instead of (forward) slashes. – Jonathan Leffler Sep 12 '22 at 03:16
  • Note that meanwhile, I have edited my answer in order to significantly simplify it. My previous answer was unnecessarily complicated. I am not sure if you automatically get notified of edits of my answer. Therefore, I am writing you this comment in order to make sure that you are aware of my edit. See the comments section of my answer for further information on what I have changed. – Andreas Wenzel Sep 12 '22 at 19:34

1 Answers1

3

You are writing to the arrays city and zipcode out of bounds, which causes undefined behavior.

The input string Townsville is 10 characters long, so it requires 11 characters including the terminating null character. However, the array city only has space for 10 characters, so it is not large enough to store that string. Therefore, when scanf attempts to store Townsville into city, it will overflow the buffer.

The array zipcode has the same problem. For the stated input 12345 it requires 6 characters (including the terminating null character), but you only gave the array space for 5 characters.

In order to prevent this kind of problem in the future, I recommend that that you use fgets for all data, as fgets will never overflow the buffer, provided that you pass the correct size of the buffer (which you are doing in your posted code). However, this would mean a lot of extra code, because you would also have to write code to remove the newline character every time you call fgets. For this reason, it would probably be best to write your own function. This function could also verify that the entire line was read in, and print an error message if the buffer was not large enough.

I have rewritten your code to use such a function:

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

void get_line_from_user( char *buffer, int buffer_size );

int main( void )
{
    char first[10],last[10],address[20],city[10],state[3],zipcode[5];

    printf("Part 3:\n");
    printf("Please enter your name and address:\n");

    printf("First Name: ");
    get_line_from_user( first, sizeof first );

    printf("Last Name: ");
    get_line_from_user( last, sizeof last );

    printf("Address: ");
    get_line_from_user( address, sizeof address );

    printf("City: ");
    get_line_from_user( city, sizeof city );

    printf("State: ");
    get_line_from_user( state, sizeof state );

    printf("Zipcode: ");
    get_line_from_user( zipcode, sizeof zipcode );

    printf("%s %s\n%s%s, %s,  %s",first,last,address,city,state,zipcode);

    return 0;
}

//This function will read exactly one line of input from the
//user. On failure, the function will never return, but will
//print an error message and call "exit" instead.
void get_line_from_user( char *buffer, int buffer_size )
{
    char *p;

    //attempt to read one line of input
    if ( fgets( buffer, buffer_size, stdin ) == NULL )
    {
        printf( "Error reading from input\n" );
        exit( EXIT_FAILURE );
    }

    //attempt to find newline character
    p = strchr( buffer, '\n' );

    //make sure that entire line was read in (i.e. that
    //the buffer was not too small to store the entire line)
    if ( p == NULL )
    {
        //a missing newline character is ok if the next
        //character is a newline character or if we have
        //reached end-of-file (for example if the input is
        //being piped from a file or if the user enters
        //end-of-file in the terminal itself)
        if ( getchar() != '\n' && !feof(stdin) )
        {
            printf( "Line input was too long!\n" );
            exit( EXIT_FAILURE );
        }
    }
    else
    {
        //remove newline character by overwriting it with
        //null character
        *p = '\0';
    }
}

Now the program will give you a proper error message if the input is too long, instead of overflowing the buffer:

Part 3:
Please enter your name and address:
First Name: Ben
Last Name: Johnson
Address: 1234 Smith St
City: Townsville
Line input was too long!
Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39