我的串联函数strcat(char *,char *,char *)有什么问题?

时间:2009-05-21 21:58:40

标签: c string

我最近接受了采访,并要求撰写mystrcat(*s1, *s2, *s3),其中s1s2是源字符串,连结结果由s3给出。有人告诉我,不要担心s3的内存分配,并假设s1s2不是空/无效字符串。所以我写了下面的蹩脚(粗)程序。我被告知s3出现了问题,或s3可能出错。你能告诉我这是什么/可能吗?

void mystrcat(char *s1, char *s2, char *s3)
{
    if (! (s1 || s2 || s3)) return; // one/more pointers are invalid

    // copy string s1 into s3
    while(*s1) {
      *s3 = *s1;
      s1++;
      s3++;
    }

    // concatenate string s2 into s3
    while(*s2) {
      *s3 = *s2;
      s2++;
      s3++;
    }

    *s3 = '\0';
}
你可以告诉我这里有什么问题吗?什么是更专业的做法?

7 个答案:

答案 0 :(得分:7)

if (! (s1 || s2 || s3) return; // one/more pointers are invalid

应该是

if ((!s1) || (!s2) || (!s3)) return;

答案 1 :(得分:7)

两个可能的点

首先,您被告知输入和输出指向有效字符串,因此可以说不需要测试有效性。如果需要,你应该吵闹失败。更好的是:

 void mystrcat(char *s1, char *s2, char *s3)
    {
        ASSERT( s1 );
        ASSERT( s2 );
        ASSERT( s3 );
        ....

然后,当你可以重用它们时,你基本上写了strcat / strcpy:

void mystrcat(char *s1, char *s2, char *s3)
{
    strcpy( s3, s1 );
    strcat( s3, s2 );
}

如果我正在采访你以外的其他任何事情,我会特别想到你指出我指定的mystrcat界面设计得非常好,并详细介绍了如何改进它。

答案 2 :(得分:5)

这是我的评论

  • s1和s2都应输入const char*,因为您无意修改它们。
  • 您没有验证s3是否有足够的分配空间来包含s1和s2的组合长度
  • 当用户传入错误数据(NULL值)时,您将无声地失败。这不好,因为呼叫者无法区分成功和失败的呼叫
  • 验证s1,s2和s3的方法不为空是不正确的(Igor没错)

在采访中我希望你提出或自我回答的问题

  • 我应该如何表达未能复制字符串?
  • 你想要一个strcat的精确克隆还是更现代的一个并且做更好的参数验证?
  • 我可以使用其他标准str函数来完成答案吗?

答案 3 :(得分:2)

除了之前的答案,还有一件事可能出错: s3可以指向s1或s2字符串的中间位置。如果这是一个合法的条件,那么你需要更复杂的实现。

答案 4 :(得分:1)

对于您的函数定义可能会有一些批评,但在问题语句的约束下,您的函数将产生正确的答案。这在技术上并不错误。

我的猜测是问题没有得到有效沟通,或者访谈员的批评没有得到有效沟通。也许澄清这些是测试的一部分,嗯?

以下是可能投诉的快速摘要......

  • 此行有逻辑错误...

    if(!(s1 || s2 || s3))return;

...因为如果 all 为null,它将返回,但如果任何为null,您可能希望返回。这不会导致失败,因为问题语句说none都不能为空。

  • 上面提到的同一行无声地失败了。最好返回错误代码,抛出异常或断言。沉默的失败是不好的做法,但在技术上仍然是正确的,因为在问题的范围内,函数不会失败。
  • s1和s2可以是const char *类型,以提高可读性和效率(const有助于一些编译器优化)。
  • 修改参数变量通常被认为是错误的 实践。另一个问题 可读性。
  • 您可以使用现有功能 strcpy和strcat。另一件事 可读性和效率。

优秀的程序员应该在技术正确性方面超越可读性,效率和强大的错误处理,但这些主要是主观的和情境性的。无论如何,忽略你的第一个if语句中的错误,我认为你的函数读得很好并完成了工作。 :)

答案 5 :(得分:1)

  1. 无效检查

    if(!(s1 || s2 || s3))返回; //一个/多个指针无效

    它实际上检查至少一个指针是否不为0,例如至少有一个指针有效。  它应该是

    if(!s1 ||!s2 ||!s3)返回;

  2. 您无法检查s3是否很大 足够或如果你在外面写作 (但假设是s3很大 对吗? - 但它仍然会 不适合真正的工作)

  3. 您无法跳过null 从s1的末尾复制 进入s3。在s3中将0附加到s2之后 所以它最终会结束 “s1stringvalueNULLs2stringvalue” (这里的NULL表示值0或null或 在实际代码中为零,这是为了 插图)。任何C方法 期望以null结尾的字符串 只能看到“s1stringvaluepart”和 将停在null。

  4. 这是一个解决方案:

    void mystrcat(char *s1, char *s2, char *s3)
    {
        if (!s1 || !s2 || !s3) return;
    
        while (*s3++ = *s1++);
        if (!*(s3-1)) --s3;             // reverse over null in s1
        while (*s3++ = *s2++);
    
        *s3 = 0;
    }
    

    您可以使用以下方法进行测试:

    #include <iostream>
    using namespace std;
    
    int main()
    {
        char s1[] = "short";
        char s2[] = "longer";
        char s3[20];
    
        memset(s3, 0, sizeof(s3));
        mystrcat(0,s2,s3);
        cout << "2: " << s3 << endl;
    
        memset(s3, 0, sizeof(s3));
        mystrcat(s1,0,s3);
        cout << "3: " << s3 << endl;
    
        memset(s3, 0, sizeof(s3));
        mystrcat(s1,s2,0);
        cout << "4: " << s3 << endl;
    
        memset(s3, 0, sizeof(s3));
        mystrcat(s1,s2,s3);
        cout << "1: " << s3 << endl;
    
    }
    

答案 6 :(得分:0)

如果我错了,请纠正我。复制s1后,s3的最后一个字符不是'\ 0'吗?那么在某些情况下,s2中的剩余字符永远无法读取?

从我的理解

while(*s3++ = *s1++);

将复制字符,直到达到NULL并且'\ 0'不是NULL,或者它是否被视为?如果我可以假设s1 =“你好”而s2 =“世界!”然后在复制s1之后,s3看起来像:Hello \ 0然后复制“世界!” bit会导致s3看起来像:Hello \ 0 world!\ 0

它有意义吗?我的理解是否正确?