简体   繁体   中英

“expected expression” error on goto label (non-local goto)

Yeah, I know Esdger Dijkstra said you shouldn't use goto , but he's not God. I think it's fine to use an unconditional branch as long as you don't overdo it. You're just as likely to create unreadable code by overusing inheritance as you are by overusing branch statements.

Anyway, now that I'm done with that preemptive rebuttal, here's the program I was writing. It's supposed to read code from a file (eventually I want it to read HTML code, but I'm using my own simplified markup language for now because it's easier) and convert it to a format that can be used as input to the enscript program.

#include <stdio.h>
#include <stdlib.h>

#define INPUT_ERROR 1

void writecolor( FILE *, float, float, float );
unsigned short hextonum( char );

char escape = '\0'; // Default for enscript

int main( int argc, char **argv ){
    FILE *in;
    FILE *out;
    if( argv[1] ){
        in = fopen( argv[1], "r" );
    }
    else{
        in = stdin;
    }
    if( argv[2] ){
        out = fopen( argv[2], "w" );
    }
    else{
        out = stdout;
    }
    char c;       // Input character
    float red;    // Red value from hex code
    float green;  // Green value from hex code
    float blue;   // Blue value from hex code
    int line = 1; // Current line number, used for error reporting
    while( (c = fgetc( in )) != EOF ){
        if( c == '\\' ){
            if( fgetc( in ) == '#' ){
                red = (float) hextonum( fgetc( in ) ) * 10 / 16 * 0.1 + (float) hextonum( fgetc( in ) ) * 10 / 16 * 0.01;
                green = (float) hextonum( fgetc( in ) ) * 10 / 16 * 0.1 + (float) hextonum( fgetc( in ) ) * 10 / 16 * 0.01;
                blue = (float) hextonum( fgetc( in ) ) * 10 / 16 * 0.1 + (float) hextonum( fgetc( in ) ) * 10 / 16 * 0.01;
                writecolor( out, red, green, blue );
            }
        }
        else{
            fputc( c, out );
        }
        if( c == '\n' ){
            line++;
        }
    }
    fclose( in );
    fclose( out );
    return 0;
    :infile_error // XXX goto in hextonum branches here
    fprintf( stderr, "%s: Error on line %d of input file.\n", argv[0], line );
    return INPUT_ERROR;
}

// Converts a color code in the markup to a color code
// recognized by enscript
void writecolor( FILE *fp, float red, float green, float blue ){
    fwrite( &escape, 1, 1, fp );
    fprintf( fp, "color{%f %f %f}", red, green, blue );
}

// Converts a hex digit to its corresponding value
unsigned short hextonum( char hex ){
    if( hex >= '0' && hex <= '9' ){
        return atoi( hex );
    }
    switch( hex ){
        case( 'a' ) : case( 'A' ) : return 0xa;
        case( 'b' ) : case( 'B' ) : return 0xb;
        case( 'c' ) : case( 'C' ) : return 0xc;
        case( 'd' ) : case( 'D' ) : return 0xd;
        case( 'e' ) : case( 'E' ) : return 0xe;
        case( 'f' ) : case( 'F' ) : return 0xf;
    }
    // Okay, I think rather than putting an if statement
    // around every piece of code that uses this function,
    // I'm just going to jump to an error code in the
    // main function.
    goto infile_error;
}

It is very much a work in progress and is far from perfect or even functional at this point. I'm just wondering why I keep getting the following error:

html2enscript.c:50:2: error: expected expression
        :infile_error // XXX goto in hextonum branches here
        ^

Is this some kind of compiler-enforced good practice rule here, or did I actually do something wrong (and by wrong I mean syntactically incorrect, not just bad programming style)?

Additional information:

Here is my gcc version information:

Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 7.3.0 (clang-703.0.31)
Target: x86_64-apple-darwin15.0.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

Two issues here. The first is that your label for goto is incorrectly specified.

You have this:

:infile_error

Where it should be this:

infile_error:

The second issue, which is the larger one, is that you're attempting to use goto to jump to a different function. This is not allowed.

It seems you're attempting to implement some kind of exception mechanism. This is not supported in C however.

The proper way to do this is to have the function return some value that specifies an error then check the value when the function exits.

If you really want to do a non-local goto , you can do this with setjmp and longjmp . This is as close as you'll get to exception handling in C.

jmp_buf hextonum_err;

int main( int argc, char **argv ){
    ...
    if (setjmp(hextonum_err) != 0) {
        goto infile_error;
    }
    while( (c = fgetc( in )) != EOF ){
    ...
}

unsigned short hextonum( char hex ){
    if( hex >= '0' && hex <= '9' ){
        // don't use atoi here as that expects a string
        return hex - '0';
    }
    switch( hex ){
        case( 'a' ) : case( 'A' ) : return 0xa;
        case( 'b' ) : case( 'B' ) : return 0xb;
        case( 'c' ) : case( 'C' ) : return 0xc;
        case( 'd' ) : case( 'D' ) : return 0xd;
        case( 'e' ) : case( 'E' ) : return 0xe;
        case( 'f' ) : case( 'F' ) : return 0xf;
    }
    longjmp(hextonum_err, 1);
    // never reached, but put here because compiler will warn on non returning a value
    return 0;
}

In general, I wouldn't recommend writing code like this.

As I mentioned in the comments, you can't use goto to branch across function boundaries, so this won't work.

Personally, I'd abstract out the red/green/blue computations into their own function and check the result of that:

int readcolor( FILE *stream, float *red, float *green, float *blue )
{
  float *rgb[3];
  rgb[0] = red;
  rgb[1] = green;
  rgb[2] = blue;

  for ( size_t i = 0; i < 3; i++ )
  {
    int b0 = fgetc( stream );
    int b1 = fgetc( stream );

    if ( b0 == EOF || b1 == EOF )
      return 0;

    *rgb[i] = hextonum( b0 ) * 10.0f / 16.0f * 0.1f + 
              hextonum( b1 ) * 10.0f / 16.0f * 0.1f;
  }
  return 1;
}

and then in your main function:

if( fgetc( in ) == '#' )
{
  if ( readcolor( in, &red, &green, &blue ) )
    writecolor( out, red, green, blue );
}
else
{
  ...
}

Makes your main code scan a little better, anyway.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM