8

写一个简单的评估我遇到了一个有趣的问题。

给定代码:

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

我很惊讶地看到 gcc 抱怨缺少控制流选项:

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

流问题可以通过将返回值存储在一个变量中来解决,如下所示:

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 接受原始代码 - (我想进行静态分析以实现我的代码有效且干净)。


编辑:

我可能有点不清楚。

假设我编译了以下代码:

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 会给我一个警告:

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

这意味着 gcc 对开关中的值选项有一定的了解,并且我已经注释掉了 LEAF 案例。这意味着 gcc 也知道在通过 switch 时正在检查每个案例。那么为什么声明:

control reaches end of non-void function

它是否缺少 gcc 中缺少的静态分析系统 - 或语言功能?

4

5 回答 5

9

你的编译器抱怨,因为你的函数逻辑中的所有路径都应该返回一个值(正如这个函数的原型所规定的):

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

编译器(就像我在这个答案中一样)专注于代码的语法而不是语义。

尽管您已经定义enum node_type {LEAF, NODE}了 ,但您的编译器不想依赖此约束并接受typeintree->type语句具有与 justNODELEAF反正不同的值的可能性。


编辑:我试过这段代码:

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

在 ideone 上,结果如下:
(gcc-4.8.1,编译为“C”)~ http://ideone.com/b0wdSk:代码有效,输出2
(gcc-4.8.1,编译为“C++”) ~ http://ideone.com/OPH5Ar : 与 "C" 相同
(gcc-4.8.1, 编译为 "C99 strict") ~ http://ideone.com/ou71fe : 无效,因为:

错误:控制到达非空函数的结尾 [-Werror=return-type]

为了支持Martin Kristiansen关于将任何整数值分配给枚举有效的观点,我尝试struct node n; n.type = 7;使用相同的代码和“C”以及“C99 strict”,编译器根本不会抱怨。然而“C++”给出:

错误:从“int”到“node_type”的无效转换 [-fpermissive]

于 2013-09-08T04:23:30.197 回答
2

为了防止 GCC 缺少返回警告,但如果您实际上缺少 ,仍会收到警告,请在语句之后但在函数结束之前enum case停止控制流。在 C 中, 您可以使用、或. (参考) 在 C++ 中有表达式 – 有或没有适当的异常作为参数。(参考)这也有在函数被错误调用的情况下定义行为的好处。switch
exit(int)quick_exit(int)_Exit(int)abort()
throw

Clang 不会就丢失的returnbtw 发出警告。

于 2015-10-04T03:09:10.383 回答
1

歧义来自这样一个事实,即在 C 中enum类型可以存储类型声明中给出的值之外的其他值,并且在任何上下文中,包括您的switch语句,枚举类型对象的计算结果为int. 你可以避免这个警告

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

如果您认为在进一步的开发中您将添加其他案例。如果没有,你最好选择一个bool isNode或类似的东西。

于 2013-09-08T07:11:11.670 回答
0

添加默认情况。

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

请注意,这种情况可能不需要 switch 语句,if/else if 可能是您想要的。

于 2013-09-08T04:26:28.687 回答
-2

有什么原因这行不通吗?(添加声明):

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

}
于 2013-09-08T04:24:37.060 回答