【问题标题】:concatenate two strings without strcat在没有 strcat 的情况下连接两个字符串
【发布时间】:2015-12-27 18:21:15
【问题描述】:

我想在不使用 strcat() 函数的情况下连接两个字符串。但我没有得到所需的结果。请指出我的错误。

void main() {
    char s1[100], s2[100];
    int i;

    puts("First string?");
    gets(s1);

    puts("Second string?");
    gets(s2);

    for (i = strlen(s1); i <= (strlen(s1) + strlen(s2)); i++) {
        s1[i] = s2[i - strlen(s1)];
    }

    puts(s1);
}

【问题讨论】:

  • 在调试器中逐行执行代码,应该很明显。也不要使用gets,它自 C99 标准以来已过时,并在 C11 标准中被删除。
  • 尽管有解决方案,但永远不要使用gets(手册页本身说明了这一点)。它不会检查缓冲区的边界,因此它非常不安全。
  • 这是作业题吗?
  • 我知道这是一个愚蠢的可怕错误。

标签: c string strcat


【解决方案1】:

我在你的程序中并没有真正改变太多,但这是我所拥有的(根据你的修改)并且它有效。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
  {
    char s1[100], s2[100];
    size_t i, s1l, s2l, total;

    puts("First string?");
    fgets(s1, sizeof(s1), stdin);
    s1[strlen(s1) - 1] = 0x00;

    puts("Second string?");
    fgets(s2, sizeof(s2), stdin);
    s2[strlen(s2) - 1] = 0x00;

    s1l = strlen(s1);
    s2l = strlen(s2);
    total = s1l + s2l;

    for(i = s1l; i <= total; i++)
      {
        s1[i] = s2[i - s1l];
      }

    puts(s1);

    return(0);
  }

逐字逐句使用您的程序,问题在于,由于 s1 的长度在循环的每次迭代中都在变化,因此检查中的 strlen(s1) 的值不断增加,因此您基本上会以无限循环结束。 ..至少直到它遇到一些随机的空字节。但是,当事先获取字符串的长度时,循环终点计数不会改变,从而导致正确的输出。我的代码将使用 clang 编译器在 FreeBSD 10.2 系统上正确编译和运行。

附带说明,请勿在 main 上返回 void。 总是返回整数。还要指定您计划使用的库头。

编辑:我修改了代码以使用 fgets 而不是 gets,因为 gets 完全不安全。它不检查缓冲区大小,因此您很容易发生缓冲区溢出。

【讨论】:

  • 我不是投反对票的人,但似乎我们今天面对的是一个真正的A玩家,他对自己的看法有点过于优越了。顺便说一句,您的回答很好地解释了 OP 的问题。
  • 我更感兴趣的是回答 OP 关于为什么他的代码不起作用的问题。然后我出于安全考虑进行编辑并发布编辑后的版本。剥离 LF 字符很简单。我将添加该代码。
  • 你是对的。我错过了那个小错误。可能是因为我使用 test1 和 test2 作为输入。
【解决方案2】:

s1 的长度在运行时发生变化,使您的索引 i 不正确。试试下面的方法:-

l = strlen(s1);

for(i = 0; i < strlen(s2); i++)
{
   s1[l++] = s2[i];
}

s1[l] = '\0';

假设 s1 = "hello" 和 s2 = "world",那么在第一次迭代中,s1 = 5 并且索引为 s2 = 0(i-length(s1));效果很好。但在第二次迭代中,长度(s1)= 6,s2 的索引= 0(i-length(s1))。所以从 s2 获取字符的索引没有改变。这与您的实现中的问题有关,尽管您应该使用 sprintf 以一种有效的方式。

sprintf(s1, "%s%s", s1, s2);

【讨论】:

  • l 看起来与1 非常相似...最好将此变量重命名为len
  • 他想知道当前实现的问题是什么,所以问题是索引。显然应该使用 sprintf。
  • 实际上,sprintf(s1, "%s%s", s1, s2); 调用了未定义的行为,因为s1 既是目标参数,也是源参数之一。
  • 恐怕你没有看到明显的。您不能传递与sprintf 的输出数组和%s 格式的输入相同的缓冲区。您的代码可能会奇迹般地产生您所期望的结果,但不能保证。 C11 7.21.6.6:sprintf 函数等效于 fprintf,除了输出写入数组(由参数 s 指定)而不是流。在所写字符的末尾写一个空字符;它不计为返回值的一部分。如果复制发生在重叠的对象之间,则行为未定义。
【解决方案3】:

使用 sprintf 的变体:

更简单

char buf[200];
sprintf(buf, "%s%s" s1, s2);

更简单更安全

char buf[200]
snprintf(buf, sizeof(buf), "%s%s", s1, s2);//prevents buffer overflow

或者,修改你的循环(原因参见 cmets 行):

int main(void)//note changed main prototype
{
        char s1[100], s2[100];//caution here: s1 must be big enough
                              //to contain its string as well as
                              //the string stored in s2, plus 1 for NULL
        int i;
        int len1, len2;//create variables to store string lengths

        puts("First string?");
        gets(s1);//note, gets is no longer recommended

        puts("Second string?");
        gets(s2);
        len1 = strlen(s1);//store string lengths only once
        len2 = strlen(s2);//to avoid calling them repeatedly in loop

        for(i = 0; i < len2; i++)//index i in one place only
        {
            s1[len1+i] = s2[i];
        }
        s1[len1 + i]=0;//null terminate string when done.
        puts(s1);
        getchar();//added to pause execution in my environment.
        return 0;
}

此处显示了使用上述修改的示例会话:

【讨论】:

  • 因为这就像试图重新发明轮子。有些功能可以做到这一点,而且使用起来可能更安全。
  • 您的算法没有错,但效率低下。每次循环迭代都调用 strlen 三次。
  • gets也删掉!
  • @chqrlie - 编辑 &lt;=&lt;
【解决方案4】:

您的解决方案不起作用,因为您在每次迭代时重新计算 strlen(s1) 以测试是否完成并计算 s2 中的偏移量以从中复制字符,但您在循环中修改 s1,因此长度会发生变化,更糟糕的是,s1 暂时不再是 '\0' 终止:测试表达式 i &lt;= strlen(s1) + strlen(s2) 调用未定义的行为,当您通过循环第二次复制 s2[i - strlen(s1)] 时也会发生同样的情况。

使用这些想法来更正您的代码:

  • 不要使用gets 读取输入,使用fgets() 并删除最后的'\n'

  • 只计算一次s1s2的长度并将它们存储在局部变量中。

  • 验证连接不会超过s1的大小。

  • 用这些局部变量重写你的循环,或者使用strcpymemcpy

这是一个例子:

#include <stdio.h>
#include <string.h>

int main(void) {
    char s1[100], s2[100];
    size_t i, len1, len2;

    puts("First string?");
    if (!fgets(s1, sizeof s1, stdin))
        return 1;

    len1 = strlen(s1);
    if (len1 > 0 && s1[len1 - 1] == '\n')
        s1[--len1] = '\0';

    puts("Second string?");
    if (!fgets(s2, sizeof s2, stdin))
        return 1;

    len2 = strlen(s2);
    if (len2 > 0 && s2[len2 - 1] == '\n')
        s1[--len2] = '\0';

    if (len1 + len2 >= sizeof s1)
        return 2;

    /* copy the characters from s2 including the final '\0' */
    for (i = 0; i <= len2; i++) {
        s1[len1 + i] = s2[i];
    }

    puts(s1);

    return 0;
}

【讨论】:

    【解决方案5】:
    // A simple strcat function
    
    int main(void)
    {
        char str1[100] ="Avis";
        stringcat(str1,"karthik");
        printf("\n%s",str1);
        return 0;
    }
    void stringcat(char *str1, char *str2)
    {
       while(*str1)
           str1++;
       while(*str2)
           *str1++ = *str2++;
    }
    

    【讨论】:

    • 关闭,但没有雪茄!您忘记复制最终的'\0'。用这种方式重写你的第二个循环:while ((*str1++ = *str2++) != '\0') continue;
    猜你喜欢
    • 2020-08-19
    • 1970-01-01
    • 1970-01-01
    • 2021-03-28
    • 2021-02-09
    • 2011-05-18
    • 1970-01-01
    • 1970-01-01
    • 2015-06-02
    相关资源
    最近更新 更多