0

I'm writing a code that I want to make sure the User only inputs an integer between 1-10 and stores that value in a 2D array. I'm trying to use pointers to do complete this task.

#include <stdio.h>

int main()
{
    //Declaration of a 2D array
    int movie_Scores [8][5];
    //Declaration of a pointer variable which points to the address of the 1st element
    int *p;
    p=&movie_Scores;
    //Array of pointers to strings and initializtion
    char movie_Names[][100] =      {"1. Movie 1",
                                    "2. Movie 2",
                                    "3. Movie 3",
                                    "4. Movie 4",
                                    "5. Movie 5",
                                    "6. Movie 6",
                                    "7. Movie 7",
                                    "8. Movie 8"
                                   };

    for(int i=0; i<5; i++)
    {
        printf("Judge %d, rate each movie from 1-10:\n\n", i+1);
        for(int j=0;j<8;j++)
        {
            printf("\n%s\t:", movie_Names[j]);
            scanf("%d", (p+i+(j*5)));

            while(*(p+i+(j*5))<1 || *(p+i+(j*5))>10)
            {
                printf("Enter number between 1-10:\n");
                printf("\n%s\t:", movie_Names[j]);
                scanf("%d", (p+i+(j*5)));
            }
        }
    }
}

Currently my code takes the input and directly stores it into the array without checking if the value entered is between 1-10.

Habib Khan
  • 37
  • 6
  • Why `int movie_Scores [8][5];`? Whats the meaning of `[5]`? – David Ranieri Dec 24 '22 at 07:54
  • 1
    For clarity, factor out the question/response into a function that will only return a number when it is in the range desired. Otherwise, it simply loops (with an error message for the user...) Should you write such a function, you could easily add the missing validation of the return code from `scanf()` (type "foobar" instead of a number and watch this program crash...) – Fe2O3 Dec 24 '22 at 07:55
  • 1
    Meaning of `[5]` is that there are 5 judges. Meaning of `[8]` is that there are 8 movies to be rated. – Dúthomhas Dec 24 '22 at 07:58
  • @DavidRanieri i am storing teh judges scored in a 2D array. The Rows represent each movie and column represents teh scores each judge gives that movie – Habib Khan Dec 24 '22 at 08:06
  • How extensive should the input validation be? For example, can it be assumed that the user always enters a number and not a word like `abc`, so that only the range of the number must be checked, and not whether the user actually entered a number? – Andreas Wenzel Dec 24 '22 at 08:08
  • @AndreasWenzel getting the numbers right would be sufficient – Habib Khan Dec 24 '22 at 08:11
  • Outer dimensions (listed first) “nest” inner dimensions (listed last). Hence flattened index calculations take multipliers on the _outer_ dimension indices. – Dúthomhas Dec 24 '22 at 09:26
  • @AndreasWenzel i just thought it would be easier for someone looking at the problem to understand what i am trying to achieve if the entire code was available. For example it would be hard to explain that i am trying to input the Integers into a 2D array. Hope that clears your doubt – Habib Khan Dec 24 '22 at 10:20
  • @HabibKhan: Yes, if your problem was with the 2D array itself, then it was appropriate to post your entire code. I have therefore deleted my comment in which I claimed that your posted code was not "minimal". – Andreas Wenzel Dec 24 '22 at 10:25
  • @AndreasWenzel no problem. On the other hand, i am facing diffiulties on the next step on the task at hand which i have posted here: https://stackoverflow.com/questions/74906823/how-do-i-sum-each-row-of-a-2d-array-using-pointers-and-what-is-wrong-with-my-cod?noredirect=1#comment132191073_74906823 – Habib Khan Dec 24 '22 at 10:31
  • @HabibKhan: After reading the comments in the other question, I believe that my previous comment in this question in which I stated that you are attempting to access the 2D array as a 1D array, is not correct. I have therefore deleted that comment of mine. I suggest that you ignore it, because it was probably misleading and confusing for you. – Andreas Wenzel Dec 24 '22 at 10:45

4 Answers4

2

Input functions do not test for valid range. (In fact, that is a known failing of scanf. See here and here for the eyes-glaze-over reasons.) You need to test the value after you get it.

int value;
if (!scanf( "%d", &value )) fooey();
if ((value < 1) or (value > 10)) fooey();

Getting input in C is actually a rather obnoxious thing to do. You can write yourself a helper function to put all the messy stuff in one spot, then you can use it elsewhere.

Also, use better variable names, and don’t introduce gratuitous variables.

for(int judge_number=0; judge_number<5; judge_number++)
{
    printf("Judge %d, rate each movie from 1-10:\n", judge_number+1);
    for(int movie_number=0; movie_number<8; movie_number++)
    {
        int rating;
        for (;;)
        {
            printf("%s\t:", movie_Names[movie_number]);
            scanf("%d", &rating);
            if ((rating >= 1) and (rating <= 10)) break;
            printf("Rating must be an integer in 1 to 10.\n");
        }
        movie_Scores[movie_number][judge_number] = rating;
    }
}

EDIT: BTW, I would be remiss if I didn’t mention today’s magic numbers 5 and 8.

You should not use magic numbers. Instead, provide a named value somewhere.

#define NUMBER_OF_MOVIES 8
#define NUMBER_OF_JUDGES 5

Then you can use those identifiers anywhere you need them:

int movie_Scores [NUMBER_OF_MOVIES][NUMBER_OF_JUDGES];

And:

for (int judge_number=0;  judge_number<NUMBER_OF_JUDGES;  judge_number++)

Et cetera.

Dúthomhas
  • 8,200
  • 2
  • 17
  • 39
  • Thank you for your step by step explanation, as i am still new to C programming, my coding style might be a little hard to read maybe? On the other hand, I realised some rookie mistakes i made in the code above. I've re-corrected those mistakes and i think my code is working now. Could you help me confirm? – Habib Khan Dec 24 '22 at 08:21
  • ohh and I'm using pointers to write this code – Habib Khan Dec 24 '22 at 08:29
  • @HabibKhan The pointers part (and flattened array indexing) is messing you up. Don’t do that. – Dúthomhas Dec 24 '22 at 09:24
1

First, I'd write a function that reads an int and checks it against an input, returning 1 if an acceptable int was read, and 0 if one was not.

int read_int_from_range(int *n, int start, int end) {
    int r = scanf("%d", n);

    return r == 1 && *n >= start && *n <= end;
}

Now, you can call this and check it and use that to loop until you get "valid" input in another function if you want, or simply exit with an error message if that's the desired behavior.

Chris
  • 26,361
  • 5
  • 21
  • 42
1

The problem of how to verify that the user actually entered an integer has already been answered in this question:

Validate the type of input in a do-while loop C

Therefore, the question remains how to verify that the integer that the user entered is also in the range 1 to 10.

In order to do this, all you need is an if statement. However, if you want the user to be automatically re-prompted for input, then you will also need a loop.

In my solution below, I use the function get_int_from_user from my solution to the question mentioned above, with a slight modification, so that it accepts printf-style input. This function automatically verifies that the user entered a valid integer, and if not, it automatically reprompts the user for input.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>
#include <stdarg.h>

#define NUM_JUDGES 3
#define NUM_MOVIES 8

int get_int_from_user( const char *prompt, ... );

int main( void )
{
    //Declaration of a 2D array
    int movie_scores[NUM_JUDGES][NUM_MOVIES];

    //Array of pointers to strings and initializtion
    char *movie_names[] = {
        "1. <Movie 1>",
        "2. <Movie 2>",
        "3. <Movie 3>",
        "4. <Movie 4>",
        "5. <Movie 5>",
        "6. <Movie 6>",
        "7. <Movie 7>",
        "8. <Movie 8>"
    };

    for( int i = 0; i < NUM_JUDGES; i++ )
    {
        printf( "Judge %d, rate each movie from 1-10:\n\n", i+1 );

        for( int j = 0; j < NUM_MOVIES; j++ )
        {
            //repeat until the input is in the range 0 to 10
            for (;;)
            {
                int score;

                score = get_int_from_user( "%s: ", movie_names[j] );

                if ( 0 <= score && score <= 10 )
                {
                    //score is good, so write it to the 2D array and
                    //break out of the loop
                    movie_scores[i][j] = score;
                    break;
                }

                printf( "Error: The score must be in the range 0 to 10!\n" );
            }
        }

        printf( "\n" );
    }

    //input is complete, so now it is time to print back the data to the user

    printf( "\nThe following data was entered:\n\n" );

    for ( int i = 0; i < NUM_JUDGES; i++ )
    {
        printf( "Judge %d: ", i+1 );

        for ( int j = 0; j < NUM_MOVIES; j++ )
        {
            printf( "%d ", movie_scores[i][j] );
        }

        printf( "\n" );
    }
}

int get_int_from_user( const char *prompt, ... )
{
    //loop forever until user enters a valid number
    for (;;)
    {
        char buffer[1024], *p;
        long l;
        va_list vl;

        //prompt user for input
        va_start( vl, prompt );
        vprintf( prompt, vl );
        va_end( vl );

        //get one line of input from input stream
        if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
        {
            fprintf( stderr, "Unrecoverable input error!\n" );
            exit( EXIT_FAILURE );
        }

        //make sure that entire line was read in (i.e. that
        //the buffer was not too small)
        if ( strchr( buffer, '\n' ) == NULL && !feof( stdin ) )
        {
            int c;

            printf( "Line input was too long!\n" );

            //discard remainder of line
            do
            {
                c = getchar();

                if ( c == EOF )
                {
                    fprintf( stderr, "Unrecoverable error reading from input!\n" );
                    exit( EXIT_FAILURE );
                }

            } while ( c != '\n' );

            continue;
        }

        //attempt to convert string to number
        errno = 0;
        l = strtol( buffer, &p, 10 );
        if ( p == buffer )
        {
            printf( "Error converting string to number!\n" );
            continue;
        }

        //make sure that number is representable as an "int"
        if ( errno == ERANGE || l < INT_MIN || l > INT_MAX )
        {
            printf( "Number out of range error!\n" );
            continue;
        }

        //make sure that remainder of line contains only whitespace,
        //so that input such as "6sdfj23jlj" gets rejected
        for ( ; *p != '\0'; p++ )
        {
            if ( !isspace( (unsigned char)*p ) )
            {
                printf( "Unexpected input encountered!\n" );

                //cannot use `continue` here, because that would go to
                //the next iteration of the innermost loop, but we
                //want to go to the next iteration of the outer loop
                goto continue_outer_loop;
            }
        }

        return l;

    continue_outer_loop:
        continue;
    }
}

This program has the following behavior:

Judge 1, rate each movie from 1-10:

1. <Movie 1>: abc
Error converting string to number!
1. <Movie 1>: 4abc
Unexpected input encountered!
1. <Movie 1>: 12
Error: The score must be in the range 0 to 10!
1. <Movie 1>: 1
2. <Movie 2>: 2
3. <Movie 3>: 3
4. <Movie 4>: 4
5. <Movie 5>: 5
6. <Movie 6>: 6
7. <Movie 7>: 7
8. <Movie 8>: 8

Judge 2, rate each movie from 1-10:

1. <Movie 1>: 8
2. <Movie 2>: 7
3. <Movie 3>: 6
4. <Movie 4>: 5
5. <Movie 5>: 4
6. <Movie 6>: 3
7. <Movie 7>: 2
8. <Movie 8>: 1

Judge 3, rate each movie from 1-10:

1. <Movie 1>: 10
2. <Movie 2>: 10
3. <Movie 3>: 10
4. <Movie 4>: 10
5. <Movie 5>: 10
6. <Movie 6>: 10
7. <Movie 7>: 10
8. <Movie 8>: 10


The following data was entered:

Judge 1: 1 2 3 4 5 6 7 8 
Judge 2: 8 7 6 5 4 3 2 1 
Judge 3: 10 10 10 10 10 10 10 10 

Note that for demonstration purposes, I have reduced NUM_JUDGES form 5 to 3.

Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
0

As far as I know, the only option is to simply use scanf. If the number is less than 1 or more than 10, you can print something like "Please enter a number from 1 to 10. and use scanf again until you get a number between 1 and 10.

Peter
  • 158
  • 1
  • 5