views:

355

answers:

5

Lets us consider this snippet:

int s;
scanf("%c",&s);

Here I have used int, and not char, for variable s, now for using s for character conversion safely I have to make it char again because when scanf reads a character it only overwrites one byte of the variable it is assigning it to, and not all four that int has.

For conversion I could use s = (char)s; as the next line, but is it possible to implement the same by subtracting something from s ?

A: 

As written this won't work reliably . The argument, &s, to scanf is a pointer to int and scanf is expecting a pointer to char. The two data type (int and char) have different sizes (at least on most architectures) so the data may get put in the wrong spot in memeory, and the other part of s may not get properly cleared.

The answers suggesting manipulation of the result after using a pointer to int rely on unspecified behavior (i.e. that scanf will put the character value it has in the least significant byte of the int you're pointing to), and are not safe.

dmckee
A: 

Not but you could use the following:

s = s & 0xFF

That will blank out all of the data except the first byte. But in general all these ideas (and the ones above) are bad ideas, since not all systems store the lowest part of the integer in memory first. So if you ever have to port this code to a big endian system, you'll be screwed.

True, you may never have to port the code, but why write unportable code to begin with?

See this for more info:

http://en.wikipedia.org/wiki/Endianness

Timothy Baldridge
The result of `scanf`ing a character into an int is not well specified. He should simply not do it.
dmckee
+9  A: 

What you've done is technically undefined behaviour. The %c format calls for a char*, you've passed it an int* which will (roughly speaking) be reinterpreted. Even assuming that the pointer value is still good after reinterpreting, storing an arbitrary character to the first byte of an int and then reading it back as int is undefined behaviour. Even if it were defined, reading an int when 3 bytes of it are uninitialized, is undefined behaviour.

In practice it probably does something sensible on your machine, and you just get garbage in the top 3 bytes (assuming little-endian).

Writing s = (char)s converts the value from int to char and then back to int again. This is implementation-defined behaviour: converting an out-of-range value to a signed type. On different implementations it might clean up the top 3 bytes, it might return some other result, or it might raise a signal.

The proper way to use scanf is:

char c;
scanf("%c", &c);

And then either int s = c; or int s = (unsigned char)c;, according to whether you want negative-valued characters to result in a negative integer, or a positive integer (up to 255, assuming 8-bit char).

I can't think of any good reason for using scanf improperly. There are good reasons for not using scanf at all, though:

int s = getchar();
Steve Jessop
And there are very few reasons to use scanf() at all ;)+1 for the getchar() recommendation.
Mustapha Isyaku-Rabiu
Although it should be noted that `fgetc(stdin)` is preferable for enterprise code, since it uses dependency injection for a bonus 10 points ;-)
Steve Jessop
Just to clear up that "(I think)": explictly converting an out-of-range value to a signed type results in an implementation-defined result or the raising of an implementation-defined signal.
caf
+1  A: 

That's probably not a good idea; GCC gives me a warning for that code:

main.c:10: warning: format ‘%c’ expects type ‘char *’, but 
argument 2 has type ‘int *’

In this case you're ok since you're passing a pointer to more space than you need (for most systems), but what if you did it the other way around? Could be crash city. If you really want to do something like what you have there, just do the typecast or mask it - the mask will be endian-dependent.

Carl Norum
+1  A: 

Are you trying to convert a digit to its decimal value? If so, then

char c = '8';
int n = c - '0';

n should 8 at this point.

Arkadiy
This is correct as far as it goes, but misses the key problem with the suggested code.
dmckee
Character literals are `int` s in C.
Sinan Ünür
@dmckee: The questioner code has already been dismantled in other answers.
Arkadiy