【问题标题】:C strlen() implementation in one line of code一行代码中的 C strlen() 实现
【发布时间】:2014-04-26 13:21:54
【问题描述】:

昨天我在面试时被要求在 C 中实现 strlen() 而不使用任何标准函数,全部手动完成。作为一个绝对的业余爱好者,我用 while 循环实现了原始版本。看着这个,我的面试官说它可以只用一行代码来实现。那时我无法使用该术语生成此代码。面试后我问了我的同事,他们中最有经验的给了我这篇文章,真的很好用:

size_t str_len (const char *str)
{
    return (*str) ? str_len(++str) + 1 : 0;
}

所以有一个问题,是否可以不使用递归,如果可以,如何? 条款:

  • 没有任何汇编程序
  • 库中不存在任何 C 函数
  • 不只是将几串代码拼在一起

请注意,这不是优化或实际使用的问题,只是完成任务的可能性。

【问题讨论】:

  • 这可能更适合代码高尔夫
  • “一行”是一个相当宽松的要求。
  • 能够在一行中实现 strlen() 是毫无意义的。我绝不会在面试或其他情况下要求任何人这样做。
  • @gnasher729:同意。再说一次,大多数面试“难题”问题都是毫无意义的,除非作为观察候选人思维方式的机会......假设他们还没有答案,在这种情况下,除了他们有一些答案之外,它什么也不会告诉你经验。
  • 在 C++ 中你可以使用尾递归:size_t strlen(const char *str, size_t acc = 0) { return *str ? strlen(str+1, acc + 1) : acc; } 我猜 应该 编译成一个循环

标签: c algorithm strlen


【解决方案1】:

类似于@DanielKamilKozar 的回答,但使用 for 循环,您可以在没有 for 循环主体的情况下执行此操作,并且 len 在函数中被正确初始化:

void my_strlen(const char *str, size_t *len)
{
    for (*len = 0; str[*len]; (*len)++);
}

【讨论】:

  • 啊,很聪明!如此明显。
  • for (*len = 0; *str; ++str, (*len)++); 怎么样?这样您就不必在每次迭代时取消引用 len 以索引到 str
  • @RemyLebeau 是的,这也行。我想如果你正在优化这个,你需要分析哪个是最快的偏离或额外的增量。无论哪种方式,编译器都可能将strlen 都放在寄存器中,因此两种方式都可能没有太大区别
  • strlen 原型是size_t strlen (const char *str)
  • 这与 strlen 的签名不同。
【解决方案2】:

我能想到的最好的就是这个,但这不是标准的strlen,因为函数本身有不同的原型。此外,它假定*len 在开始时为零。

void my_strlen(const char *str, size_t *len)
{
        while(*(str++)) (*len)++;
}

我很好奇如何在“一行代码”中实现标准 strlen,因为根据您发布的内容判断,它将需要 return,即“一行代码” .

也就是说,我确实同意 cmets 的说法,即这是一个非常愚蠢的面试问题。

【讨论】:

    【解决方案3】:
    size_t str_len (const char *str)
    {
        for (size_t len = 0;;++len) if (str[len]==0) return len;
    }
    

    【讨论】:

    • 我会用这个:for (size_t len = 0; ; ++str, ++len) { if (!*str) return len; }
    • 注意条件:without just spelling few strings of code in one
    • 行以换行符结尾.. 你可能会想到“语句”
    • +1 鉴于问题的表述含糊不清,我认为这是最好的答案:它保留了strlen() 的功能和签名,并且可以说是在一行。
    • @MattMcNabb:从技术上讲,您是正确的,问题描述肯定可以改写得更准确;但是将三个语句放在一行中显然不能满足 IMO 问题的意图。
    【解决方案4】:

    如果这不必是一个函数,那么这个单行可能是计算任何以 null 结尾的数组(不包括变量声明和打印)长度的最简单和最短的方法:

    int main() {
        const char *s = "hello world";
        int n = 0;
    
        while (s[++n]);
    
        printf ("%i\n", n);
        return 0;
    }
    

    如果它必须是一个函数,那么你不能有一个精确的函数签名作为 strlen(),因为返回必须在单独的行中。否则你可以这样做:

    void my_strlen(int* n, const char* s) {
        while (s[++(*n)]);
    }
    
    int main() {
        const char *s = "hello world";
        int n = 0;
    
        my_strlen(&n, s);
    
        printf ("%i\n", n);
        return 0;
    }
    

    【讨论】:

    • 您的 while 循环计算空终止符,因此长度将始终比 strlen() 返回的长度高 +1。您必须使用 while (s[n]) ++n; 来解决这个问题。
    • 该修复为大小为 0 的字符串引入了未定义的行为。大小为 0 的字符串是有效字符串,strlen 在这种情况下也不会失败。 Remy Lebeau 提供了正确的解决方案。
    • 这里有一些其他的方法来解决它:if (*s) while (s[++n]);; while (s[n++]); n--;; for (n = -1; s[++n];);.
    【解决方案5】:

    这似乎工作正常。

    unsigned short strlen4(char *str)
    {
        for (int i = 0; ; i++) if (str[i] == '\0') return i;
    }
    

    有什么想法吗?

    更新!

    这可能是实现 strlen 的最有效方式。大多数编译器都使用以一种或另一种方式实现的这个。

    这可能有点难以理解,尤其是在没有学习过指针的情况下。

    size_t strlen(char *str)
    {
        register const char *s;
        for (s = str; *s; ++s);
        return(s - str);
    }
    

    【讨论】:

    • 我在很久以前就编写了这个代码,我可以随时做出更好的实现! :D
    • 我很好奇你更好的实现是什么? (顺便说一句,我刚刚注意到其他人发布了相同的答案。)
    • register 变量?又是 1994 年吗?
    【解决方案6】:

    空的 for 循环怎么样。喜欢

    int i=0;
    for(; str[i]!=0; ++i);
    

    【讨论】:

    • 这不是完整的功能。 return 在哪里?
    • @DanielKamilKozar 已编辑 - 计数器必须在循环外初始化。但它仍然算在一行中!?
    • 这不会产生 strlen 函数,因此如果需要,它不会计算在内。
    • 看起来像两行
    【解决方案7】:

    与其他人所说的相反,不可能遵循一些合理的限制。限制是:

    • 函数体中的一个非空语句声明(非空体的复合语句至少总共有两个语句)
    • 与真实strlen相同的签名
    • 没有递归

    任何带有strlen 签名的函数都必须有一个return 语句。 return 语句由return 关键字和一个表达式组成。 C 语言没有执行循环的表达式。为此需要一个声明,但这个位置是为返回声明保留的。

    如果放宽这些限制(例如,更改签名,或者不计算返回语句和/或声明和/或复合语句的主体,只要它们是“简单的”,作为单独的“行”)任务变得可能并且相当简单,正如在此处和整个互联网上的许多示例中可以看到的那样。o

    【讨论】:

    • size_t strlen (const char *s) { for (auto p = s;; ++p) if (!*p) return p - s; } 怎么没有一个说法?
    • @pmttavara 我数了三个语句(for、if 和 return)。当 C 有 auto 时请给我打电话。
    • 这是对语句的一种不可行的限制性定义。
    • @pmttavara 如果您不将嵌套语句算作语句,您可以将所有内容包装在一个块中并收工。
    • 那些不是嵌套语句,它们是复合语句。 p = s; p++; 显然是两个语句。 if (*p) p = s; 是一个带有简单语句的控制语句。我不是在说“嘿,看这是一行,因为我删除了所有换行符”。
    【解决方案8】:
    size_t strlen (const char* str) {
        for (const char* s1 = str; ; s1++) if (!(*s1)) return s1 - str; 
    }
    

    与 str[i] 相比,上面增加指针的代码会执行得更好,因为索引 str[i] 将评估为: *(str + i) 这是每个循环完成的加法运算,而增量操作更快。

    另外,与 strlen(const char *str, size_t *len) 相比,调用者不需要提供额外的参数。

    最后,按照原始请求在一行上。

    【讨论】:

    • 虽然此代码可能会回答问题,但提供有关它如何和/或为什么解决问题的额外上下文将提高​​答案的长期价值。
    • 与 str[i] 相比,增加指针的代码会执行得更好,因为索引 str[i] 会计算为: *(str + i) 这是对每个循环,而增量操作更快。此外,与 strlen(const char *str, size_t *len) 相比,调用者不需要提供额外的参数。最后,按照原始请求在一行上。
    • 添加到答案中。
    【解决方案9】:

    本机代码

    unsigned int strlen(const char *s)
    {
        int l;for(l=0;s[l]!='\0';l++);return l;
    }
    

    你可以用零替换'\0',但我更喜欢它

    希望对你有帮助。

    【讨论】:

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