为什么不是';GCC是否足够聪明,能够理解丢失的返回语句?

为什么不是';GCC是否足够聪明,能够理解丢失的返回语句?,c,llvm,clang,C,Llvm,Clang,我有一个C代码: EaglePage* EaglePage_RealCopy(EaglePage *page) { if(NULL == page) { return NULL; } switch(page->type) { case EagleDataTypeUnknown: EagleLogger_Log(EagleLoggerSeverityError, "Cannot page of Unknown

我有一个C代码:

EaglePage* EaglePage_RealCopy(EaglePage *page)
{
    if(NULL == page) {
        return NULL;
    }

    switch(page->type) {

        case EagleDataTypeUnknown:
            EagleLogger_Log(EagleLoggerSeverityError, "Cannot page of Unknown type.");
            return NULL;

        case EagleDataTypeInteger:
            return EaglePage_RealCopyInt_(page);

        case EagleDataTypeVarchar:
            return EaglePage_RealCopyVarchar_(page);

        case EagleDataTypeFloat:
            return EaglePage_RealCopyFloat_(page);

    }
}
当我在MacOSX上使用clang编译时,它知道交换机处理所有“可能”的分支,并且不会警告丢失的返回语句(如Java)。但是,如果我通过GCC4.4.5运行相同的代码,它总是会给出一个缺少返回警告


这很烦人,因为如果我输入return语句,那么我的代码覆盖率就会中断,因为我没有覆盖那些“不可能”的场景。GCC有没有办法像clang/javac那样处理这个问题?

有时候会发生这种情况,但是用
默认的
标签很容易解决

switch(page->type) {

    case EagleDataTypeInteger:
        return EaglePage_RealCopyInt_(page);

    case EagleDataTypeVarchar:
        return EaglePage_RealCopyVarchar_(page);

    case EagleDataTypeFloat:
        return EaglePage_RealCopyFloat_(page);

    case EagleDataTypeUnknown:
    default:
        EagleLogger_Log(EagleLoggerSeverityError, "Cannot page of Unknown type.");
        return NULL;

}

clang不仅仅是一个编译器,更是字节码编译器和静态分析工具的混合体。因此,在这种特殊情况下,您没有看到来自clang的警告的原因是,可以证明您的函数不能用开关未覆盖的值调用

也可能是叮当作响的虫子

尝试将此函数添加到代码中,并确保已调用该函数,然后查看发生的情况:

void BogusEaglePage_Call(void) {
  EaglePage *bp, *br;
  bp = malloc (sizeof (*bp));
  if (bp) {
    bp->type = (EagleDataTypeInteger | EagleDataTypeVarchar | EagleDataTypeFloat | EagleDataTypeUnknown) << 2;
    br = EaglePage_RealCopy(bp);
  }
}
void BogusEaglePage\u调用(void){
EaglePage*bp,*br;
bp=malloc(sizeof(*bp));
如果(bp){

bp->type=(EagleDataTypeInteger | EagleDataTypeVarchar | EagleDataTypeFloat | EagleDataTypeUnknown)您没有向编译器提供任何信息来知道只有四个可能的值,例如,如果您放置开关(page->type&3)然后,取决于编译器是否足够聪明,知道您已经涵盖了所有可能的情况,并且所有路径都已处理(切换后的任何代码都将被标记为无法访问)。编译器是否能够做到这一点与编译器、该品牌和版本的特定功能有关

拥有一个单一的返回点并不能给你带来任何你想要做的事情,你仍然需要通过这段代码验证5条路径(你在系统级别上说的其中一条是无法到达的)。编译器并不总是且不能总是查看系统级别,但有时只能关注功能级别。llvm工具可以使用单独编译的源文件轻松地在整个项目中进行优化,gcc需要在一个大文件(包含)中进行优化


在您编写代码时,编译器将正确生成代码的第五条路径,这是您的代码告诉它要做的事情,除非编译器中有错误。如果您希望编译器做其他事情,请通过更改源代码告诉编译器做其他事情(或者向编译器展示比您向我们展示的更多的代码,如果这里有我们看不到的代码是用这段代码编译的,这是另一个故事,或者正如我在上面所展示的,如果在这段代码中您向编译器展示了范围限制,那么它可以有机会限制到该范围).

您是否尝试使用最新的
GCC
编译器?OT:如果您将代码设计为每个函数只有一个退出点,您就不会遇到这个问题。您的问题并不十分清楚,但我猜
page->type
是一个枚举,在
switch
语句中只列出了这四个值?@alk,你说的对,总体来说这是个好主意,但我更好奇的是GCC是否有这个功能。这是C,而
enum
s是
int
s,因此
type
很容易就可以有
int
可能有的任何值。我从不在enum的开关上使用默认情况,我依赖编译器警告ing(to error)显式声明是否覆盖了所有枚举值。正如其他人指出的,这是一种错误的安全感。如果代码中的错误在
type
字段中放入意外值,则程序(使用的编译器不会向您发出警告)只会默默地对您造成未定义的行为。如果您真的觉得不应该发生这种情况,您可以在交换机外部添加
assert()
,并将
EaglePage\u RealCopy(&(EaglePage){.type=0xDEADBEEF});
添加到覆盖率测试中。