简体   繁体   中英

Why in C does taking input using int format to a char change the value of another char variable?

I took input in "%d" format into a character using scanf() because I didn't want more than 8 bits. But doing so changed the value of another char variable.

Code:

#include <stdio.h>
int main() {
        char a;
        char b;
        printf("Enter a: ");
        scanf("%c", &a);
        printf("a = %c\n", a);
        printf("Enter b: ");
        scanf("%d", &b);
        printf("\na = %d\n", a);
        printf("b = %d\n", b);
}

Output:

Enter a: c
a = c
Enter b: 56

a = 0
b = 56

Screenshot

scanf reads data from stdin and stores them according to the parameter format into the locations pointed by the additional arguments.

scanf("%d", &b);

With %d you are storing an integer into the char variable that can not hold an integer. the Variable now grows into the other variable in stack above it.

if you compile with the flag "-fstack-protector-all" you should get the *** stack smashing detected *** error on execution.

This problem occurs when the scanf() function is used repeatedly. Since scanf() doesn't automatically skip whitespace in the stream, a space must be left in front of the %c conversion specifier.

I used the GCC compiler in ISO C11 (-std=c11) mode to investigate this issue.

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

/**
 * @brief   Main Program
 * @return  Returns 0 if the program is successful.
 */
int main( void )
{
    char a;
    char b;

    printf( "Enter the value of a: " );
    scanf( "%c", &a );

    printf( "Enter the value of b: " );
    scanf( " %c", &b );
    /// A space has been added in front of the %c conversion identifier of the scanf() function.

    printf( "The value of variable a: %d\n", a );
    printf( "The value of variable b: %d\n", b );

    return EXIT_SUCCESS;
}

Screenshot 1: Testing The Application

@JBahn77 thanks. I just noticed that the second scanf() function uses the %d conversion token. I agree with the side effect you indicated. This happens when the compiler loads variables in memory one after the other. For example, I examined the change in variable a when a sufficiently large number is typed in variable b. I changed the code as follows.

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

/**
 * @brief   Main Program
 * @return  Returns 0 if the program is successful.
 */
int main( void )
{
    char a;
    char b;

    printf( "Address of variable a: %p\n", &a );
    printf( "Address of variable b: %p\n", &b );

    printf( "Enter the value of a: " );
    scanf( "%c", &a );

    printf( "Enter the value of b: " );
    scanf( " %d", &b );

    printf( "The value of variable a: %d\n", a );
    printf( "The value of variable b: %d\n", b );

    return EXIT_SUCCESS;
}

Then, when I tested the code, I realized the consequences of this side effect.

Screenshot 1 : Testing The Edited Code

The tested code is interpreted in the image below.

Screenshot 2 : Interpretation On Results

The formatting directives in a scanf format string indicate not just how to match the input, but also the type of the corresponding variable in which the resulting value is to be stored. If you pass a pointer to an object of a different type than the corresponding directive expects then you reap undefined behavior.

That's true even for a simple signedness mismatch, but in your case you have a size mismatch. For a %d directive without a size modifier, scanf expects to write the scanned integer value in an object of type [ signed ] int . If the pointer you pass instead points to a char , and if, as is the case in most implementations, int is larger than char , then it is particularly likely that the resulting undefined behavior manifests in unwanted ways, such as by producing changes in the values of other variables.

If you want to scan a number as a char -sized integer, then you should

  1. Declare the variable as either signed char or unsigned char , not default char , and
  2. Use the correct conversion specifier and size modifier for the chosen data type.

For example,

unsigned char b;

scanf("%hhu", &b);

%u is the format directive for an unsigned integer, and the hh modifier instructs scanf that the destination variable is the size of a char / signed char / unsigned char .

If you want a signed integer then it would be similar, but with the %d formatting directive:

signed char b;

scanf("%hhd", &b);

As a matter of style, do not use default char for numeric data, even if you are confident that the range of values you need to support is a subset of the intersection of the ranges of signed char and unsigned char . Especially so if you plan to use it with formatted I/O functions, because it is implementation-specific whether %hhd or %hhu is the correct directive for a default char .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM