2016-10-25 39 views
3

是的,我知道Esdger Dijkstra說你不應該使用goto,但他不是上帝。只要你不過分,我認爲可以使用無條件分支。您可能會像過度使用分支語句一樣,過度使用繼承來創建不可讀的代碼。goto label上的「expected expression」錯誤(non-local goto)

無論如何,現在我已經完成了先發制人的反駁,這是我寫的程序。它應該讀取文件中的代碼(最終我希望它讀取HTML代碼,但現在我使用自己的簡化標記語言,因爲它更容易),並將其轉換爲可用作enscript程序輸入的格式。

#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; 
} 

這是一項非常有進步的工作,目前還遠未達到完美甚至功能。我只是想知道爲什麼我不斷收到以下錯誤:

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

這是某種形式的編譯器強制執行的良好實踐規則在這裏,還是我確實做錯事(和錯我的意思是語法不正確,不只是糟糕的編程風格)?

其他信息:

這裏是我的gcc版本信息:這裏

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 
+0

哦,哇,這很有道理!我完全忘了如何做標籤。 – user628544

+6

請注意'goto'只能在單個函數內分支;你不能用它來分支功能。 –

+1

'goto'有其應用。但是你的沒有。這裏有更好的方法來處理錯誤。 (讓@JohnBode指出它是無效的)。 – Olaf

回答

3

兩個問題。首先是您的標籤goto未正確指定。

你有這樣的:

:infile_error 

,它應該是這樣的:

infile_error: 

的第二個問題,這是一個較大的,是你正在嘗試使用goto跳轉至一個不同的功能。這是不允許的。

看來你試圖實現某種異常機制。但在C中不支持。

正確的做法是讓函數返回一些指定錯誤的值,然後檢查函數退出時的值。

如果你真的想要做一個非本地goto,你可以用setjmplongjmp做到這一點。這與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; 
} 

通常,我不會推薦編寫這樣的代碼。

+0

謝謝澄清。我應該知道你不能用'goto'跳轉到另一個函數,因爲這在彙編語言級別上沒有意義。我想使用某種跳轉來分支到錯誤處理代碼,否則我必須返回一個錯誤值並將每個函數調用放在一個'if'語句中,這是乏味的,並且與整個子程序抽象的「黑盒子」模型。 – user628544

+0

@ user628544,不是,原因不是彙編語言,實際原因是涵蓋每個函數調用的環境(局部變量和參數是在函數入口時創建的,並在函數退出時死掉的),如果你跳進功能體的中間。一個簡單的例子來說明:假設你用一個控制變量'i'跳到'for()'循環的中間。編譯器初始化該變量以使環境變得有意義的值是什麼?因爲局部變量在函數輸入時被初始化並且... –

+0

@ user628544,...通過正常執行流程執行。編譯器如何初始化所有內容,就像調用了正確的函數一樣?繼續執行這一點還有意義嗎? –

1

正如我在評論中提到的那樣,您不能使用goto跨越功能邊界進行分支,因此這不起作用。

個人而言,我抽象出來的紅/綠/藍計算到自己的功能和檢查的結果是:

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; 
} 

,然後在main功能:

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

無論如何,使您的main代碼掃描更好一點。