Search code examples
clabelgoto

Why must I include a semicolon when putting a label at the end of a function?


Simple function with a goto error handler:

void checkFile(char *name)
{
    FILE *fp = fopen(name, "r");
    if(NULL == fp)
    {
        fprintf(stderr, "Could not open %s\n", name);
        goto end;
    }

    // use fp...

    fclose(fp);
    end:
    ;
}

Notice, if I remove the useless semicolon after end: the function fails to compile.

On GCC:

error: expected primary-expression before '}' token
   16 | }

On MSVC:

error C2143: syntax error: missing ';' before '}'

So, I understand that C standard does say that the destination of the goto keyword expects a statement in § 6.8.6.1 p 2:

A goto statement causes an unconditional jump to the statement prefixed by the named label in the enclosing function

However, the error exists just because the label exists; if I remove the goto keyword, the label itself is still treated as an error and won't compile. I read the standard's section on "Labeled statements" (§ 6.8.1) but still didn't find anything that explained this odd constraint.


Solution

  • In C a label may be placed before a statement. So if there is no statement you can place a null statement.

    From the C Standard (6.8.1 Labeled statements)

    labeled-statement:
        identifier : statement
        case constant-expression : statement
        default : statement
    

    And (6.8.3 Expression and null statements)

    expression-statement:
        expressionopt ;
    

    3 A null statement (consisting of just a semicolon) performs no operations.

    Opposite to C in C++ declarations are also statements. So in C++ you may place a label before a declaration.

    Here are demonstrative programs.

    C program.

    #include <stdio.h>
    
    int main(void) 
    {
        goto End;
        
        End:;
        const char *bye = "Bye";
        
        puts( bye );
        
        return 0;
    }
    

    The program output is

    Bye
    

    C++ program

    #include <iostream>
    
    int main() 
    {
        goto End;
        
        End:
        const char *bye = "Bye";
        
        std::cout <<  bye << '\n';
    
        return 0;
    }
    

    The program output is

    Bye
    

    Pay attention to that in the C program after the label there is placed a null statement

    End:;
    

    Without it the compiler will issue an error.