Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Value of boolean type changed in C

Tags:

c

boolean

scanf

I noticed that in C, my boolean variable somehow gets changed in a way I don't understand.

#include <stdio.h>
#include <stdbool.h>

int main(void) {
   bool x, y;

   printf("x: ");
   scanf("%d", &x);

   printf("x is %d\n", x);

   printf("y: ");
   scanf("%d", &y);

   printf("x is %d\n", x);
   printf("y is %d\n", y);

   return 0;
}

If I input a value of 1 for x and any value for y (1 in this example):

x: 1
x is 1
y: 1
x is 0
y is 1

at the end, y outputs the correct original value, but x magically changes to 0 in between!

This is not a problem when the input for x is 0 since the outputs for both x and y are their respective original values as expected.

Please explain what is going on!

like image 885
Naturally Upbeat Student Avatar asked Aug 31 '17 10:08

Naturally Upbeat Student


4 Answers

You are passing address of boolean variable to scanf() which expects variable of type int*. This will invoke undefined behavior and you may get wrong results or even crash.

To solve this problem, use temporary int to store scanning of an boolean value (as int), and after that store it to boolean variable.

Demo

bool x, y;
int tmp;

printf("x: ");
scanf("%d", &tmp);
x = tmp;

On the other hand, printing boolan variable is different story, where boolean value is protomoted to int without any problems and printed correctly.

like image 50
kocica Avatar answered Oct 06 '22 00:10

kocica


A bool isn't an int. Reading it with the %d format specifier for an int is undefined behavior.

Per 7.21.6.2 The fscanf function, paragraph 13 of the C standard:

If a conversion specification is invalid, the behavior is undefined.

Note that paragraph 9 of 7.21.6.1 The fprintf function states:

If a conversion specification is invalid, the behavior is undefined. If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.

But that's for fprintf(), not fscanf(). Format specifiers are much more stringent for the scanf() functions as there will be no argument promotion that allows a format such as %d to "work" for a char or bool, which get promoted to int for a printf() call. The scanf() functions are passed the address of the argument, and if what the address refers to is the wrong size from what is expected per the format specifier, undefined behavior will result - such as unexplained changes to another variable.

like image 44
Andrew Henle Avatar answered Oct 06 '22 00:10

Andrew Henle


OK, two points here.

  1. The size of bool is implementation-defined.
  2. There is no format specifier defined for bool type in the standard.

So, while scanning the value, passing the address of a bool as the argument for %d is bad see note as the supplied type is not the same as expected type.

You can use a intermediate integer, scan the value into that and (after validation or transformation to true and false MACROs) assign the result back to bool type variable.

For printing, however, because of default argument promotion, a bool can be a candidate for the argument for %d without a problem.


Note:

%d with *scanf() expects the argument to be an int *, instead supplying a bool* will cause undefined behavior.

Related, quoting from chapter §7.21.6.2, Paragraph 10

[....] Unless assignment suppression was indicated by a *, the result of the conversion is placed in the object pointed to by the first argument following the format argument that has not already received a conversion result. If this object does not have an appropriate type, or if the result of the conversion cannot be represented in the object, the behavior is undefined.

like image 45
Sourav Ghosh Avatar answered Oct 06 '22 00:10

Sourav Ghosh


bool is a different data type then int, and it is likely to be a single byte.

scanf is not type-safe function, you are telling it with the %d conversion specifier that expects pointer to an int, and scanf has no way to know that you have passed pointer to bool instead of pointer to an int. Then, you will get undefined behaviour.

clang compiler generated warning:

source_file.c:8:16: warning: format specifies type 'int *' but the argument has type 'bool *' [-Wformat]
   scanf("%d", &x);
          ~~   ^~
source_file.c:13:16: warning: format specifies type 'int *' but the argument has type 'bool *' [-Wformat]
   scanf("%d", &y);
          ~~   ^~
like image 32
msc Avatar answered Oct 05 '22 22:10

msc