после определения case для всех значений перечисления компилятор все еще говорит: "control достигает конца функции Non-void"

написание простой оценки я столкнулся с забавной проблемой.

данный код:

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 принять исходный код - (я хочу, чтобы статический анализ понял, что мой код действителен и чист).


EDIT:

Я, возможно, был немного неясно.

допустим, я компилирую следующий код:

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 имеет представление о параметрах значений в коммутаторе и о том, что я прокомментировал листовой случай. Это означало бы, что gcc также знает, что при переходе на коммутатор каждый случай рассматривается. так почему заявление:

control reaches end of non-void function

не хватает ли системы статического анализа в gcc-или языковой функции?

5 ответов


ваш компилятор жалуется, потому что все пути в логике вашей функции должны возвращать значение (как предписывает прототип этой функции):

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}, ваш компилятор не хочет полагаться на это ограничение и принимает возможность type на tree->type оператор, имеющий другое значение от just NODE или LEAF в любом случае.


EDIT: я пробовал этот код:

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, составленном как "С99 строгий") ~ http://ideone.com/ou71fe : инвалид из-за:

ошибка: управление достигает конца функции Non-void [- Werror=return-type]

и Мартин Кристиансено присвоении любого интегрального значения перечислению, которое является действительным, я попытался struct node n; n.type = 7; С тем же кодом и с "C", но также и с "C99 strict" компилятор вообще не жалуется. Однако "С++" дает:

ошибка: недопустимое преобразование из ' int ' в 'node_type' [- fpermissive]


двусмысленность исходит из того, что в C enum типы могут хранить другие значения, чем те, которые указаны в объявлении типа, и это в любом контексте, включая ваш switch оператор, типизированный объект перечисления оценивается в int. Вы можете избежать этого предупреждения с помощью

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

если вы думаете, что в дальнейшем развитии вы добавите другие случаи. Если нет, вам лучше пойти с bool isNode или что-то подобное.


чтобы предотвратить недостающее предупреждение о возврате с GCC, но по-прежнему получать предупреждения, если вы на самом деле отсутствует enum case, остановите поток управления после switch заявление, но до конца функции.
В C вы можете использовать exit(int), quick_exit(int), _Exit(int) или abort(). (ссылка)
В C++ есть throw expression - с или без надлежащего исключения в качестве аргумента. (ссылка) Это также имеет преимущество определенного поведения в случае функция вызывается неправильно.

Clang не предупреждает о пропавших return кстати.


добавить в случае дефолта.

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, вероятно, то, что вы хотите.


есть ли какая-то причина, по которой это не сработает? (добавлено объявление):

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

}