2013-09-08 10 views
6

Pisanie prostej oceny Natknąłem się na zabawny problem.po zdefiniowaniu wielkości liter dla wszystkich wartości wyliczeniowych, kompilator wciąż mówi: "kontrola osiąga koniec funkcji bez funkcji"

względu na kod:

enum node_type {LEAF, NODE}; 

struct tree_elm_t { 
    enum node_type type; 
    union { 
    struct tree_node_t node; 
    struct tree_leaf_t leaf; 
    } datum; 
}; 

int parse_leaf(struct tree_leaf_t leaf); 
int parse_node(struct tree_node_t node); 
int parse_tree(struct tree_elm_t* tree); 

.... 

int parse_tree(struct tree_elm_t* tree) { 
    switch(tree->type) { 
    case NODE: return parse_node(tree->datum.node); 
    case LEAF: return parse_leaf(tree->datum.leaf); 
    } 
} 

Byłem zaskoczony, aby zobaczyć, że gcc narzeka brakującej opcji przepływu sterowania:

example.c: In function 'parse_tree': 
example.c:54: warning: control reaches end of non-void function 

problem przepływu może być rozwiązane poprzez przechowywanie wartości zwracanej, w zmiennej takiej jak:

int parse_tree(struct tree_elm_t* tree) { 
    int sum; 
    switch(tree->type) { 
    case NODE: sum = parse_node(tree->datum.node); break; 
    case LEAF: sum = parse_leaf(tree->datum.leaf); break; 
    } 
    return sum; 
} 

Mam jednak znaleźć oryginalny kod dużo czystsze, jest istnieje sposób na to, aby gcc zaakceptował oryginalny kod - (chcę przeprowadzić analizę statyczną, aby zdać sobie sprawę, że mój kod jest prawidłowy i czysty).


EDIT:

mogę być nieco niejasna.

powiedzmy skompilować następujący kod:

int parse_tree(struct tree_elm_t* tree) { 
    int sum; 
    switch(tree->type) { 
    case NODE: sum = parse_node(tree->datum.node); break; 
    // case LEAF: sum = parse_leaf(tree->datum.leaf); break; 
    } 
    return sum; 
} 

gcc da mi ostrzeżenie:

example.c: In function 'parse_tree': 
example.c:51: warning: enumeration value 'LEAF' not handled in switch 

co oznacza, że ​​gcc ma poczucie opcji dla wartości w przełączniku, a fakt, że ja skomentowałem sprawę LEAF. Oznaczałoby to, że gcc wie również, że podczas przechodzenia przez przełącznik, każdy przypadek jest badany. dlaczego więc stwierdzenie:

czy brakuje w nim brakującego systemu analizy statycznej w gcc - czy funkcji języka?

+1

Czy rozważałeś powrót '0'? –

+1

Czy próbowałeś dodać do przełącznika "domyślną" instrukcję, która zwraca pewną wartość błędu, być może zgłasza wyjątek, jeśli jest to rzeczywiście okoliczność, która nie powinna mieć miejsca? – ChrisCM

+1

Dlaczego sprawić, by zwracano 'int', jeśli to nie jest twoja intencja, a nie' void'? –

Odpowiedz

7

Twój kompilator narzeka ponieważ wszystkie ścieżki w logice czynność powinna zwrócić wartość (jako prototyp tej funkcji przepisuje):

int parse_tree(struct tree_elm_t* tree) { 
    switch(tree->type) { 
    case NODE: return parse_node(tree->datum.node); 
    case LEAF: return parse_leaf(tree->datum.leaf); 
    default: return 0; // <-- problem solved 
    } 
} 

Compiler (jak ja w tej odpowiedzi) skupia się raczej na składni niż semantyka twojego kodu.

I choć zdefiniowaniu enum node_type {LEAF, NODE}, kompilator nie chce polegać na tego ograniczenia i akceptuje możliwość type w tree->type oświadczenie o inną wartość z Just NODE lub LEAF tak.


EDIT: Próbowałem ten kod:

enum node_type {LEAF, NODE}; 
struct node { enum node_type type; }; 

int parse_tree(struct node* n) { 
    switch(n->type) { 
    case NODE: return 1; 
    case LEAF: return 2; 
    } 
} 

int main() { 
    struct node n; 
    printf("%d", parse_tree(&n)); 
    return 0; 
} 

na ideone i wynik jest następujący:
(gcc-4.8.1, skompilowane jako "C") ~ http://ideone.com/b0wdSk: Code jest valid, output 2 (gcc-4.8.1, skompilowany jako "C++") ~ http://ideone.com/OPH5Ar: taki sam jak "C"
(gcc-4.8.1, oblicza się jako „C99 ścisłym”) ~ http://ideone.com/ou71fe: nieważne z powodu:

error: control reaches end of non-void function [-Werror=return-type]

i wspierać Martin Kristiansen jest punkt o przypisanie żadnej wartości integralną do ENUM jest poprawny, próbowałem struct node n; n.type = 7; z tym samym kodem i "C", ale także "C99 strict" kompilator wcale nie narzeka. Jednak "C++" daje:

error: invalid conversion from ‘int’ to ‘node_type’ [-fpermissive]

+3

Dziwne, jak to brzmi, OP ma interesujące pytanie, ale nie jestem pewien, że oni rozumieją, jak tak (a może oni zrobić). Na ich kod * wszystkie * zdefiniowane wartości w ich enum są objęte przełącznikiem. W związku z tym "domyślny" przypadek, który tutaj podasz, miałby miejsce tylko wtedy, gdyby jako parametr wprowadzono niepoprawną * (a więc niezdefiniowaną) wartość wyliczenia. To właściwie bardziej interesujące pytanie, niż myślałem. – WhozCraig

+0

@WhozCraig: Nie zauważyłem wcześniej 'enum'. Edytowałem swoją odpowiedź (patrz ostatni akapit), jednak nie dostarcza ona rozsądnego wyjaśnienia * "dlaczego" * kompilator nie jest w stanie tego zrobić. – LihO

+4

Dokładnie. Nie jesteś sam. Kiedy po raz pierwszy zerknąłem na pytanie, które myślałem ... "no cóż, upewnij się, że wszystkie ścieżki wracają.", Ale wtedy zdałem sobie sprawę, że jeśli dane wejściowe są zgodne z domeną 'typ_węzła', to * zwraca * wszystkie ścieżki. I to nagle, ponieważ interesujące (przynajmniej dla mnie). – WhozCraig

-2

Czy jest jakiś powód, dla którego to nie zadziała? (dodane deklaracje):

void parse_leaf(struct tree_leaf_t leaf); 
void parse_node(struct tree_node_t node); 
void parse_tree(struct tree_elm_t* tree); 

void parse_tree(struct tree_elm_t* tree) { 
    switch(tree->type) { 
    case NODE: parse_node(tree->datum.node); break; 
    case LEAF: parse_leaf(tree->datum.leaf); break; 
    } 

} 
+0

To działa, kompilator jednak narzeka na "kontrolę osiąga koniec funkcji non void", co mnie martwi. –

+0

Tęskniłeś za deklaracją funkcji 'void' ... – ChrisCM

+0

Funkcje zwracają ints ....;) to jest część punktu pytania –

0

Dodaj domyślny przypadek.

switch(tree->type) 
{ 
    case NODE: return parse_node(tree->datum.node); break; 
    case LEAF: return parse_leaf(tree->datum.leaf); break; 
    default : 
     //perhaps throw exception here 
     return 0; 
} 

Należy pamiętać, że ta sytuacja prawdopodobnie nie wymaga instrukcji switch, jeśli/else if, prawdopodobnie jest to, czego chcesz.

+1

"return 0" byłby jank-code, ponieważ wszystkie przypadki są badane ... kończą się jeszcze gorzej, to znaczyłoby, że w przypadku gdybym zdecydował się dodać więcej opcji do mojego wyliczenia, kompilator nie narzekałby na brak sprawy oświadczenie –

+0

Dlatego sugeruję, abyś rzucił wyjątek ... – ChrisCM

+0

Wyrzucenie wyjątku oznaczałoby znalezienie błędu w produkcji - chciałbym znaleźć jak najwięcej błędów przed umieszczeniem mojego kodu w klastrze;) –

1

Niejednoznaczność pochodzić z faktu, że w C enum typy mogą przechowywać inne wartości niż te podane w deklaracji typu, i że w każdym kontekście, w tym wyciągu switch, wyliczenie obiekt wpisany do int ocenia. Możesz uniknąć tego ostrzeżenia, podając

, jeśli uważasz, że w dalszym rozwoju dodasz inne przypadki. Jeśli nie, lepiej pójdź z boolisNode lub coś podobnego.

+0

Widzę, dokąd zmierzasz - moje pytanie pochodzi jednak z faktu, że próbuję pisać funkcjonalne fałdy na drzewiastych strukturach danych i chciałbym, żeby analiza statyczna w gcc pomogła mi ją poprawić. Powiedzmy, że zmieniłem formę wyliczenia, a następnie Twój kod skompilowałby się bez błędu. –

1

Aby zapobiec brakujący ostrzeżenie powrotną z GCC, ale jeszcze dostać ostrzeżenia, jeśli rzeczywiście brakuje enumcase, zatrzymać przepływ sterowania po oświadczeniu switch ale przed końcem tej funkcji.
W języku C można używać exit(int), quick_exit(int), _Exit(int) lub abort(). (reference) W języku C++ występuje wyrażenie throw - z lub bez odpowiedniego wyjątku jako argumentu. (reference) Ma to również określone zachowanie, jeśli funkcja zostanie wywołana niepoprawnie.

Clang nie ostrzega o brakującym return btw.