4

我应该实现一个比较两个字符串的函数,strcmp但忽略空格字符,所以

strcmpignorews("abc   ", " a b c")

应该给出相同的结果。

这是我的实现:

namespace {
    void SkipWhitespace(const char *&s) {
        for (; std::isspace(*s, std::locale::classic); ++s);
    }
}

int strcmpignorews(const char *s1, const char *s2) {
    for (; *s1 != '\0' && *s2 != '\0'; ++s1, ++s2) {
        SkipWhitespace(s1);
        SkipWhitespace(s2);

        if (*s1 != *s2) {
            break;
        }
    }

    return (*s1 < *s2) ? -1 : ((*s1 == *s2) ? 0 : 1);
}

现在,问题是,内联 SkipWhitespace 函数是否有意义?我想我在某处读过inline不应该用于包含循环或开关的函数,但我不记得在哪里以及为什么。

4

5 回答 5

14

从历史上看,内联一直向编译器指示它应该将函数体插入调用站点。但是,这不再是有意义的注释。无论是否存在inline限定条件,现代编译器都会内联函数或不内联函数。

需要强调的是,编译器是否会执行内联优化完全不在你的掌控之中

在现代使用中,内联只有一个功能。它可以用来让链接器忽略多个符号,就像在多个编译单元中定义一个函数一样。这种技术可以用来打破循环依赖。将内联用于其他目的。

于 2010-12-23T13:37:03.897 回答
3

我不会说在这种情况下应该使用它,但是如果代码相对较快,内联代码的行为将有更大的好处。

那是因为内联消除了函数调用的开销。如果一个函数调用和返回需要一秒钟,但您内联的代码需要十分钟,那么您不会看到巨大的改进。但是,如果您内联的代码也需要一秒钟,那么您基本上可以将其性能提高一倍。

请记住,这inline是对编译器的一个建议,如果它发现你错了,或者即使它只是令人讨厌,它可以自由地忽略它。我很少使用它,也很少发现需要它,将它归为与autoand相同的类别register(至少在 C 中)。

于 2010-12-23T13:40:24.773 回答
3

inline关键字一直只是对编译器的建议。这意味着如果编译器如此选择,那么它可能会忽略该建议。最重要的是,如果编译器可以内联一个函数,即使您没有要求它这样做,它也可能内联该函数。

也就是说,为了让编译器内联函数,它必须知道函数的主体。如果函数是在单独的编译单元中定义的,那么编译器可能不知道该编译单元之外的函数定义。在这种情况下,编译器只能在定义函数的编译单元内的调用者中内联函数。所以从中得到的一点是,如果你想允许编译器内联一个函数,那么你必须在类定义中定义函数或添加inline关键字并在标题中定义它。内联函数不违反ODR

您应该考虑的另一个问题是,因为内联函数必须驻留在头文件中,并且因为头文件通常包含在许多编译单元中,所以内联函数会增加静态耦合。这意味着更改内联函数的定义将导致编译通过所有相关编译单元的级联。这很重要:函数的定义不应该是接口的一部分,但内联函数会强制这种耦合。

最后一点,最后,我会说永远不要内联函数。也就是说,直到您对应用程序或库的运行时性能感到厌烦,此时您应该运行分析器以查看是否有任何特定函数可以提高内联性能。如果内联函数导致的目标代码比生成函数调用所需的代码更小,那么内联函数也可以减少可执行文件的大小,但这在大多数但很少(嵌入式?)上下文中不是一个重要的决策因素。

探查器可以告诉您,如果一个特定的函数是内联的,它可以提高性能,但它不能告诉您一个特定的内联函数是否可以提高性能(大小,运行时,开发,......)如果它是非内联的。

于 2010-12-23T14:31:07.017 回答
3

一般来说,Profile 在 inlining 之前

内联始终是对编译器的建议。它保留忽略您或同意您的权利。无论如何,它可能会在未经您许可的情况下内联代码的其他部分。

如果您不介意额外输入,这里有一些将方法或函数声明为内联的准则:

  1. 该方法的内容使用小于或等于该方法调用、初始化、清理和返回的指令周期。
  2. 该方法没有跳转或分支到其他方法。
  3. 该方法没有循环(现在,小循环可以放入处理器的缓存中)。
  4. 该方法仅使用内联或可以内联的方法,这包括库函数。
  5. 您正在转换宏 ( #define)。
  6. 该方法通过了上述测试(5除外),并且经过了彻底的测试。(你能说因为你改变了一个标题就重建了所有的依赖关系吗?)

我的风格是内联类 getter 和 setter。任何不稳定的代码(无论是不工作或可能更改)或复杂的代码都不会被内联。

于 2010-12-23T19:26:04.320 回答
2

在上下文中,使用inline.

编译器将内联的函数的复杂性是有限制的;具有复杂循环或开关的功能比没有的功能更有可能达到该限制。因此,您阅读的内容并非全错。它只需要合格。

从风格上讲,我会用一个while循环来代替你的for循环:

while (isspace(*s, std::locale::classic))
    ++s;

这也修复了代码中的错误,该错误仅跳过非空白字符的字符。(在输入此答案时,该错误已修复!

于 2010-12-23T13:39:46.933 回答