2

Why is it that I don't see anything displayed when I run this? I was expecting EINVAL to be printed instead of nothing.

#include <stdio.h>

unsigned int set_request(int val)
{
    printf("set_request entry\n");
    return 0;
}


void main()
{
    unsigned int val = 2;

    if (val == 0 || val == 1)
        if (set_request(val))
            printf("EIO\n");
    else
        printf("EINVAL\n");
}
jj@coffy:~/nvmemaster/tmp$ ./a.out
jj@coffy:~/nvmemaster/tmp$

I am getting the right behaviour when I add {}, but I would like to know the behaviour of if without {}.

Alternatively if I modify the assignment line to 0 as shown below, then I am seeing something else odd. Now I wasn't expecting EINVAL to be printed.

#include <stdio.h>

unsigned int set_request(int val)
{
    printf("set_request entry\n");
    return 0;
}


void main()
{
    unsigned int val = 0;

    if (val == 0 || val == 1)
        if (set_request(val))
            printf("EIO\n");
    else
        printf("EINVAL\n");
}
jj@coffy:~/nvmemaster/tmp$ gcc if.c
jj@coffy:~/nvmemaster/tmp$ ./a.out
set_request entry
EINVAL
jj@coffy:~/nvmemaster/tmp$
S.S. Anne
  • 15,171
  • 8
  • 38
  • 76
jithu83
  • 539
  • 6
  • 11
  • 2
    Indentation doesn't matter. C++ is not Python. – Jesper Juhl Mar 20 '20 at 17:15
  • 1
    Please, please read [What should main() return in C and C++?](https://stackoverflow.com/questions/204476/what-should-main-return-in-c-and-c) TL;DR: `void main()` is bad and you should use `int main(void)`. – S.S. Anne Mar 20 '20 at 17:16
  • @S.S.Anne Or even better (in C++) `int main()` that `void` for an empty argument list is a C-ism - not needed in C++. But yes, `void main()` is obviously wrong. – Jesper Juhl Mar 20 '20 at 17:18
  • 1
    This is a well-known grammar ambiguity which most languages (including C) resolve by binding the `else` to the closest possible `if`. The problem dates back at least to Algol 60. The computer science term for the problem is [dangling else](https://en.wikipedia.org/wiki/Dangling_else). – Tom Karzes Mar 20 '20 at 18:08

3 Answers3

4

An else will bind to the innermost if. So this:

if (val == 0 || val == 1)
    if (set_request(val))
        printf("EIO\n");
else
    printf("EINVAL\n");

Is actually the same as:

if (val == 0 || val == 1) {
    if (set_request(val)) {
        printf("EIO\n");
    } else {
        printf("EINVAL\n");
    }
}

The braces are needed to correctly bind the else clause. For this reason, it's best to always use them with if...else to avoid confusion.

Paul Ogilvie
  • 25,048
  • 4
  • 23
  • 41
dbush
  • 205,898
  • 23
  • 218
  • 273
  • 1
    @PaulOgilvie It's showing equivalency. The first code block is equivalent to the second. Also see *"I am getting the right behaviour when I add `{}`, but I would like to know the behaviour of if without `{}`"* inside the question. – S.S. Anne Mar 20 '20 at 17:17
3

Your code looks like this

    if (val == 0 || val == 1)
        if (set_request(val))
            printf("EIO\n");
    else
        printf("EINVAL\n");

Now indentation doesn't matter to the compiler, so it puts braces like this

    if (val == 0 || val == 1)
    {
       if (set_request(val))
            printf("EIO\n");
       else
            printf("EINVAL\n");
    }

So the else is not matched with the first if, but the second one.

It appears that you actually want something like

    if (val == 0 || val == 1)
    {
       if (set_request(val))
            printf("EIO\n");
    }
    else
       printf("EINVAL\n");

In which case you need to specify the braces. Note that to avoid this kind of ambiguity, it's best to always be explicit with braces.

Also, if you compile with warnings turned on, the compiler will warn about your code snippet. [-Wdangling-else]

cigien
  • 57,834
  • 11
  • 73
  • 112
2

The else is matched to the closest preceding if statement. This situation has a name, it's the dangling else problem. Your code is actually seen by the compiler as:

void main()
{
    unsigned int val = 0;

    if (val == 0 || val == 1)
        if (set_request(val))
            printf("EIO\n");
        else
            printf("EINVAL\n");
}

That's why you must use curly braces to make your intention clear. If you want the else bound to the outer if you could write your code as:

void main()
{
    unsigned int val = 0;

    if (val == 0 || val == 1) {
        if (set_request(val))
            printf("EIO\n");
    } else
        printf("EINVAL\n");
}
Blastfurnace
  • 18,411
  • 56
  • 55
  • 70