-3

I'm learning C and I've came around a strange problem. I think I understand the difference between multiple ifs and else-if statement, but I simply can not understand the difference in behavior this time. If I delete the else keyword it works as intended, but with else on it does not.

The code is about counting of occurrences of each letter without differentiating lower case or upper case (so 'a' and 'A' both counts as 1 occurrence for letter 'a').

I've tried omitting braces where I could but nothings changed so I've left them in to avoid caveats.

while ((c = getchar()) != EOF)
{
if ('A' < c < 'Z')
    {
        ++array[c - 'A'];
    }
    else if ('a' < c < 'z')
    {
        ++array[c - 'a'];
    }
}

When I type in 'a' then the array is not being incremented, but if I delete the else statement thus switching to a multiple if situation, it works as intended. Letter 'A' updates the array nicely in both cases.

Could you please help me understand the difference in behavior in this case?

  • 5
    `if ('A' < c < 'Z')` does *not* do what you think it does. You're going to have considerably better luck with `if ('A' < c && c < 'Z')` (which, btw, *excludes* 'A' and 'Z' from the domain of allowable values; probably not what you want considering the popularity of the letter 'A' in typical vernacular). – WhozCraig Aug 08 '19 at 08:33
  • 1
    This is a common beginner bug originating from _assuming_ that the language works in a certain way, rather than _studying_ how it actually works. You can't write code by trial and error. – Lundin Aug 08 '19 at 08:36
  • You defined `c` as `int`, right? It's an error if you defined as `char`. – pmg Aug 08 '19 at 08:38
  • 1
    Well at least Antti found a decent canonical dupe. I added to the SO C FAQ [here](https://stackoverflow.com/tags/c/info), as this is a common beginner question. – Lundin Aug 08 '19 at 08:52
  • The strange thing is that ```if ('A' < c < 'Z')``` worked for the letter 'A' . You are right about assuming but I'm still at the beginning of the book and started to spice up things a bit. Yes, c is int. Thanks for linking the FAQ I will make sure to read it carefully. – Gumi Maczko Aug 08 '19 at 09:21

3 Answers3

5

What we need to know:

  • The result of < comparison is an int with value 1 for true and 0 for false. It's like the result of 1 + 3 is int with value 4, the same way the result of 1 < 3 is an int with value 1.
  • Operator < has associativity Left to Right. That means that in 1 < 2 < 3 it will be parsed as (1 < 2) < 3 - ie. first will be 1 < 2 calculated, then the result will be < 3 compared with 3.

So:

'A' < c < 'Z'

is interpreted as

('A' < c) < 'Z'

The result of 'A' < c is either 1 or 0. When 'A' is lower then c, then it becomes:

1 < 'Z' 

otherwise it becomes:

0 < 'Z'

Both cases are true, so the comparison is always true.

If you want to check if a number is a letter between A and Z including the letters A and Z, you can:

if ('A' <= c && c <= 'Z') {

or #include <ctype.h> and use isupper function:

if (isupper(c)) {
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • Keep in mind that isupper/islower uses the current locale though, so they may not necessarily be the same as checking if a letter is between A and Z. – Lundin Aug 08 '19 at 08:45
  • Note for the OP. Assuming the desire locale is in play when running this code, the latter of these is particularly worth considering, since the assumption of uninterrupted continuity of `'A'...'Z'` [doesn't necessarily hold water](https://en.wikipedia.org/wiki/EBCDIC#Code_page_layout). The comparison isn't so much where the world of hurt comes in that case; the *array* indexing however... ouch. – WhozCraig Aug 08 '19 at 08:49
3

try

while ((c = getchar()) != EOF)
{
if ('A'  <= c && c <= 'Z')
    {
        ++array[c - 'A'];
    }
else if ('a' <= c && c <= 'z')
    {
        ++array[c - 'a'];
    }
}

'a'<c<'z' is computed not like a mathmatical expression, first 'a' < c is evaluated to True or False then that value (converted to 0 or 1 probably) is compared to 'z', so it is not doing what you are expecting it to.

Atreyagaurav
  • 1,145
  • 6
  • 15
2

The relational operators like < take two operands and returns 1 if the first operand is smaller than the second, otherwise 0.

Thus 'A' < c gives a result of 1 or 0, and then (since < operators associate left-to-right) you compare the value 1 or 0 with the ASCII value of 'Z', which is nonsense.

Correct code for checking if a variable is inside an interval is

if ( (c >= 'A') && (c <= 'Z') )

Also make sure c is int and not char, because getchar actually returns int, and to compare against EOF you will need to use int.

Lundin
  • 195,001
  • 40
  • 254
  • 396