我自己的带指针的 strcat 函数不能正常工作

My own strcat function with pointers does not work right

我是指针的新手,想学好指针。所以这是我自己尝试编写 strcat 函数。如果我 return 只是 a 它打印一些二进制的东西(我认为它应该打印解决方案),如果我 return *a 它说 seg fault core dumped 我不能'找不到错误。接受任何帮助谢谢。

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

int main() {
    char *strcaT();

    char *a = "first";

    char *b = "second";

    printf("%s", strcaT(a, b));

    return 0;
}

char *strcaT(char *t, char *s) {   
    char buffer[strlen(t) + strlen(s) - 1];

    char *a = &buffer[0];

    for (int i = 0; i < strlen(s) + strlen(t); i++, t++) {
        if (*t == '\n') {
            for (int i = 0; i < strlen(s);i++) {
                buffer[strlen(t) + i] = *(s + i);
            }
        }
        buffer[i] = *(t + i);
    }
    return a;
}

return一些局部变量是一个非常糟糕的主意,它会在函数完成操作后被清除。以下功能应该可以工作。

char* strcaT(char *t, char *s)
{
    char *res = (char*) malloc(sizeof(char) * (strlen(t) + strlen(s) + 1));

    int count = 0;

    for (int i = 0; t[i] != '[=10=]'; i++, count++)
        res[count] = t[i];

    for (int i = 0; s[i] != '[=10=]'; i++, count++)
        res[count] = s[i];

    res[count] = '[=10=]';

    return res;
}

我已将您的程序更改为如下所示:

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

int main()
{
    char* strcaT();

    char* a = "first";

    char* b = "second";

    printf("%s",strcaT(a,b));

    return 0;
}
char* strcaT(char *t, char *s)
{   

    char* a = (char*)malloc(sizeof(char)*(strlen(t) + strlen(s) + 1));

    for(int i=0; i<strlen(t); i++) {
        a[i] = t[i];
    }
    for(int i=0; i<strlen(s); i++) {
        a[strlen(t) + i] = s[i];
    }
    a[strlen(t)+strlen(s)] = '[=10=]';
    return a;
}

您遇到了段错误,因为您正在 returning 本地数组的地址,该数组在堆栈上并且在您 return 之后将无法访问。其次是您连接字符串的逻辑很复杂。

对于初学者来说,函数 strcaT 应该将第二个参数指定的字符串附加到第一个参数指定的字符串的末尾。所以第一个参数应该指向一个足够大的字符数组来存储附加的字符串。

你的函数不正确,因为至少它 returns 一个指向局部可变长度字符数组的(无效)指针,该数组在退出函数后将不再存在,而且数组的大小小于实际大小需要存储两个字符串而不是

char buffer[strlen(t) + strlen(s) - 1];
                                 ^^^

至少应该像

那样声明
char buffer[strlen(t) + strlen(s) + 1];
                                 ^^^

并且可以声明为静态

static char buffer[strlen(t) + strlen(s) + 1];

嵌套循环也没有意义。

注意在调用函数前先提供函数原型。在这种情况下,编译器将能够检查传递给函数的参数。而且函数名 strcaT 令人困惑。至少该函数可以命名为 strCat.

函数可以这样定义

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

char * strCat( char *s1, const char *s2 )
{
    char *p = s1 + strlen( s1 );

    while ( ( *p++ = *s2++ ) );

    return s1;
}

int main(void) 
{
    enum { N = 14 };
    char s1[N] = "first";
    char *s2 = "second";

    puts( strCat( s1, s2  ) );

    return 0;
}

程序输出为

firstsecond

另一方面,如果您已经在使用标准 C 函数 strlen 那么为什么不使用另一个标准 C 函数 strcpy

有了这个函数,你的函数可以定义得更简单,比如

char * strCat( char *s1, const char *s2 )
{
    strcpy( s1 + strlen( s1 ), s2 );

    return s1;
}

如果您想构建一个包含两个字符串的新字符数组,一个附加到另一个,则该函数可以按以下方式查找示例。

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

char * strCat( const char *s1, const char *s2 )
{
    size_t n1 = strlen( s1 );

    char *result = malloc( n1 + strlen( s2 ) + 1 );

    if ( result != NULL )
    {
        strcpy( result, s1 );
        strcpy( result + n1, s2 );
    }       

    return result;
}

int main(void) 
{
    char *s1 = "first";
    char *s2 = "second";

    char *result = strCat( s1, s2 );

    if ( result ) puts( result );

    free( result );

    return 0;
}

程序输出同样是

firstsecond

当然可以调用标准 C 函数 strcpy 您可以用自己的循环代替,但这没有多大意义。

如果不允许使用标准的 C 字符串函数,则可以通过以下方式实现上述函数。

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

char * strCat( const char *s1, const char *s2 )
{
    size_t n = 0;

    while ( s1[n] != '[=18=]' ) ++n;

    for ( size_t i = 0; s2[i] != '[=18=]'; )
    {
        n += ++i;
    }

    char *result = malloc( n + 1 );

    if ( result != NULL )
    {
        char *p = result;
        while ( ( *p = *s1++ ) != '[=18=]' ) ++p;
        while ( ( *p = *s2++ ) != '[=18=]' ) ++p;
    }       

    return result;
}

int main(void) 
{
    char *s1 = "first";
    char *s2 = "second";

    char *result = strCat( s1, s2 );

    if ( result ) puts( result );

    free( result );

    return 0;
}

代码有多个未定义行为的情况:

  • 你return一个本地数组的地址在strcaT自动存储,这意味着这个数组一旦超出范围就不能再使用,即:当你从函数 return.

  • 缓冲区太小,应该是长度之和加1作为空终止符。您写入超出此本地数组的末尾,可能会覆盖一些重要信息,例如调用者的帧指针或 return 地址。这种未定义的行为很有可能导致分段错误。

  • 您从第一个字符串复制 strlen(t)+strlen(s) 个字节,访问超出 t 的末尾。

  • 不清楚为什么要测试'\n'并在第一个字符串中换行符的位置复制第二个字符串。字符串不以换行符结尾,它们可能包含换行符但在空终止符处(字节值 '[=16=]' 或简单地 0)。 fgets() 读取的字符串可能在空终止符之前有一个尾随换行符,但并非所有字符串都有。在您的循环中,当您继续从第一个字符串复制字节时,复制第二个字符串的效果会立即取消,甚至超出其空终止符。您应该分别执行这些循环,首先从 t 复制,然后从 s 复制,而不管任一字符串是否包含换行符。

另请注意,在 main() 中局部声明 strcaT() 是非常糟糕的风格,甚至没有适当的原型。在 main 函数及其参数列表之前声明此函数。

这是分配连接字符串的修改版本:

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

char *strcaT(const char *s1, const char *s2);

int main() {
    const char *a = "first";
    const char *b = "second";
    char *s = strcaT(a, b);
    if (s) {
        printf("%s\n", s);
        free(s);
    }
    return 0;
}

char *strcaT(const char *t, const char *s) {
    char *dest = malloc(strlen(t) + strlen(s) + 1);
    if (dest) {
        char *p = dest;
        /* copy the first string */
        while (*t) {
            *p++ = *t++;
        }
        /* copy the second string at the end */
        while (*s) {
            *p++ = *s++;
        }
        *p = '[=10=]';  /* set the null terminator */
    }
    return dest;
}

但是请注意,这不是 strcat 函数的作用:它将第二个字符串复制到第一个字符串的末尾,因此第一个字符串结束后必须有足够的 space其数组中的字符串以适合第二个字符串,包括空终止符。 main()ab 的定义不适合这些语义,您必须使 a 成为一个数组,其大小足以容纳两个字符串。

这是使用这种方法的修改版本:

#include <stdio.h>

char *strcaT(char *s1, const char *s2);

int main() {
    char a[12] = "first";
    const char *b = "second";
    printf("%s\n", strcaT(a, b));
    return 0;
}

char *strcaT(char *t, const char *s) {
    char *p = t;
    /* find the end of the first string */
    while (*p) {
        *p++;
    }
    /* copy the second string at the end */
    while (*s) {
        *p++ = *s++;
    }
    *p = '[=11=]';  /* set the null terminator */
    return t;
}

在主函数中

char *strcaT();

应该在main函数之前声明:

char *strcaT(char *t, char *s);
int main() {...}

你returns本地数组buffer[],这是未定义的行为,因为在strcaT函数之外,它可能不存在。你应该使用指针然后为它分配。

您的 buffer 的大小应该是 +1 而不是您在代码中所做的 -1

char *strcaT(char *t, char *s) {   
    char *a = malloc(strlen(t) + strlen(s) + 1);
    if (!a) {
        return NULL;
    }
    int i;
    for(i = 0; t[i] != '[=12=]'; i++) {
        a[i] = t[i];
    }

    for(int j = 0; s[j] != '[=12=]'; j++,i++) {
        a[i] = s[j];
    }
    a[i] = '[=12=]';
    return a;
}

完整测试代码:

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

char *strcaT(char *t, char *s);

int main() {

    char *a = "first";
    char *b = "second";
    char *str = strcaT(a, b);
    if (str != NULL) {
       printf("%s\n", str);
       free(str); // Never forget freeing the pointer to avoid the memory leak
    }
    return 0;
}

char *strcaT(char *t, char *s) {   
    char *a = malloc(strlen(t) + strlen(s) + 1);
    if (!a) {
        return NULL;
    }
    int i;
    for(i = 0; t[i] != '[=13=]'; i++) {
        a[i] = t[i];
    }

    for(int j = 0; s[j] != '[=13=]'; j++,i++) {
        a[i] = s[j];
    }
    a[i] = '[=13=]';
    return a;
}