【问题标题】:Re-implementing strlen (C)重新实现 strlen (C)
【发布时间】:2018-11-12 09:14:07
【问题描述】:

新手提醒:

出于学习目的,我被赋予了重新实现 strlen() 函数的任务,我认为最好使用宏之类的函数而不是函数来完成此操作, 我的理由是使用宏我不必处理将字符串传递给函数。

你的想法是什么? 在这种情况下创建适当的函数还是宏更好?

【问题讨论】:

  • 如你所愿。先制作函数,然后实现为宏。 C 中有类似函数的宏可以接受参数。
  • Macro vs Function in C的可能重复
  • 类似函数的宏通常是个坏主意。并且将函数传递给字符串是微不足道的,完全没有问题。使其成为常规功能。 (从函数中返回字符串可能很棘手,但当然strlen 不必这样做。)
  • 据我所知,使用宏我可以使用“sizeof”运算符,但在函数中“sizeof”不起作用,因为它会返回一个指针,对吗?
  • 您似乎仍想使用宏,尽管建议不要这样做。

标签: c string function macros


【解决方案1】:

在编译程序时,宏只展开一次。由于时间旅行不是 C 语言的一部分,因此将来执行程序不可能追溯更改宏的结果。所以如果一个计算,比如计算一个字符串的长度,依赖于程序编译时不知道的信息,那么宏是完全没用的。除非字符串碰巧是文字,否则就是这种情况。而且我敢断言,在绝大多数情况下,程序编译时所需长度的字符串并不存在。

清楚地了解宏的实际作用(在编译前修改程序文本)将有助于避免分心,例如本问题中的建议。

在常量字符串文字上使用strlen 有时会很有用,以避免将来修改字符串文字时可能引入的错误。例如下面的(测试line是否以Hello开头):

/* Code smell: magic number */
if (strncmp(line, "Hello", 5) == 0) { ... }

最好写成:

/* Code smell: redundant repetition, see below */
if (strncmp(line, "Hello", strlen("Hello")) == 0) { ... }

显然,如果计算可以在编译时执行一次,那么最好这样做而不是在程序运行时重复执行。曾几何时,当编译器很原始并且几乎无法理解控制流时,担心这些事情是有道理的,尽管即使在那时,很多手动优化也过于复杂,无法获得微不足道的好处。

今天,即使是这个借口也不适用于过早的优化器。大多数现代 C 编译器都能够完美地用常量 5 替换 strlen("Hello");,因此永远不会调用库函数。实现这种优化不需要宏魔法。

如前所述,示例中的测试仍有不必要的前缀字符串重复。我们真正想写的是:

 if (startsWith(line, "Hello")) { ... }

在这里将startsWith 定义为宏的诱惑将非常强烈,因为看起来简单的编译时替换就足够了。应该避免这种诱惑。现代编译器也能够“内联”函数调用;也就是将调用的主体直接插入到代码中。

所以定义:

static int startsWith(const char* line, const char* prefix) {
  return strncmp(line, prefix, strlen(prefix)) == 0;
}

将与其对应的宏一样快,并且与宏不同,当使用带有副作用的第二个参数调用它时不会导致问题:

/* Bad style but people do it */
if (startsWith(line, prefixes[++i])) { doAction(i); }

一旦调用被内联,编译器就可以继续应用其他优化,例如在前缀参数是字符串文字的情况下消除对 strlen 的调用。

【讨论】:

    猜你喜欢
    • 2011-10-14
    • 2012-08-01
    • 2014-04-26
    • 2013-11-30
    • 2014-08-05
    • 2014-06-12
    • 1970-01-01
    • 2016-04-11
    • 2010-12-16
    相关资源
    最近更新 更多