2013-09-08 19 views
6

Viết một đánh giá đơn giản tôi đã gặp một vấn đề buồn cười.sau khi xác định trường hợp cho tất cả các giá trị enum, trình biên dịch vẫn nói: "kiểm soát đạt đến kết thúc của chức năng không void"

Với mã:

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

Tôi đã ngạc nhiên khi thấy gcc được phàn nàn về một lựa chọn dòng điều khiển mất tích:

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

vấn đề lưu lượng có thể được giải quyết bằng cách lưu trữ các giá trị trả về, trong một biến số như vậy:

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

Tuy nhiên, tôi tìm thấy bộ dọn sạch mã gốc là có cách để làm cho gcc chấp nhận mã gốc - (Tôi muốn phân tích tĩnh để nhận ra rằng mã của tôi là hợp lệ và sạch sẽ).


EDIT:

tôi có thể có được một chút không rõ ràng.

phép nói rằng tôi biên dịch đoạn mã sau:

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 sẽ cho tôi một lời cảnh báo:

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

nghĩa là gcc có một cảm giác trong những lựa chọn cho các giá trị trong việc chuyển đổi, và thực tế là tôi đã nhận xét trường hợp của LEAF. Điều này sẽ ngụ ý rằng gcc cũng biết rằng khi đi qua việc chuyển đổi mọi trường hợp đang được kiểm tra. vậy tại sao tuyên bố:

control reaches end of non-void function 

là thiếu hệ thống phân tích tĩnh trong gcc - hoặc tính năng ngôn ngữ?

+1

Bạn đã cân nhắc trả lại '0' chưa? –

+1

Bạn đã thử thêm câu lệnh "mặc định" vào chuyển đổi trả về một số giá trị lỗi, có lẽ ném một ngoại lệ nếu đây thực sự là một trường hợp không nên xảy ra? – ChrisCM

+1

Tại sao làm cho nó trở lại 'int' nếu đó không phải là ý định của bạn ở nơi đầu tiên chứ không phải là' void'? –

Trả lời

7

trình biên dịch của bạn được phàn nàn bởi vì tất cả những con đường trong logic của chức năng của bạn nên trả về một giá trị (như nguyên mẫu của chức năng này quy định):

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 (như tôi trong câu trả lời này) tập trung chứ không phải về cú pháp hơn ngữ nghĩa của mã của bạn.

Và mặc dù bạn đã xác định enum node_type {LEAF, NODE}, trình biên dịch của bạn không muốn dựa vào ràng buộc này và chấp nhận khả năng type trong tree->type tuyên bố có giá trị khác nhau từ NODE hoặc LEAF anyway.


EDIT: Tôi đã thử mã này:

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

trên ideone và kết quả là như sau:
(gcc-4.8.1, biên soạn như "C") ~ http://ideone.com/b0wdSk: Mã là hợp lệ, kết quả đầu ra 2
(gcc-4.8.1, được biên dịch là "C++") ~ http://ideone.com/OPH5Ar: giống như "C"
(gcc-4.8.1, biên soạn như "C99 nghiêm ngặt") ~ http://ideone.com/ou71fe: không hợp lệ vì:

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

Và để hỗ trợ Martin Kristiansen 's điểm về gán bất kỳ giá trị không thể thiếu để enum là hợp lệ, tôi đã cố gắng struct node n; n.type = 7; với cùng mã và với "C" nhưng cũng với "C99 nghiêm ngặt" trình biên dịch không phàn nàn gì cả. Tuy nhiên, "C++" cho:

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

+3

Như nó có vẻ lạ, OP có một câu hỏi thú vị, nhưng tôi không chắc là họ hiểu như thế nào (hoặc có thể là làm). Mỗi mã của họ * tất cả * giá trị được xác định trong enum của họ được bao phủ trong chuyển đổi. Do đó trường hợp "mặc định" bạn cung cấp ở đây sẽ chỉ xảy ra nếu giá trị enum * không hợp lệ * (và do đó không xác định) được cung cấp dưới dạng thông số. Nó thực sự là một câu hỏi thú vị hơn tôi nghĩ rằng nó sẽ được. – WhozCraig

+0

@WhozCraig: Tôi chưa nhận thấy 'enum' trước đây. Tôi đã chỉnh sửa câu trả lời của tôi (xem đoạn cuối), tuy nhiên nó không cung cấp giải thích hợp lý * "tại sao" * trình biên dịch không có khả năng làm như vậy. – LihO

+4

Chính xác. Bạn không cô đơn. Khi tôi lần đầu tiên liếc nhìn câu hỏi, tôi đã suy nghĩ .. "well duh, hãy chắc chắn tất cả các đường dẫn trở lại.", Nhưng hơn tôi nhận ra rằng nếu đầu vào là trong thực tế phù hợp với tên miền của 'node_type', anh * là * trở về tất cả các đường dẫn. Và với nó nó đột nhiên vì thú vị (ít nhất là với tôi). – WhozCraig

-2

Có lý do nào không hiệu quả? (các khai báo đã thêm):

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

Nó không hoạt động, trình biên dịch tuy nhiên phàn nàn về 'kiểm soát đạt đến kết thúc của chức năng không void 'đó là những gì rắc rối cho tôi. –

+0

Bạn đã bỏ lỡ khai báo hàm 'void' ... – ChrisCM

+0

Các hàm trả về ints ....;) đó là một phần của điểm của câu hỏi –

0

Thêm vào trường hợp mặc định.

Lưu ý rằng tình huống này có thể không cần câu lệnh chuyển đổi, nếu/nếu có, có lẽ là những gì bạn muốn.

+1

'return 0' sẽ là jank-code vì tất cả các trường hợp được khám phá ... kết thúc thậm chí tệ hơn, điều đó có nghĩa là trong trường hợp tôi quyết định thêm nhiều tùy chọn vào enum, trình biên dịch sẽ không phàn nàn về việc thiếu một trường hợp tuyên bố –

+0

Đó là lý do tại sao tôi đề nghị bạn ném một ngoại lệ ... – ChrisCM

+0

Ném ngoại lệ có nghĩa là tìm lỗi trong sản xuất - Tôi muốn tìm nhiều lỗi nhất có thể trước khi đặt mã của tôi trên cụm;) –

1

Sự mơ hồ xuất phát từ thực tế là trong C enum loại có thể lưu trữ các giá trị khác hơn so với những người được đưa ra trong tuyên bố loại, và rằng trong bất kỳ bối cảnh, trong đó có tuyên bố switch của bạn, một đối tượng liệt kê gõ kết quả là một int. Bạn có thể tránh cảnh báo này với

switch(tree->type) { 
    case NODE: return parse_node(tree->datum.node); 
    default: return parse_leaf(tree->datum.leaf); 
    } 

nếu bạn nghĩ rằng trong việc phát triển hơn nữa, bạn sẽ thêm các trường hợp khác. Nếu không, bạn nên đi theo số boolisNode hoặc thứ gì đó tương tự.

+0

Tôi thấy nơi bạn đi - câu hỏi của tôi tuy nhiên bắt nguồn từ thực tế là tôi đang cố gắng viết các nếp gấp cây trên cơ sở dữ liệu treelike, và tôi muốn phân tích tĩnh trong gcc để giúp tôi làm cho nó đúng. Cho phép nói rằng tôi đã thay đổi hình thức của enum của tôi, sau đó mã của bạn sẽ biên dịch mà không có một lỗi. –

1

Để ngăn cảnh báo trả về bị thiếu với GCC nhưng vẫn nhận được cảnh báo nếu bạn đang thiếu enumcase, dừng luồng điều khiển sau câu lệnh switch nhưng trước khi kết thúc hàm.
Trong C, bạn có thể sử dụng exit(int), quick_exit(int), _Exit(int) hoặc abort(). (reference)
Trong C++ có biểu thức throw - có hoặc không có ngoại lệ thích hợp làm đối số. (reference) Điều này cũng có lợi ích của hành vi được xác định trong trường hợp hàm được gọi không chính xác.

Clang không cảnh báo về số thiếu return btw.

Các vấn đề liên quan