c - 重新实现 strlen (C)

标签 c string function macros

菜鸟警报:

出于学习目的,我被赋予了重新实现 strlen() 函数的任务,我得到了这样的想法:最好使用宏等函数而不是函数来完成此操作, 我的理由是,使用宏我不必处理将字符串传递给函数的问题。

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

最佳答案

在编译程序时,宏只会展开一次。由于时间旅行不是 C 语言的一部分,因此程序的 future 执行不可能追溯更改宏的结果。因此,如果计算(例如计算字符串的长度)依赖于程序编译时未知的信息,则宏完全没有用处。除非字符串恰好是文字,否则就会出现这种情况。而且我敢断言,绝大多数情况下,所需长度的字符串在程序编译时并不存在。

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

在常量字符串文字上使用 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 的调用。

关于c - 重新实现 strlen (C),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50656792/

相关文章:

检查括号平衡的C程序

c - 数组赋值给C中的指针

java getBytes() 在 python 中等效

python - 可以使用字符串格式来分割字符串值吗?

c# - 在另一个已经打开的窗体上执行函数

python - 查找定义 python 函数的位置

projecteuler中问题10的混淆解决方案

c++ - 如何将 CURLOPT_DNS_CACHE_TIMEOUT 与共享 DNS 缓存一起使用

c++ - 带有 vsprintf (C++) 的查询助手的 MySQL 动态字符串大小

c - 输入 fget 时检测到堆栈崩溃