strncat 导致缓冲区溢出

时间:2021-03-18 09:59:36

标签: c concatenation c-strings function-definition

如何将 strncat 与堆对象一起使用?

我正在尝试编写一个简单的函数来将 2 个字符串连接在一起返回结果,但是,如果不使返回缓冲区非常大(为其长度增加大约 5000),我就无法运行它,以免溢出。< /p>

我可能只是错误地使用了 strncat 函数,使用堆对象而不是固定长度的字符数组。但我不知道我会怎么写。

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

char *concatStrings(char *arg1, char *arg2) {
    char *ret = malloc(strlen(arg1) + strlen(arg2));
    strncpy(ret, arg1, strlen(arg1));
    strncat(ret, arg2, strlen(arg2));
    ret[strlen(arg1)+strlen(arg2)] = '\0';
    return ret;
}

int main(int argc, char *argv[]) {
    if (argc == 3) {
        char *print = concatStrings(argv[1], argv[2]);
        printf("\n%s", print);
        free(print);
    }
    return 0;
}

4 个答案:

答案 0 :(得分:3)

第一个问题是,你没有分配足够的内存,因为在 C 中,字符串以 0-byte 结尾,所以它应该是

char *ret = malloc((strlen(arg1) + strlen(arg2))+1);

当然你应该检查 malloc 是否有效。

if (!ret)
    // error

第二个问题是,您在这里使用了带有硬编码长度的 strncpy。 只需使用 strcpy,因为您已经分配了足够的内存。 出于同样的原因,您也可以使用 strcatstrncat 没有提供任何额外的好处,实际上会使代码变慢,因为您再次调用 strlen

这个

ret[10 + strlen(arg2)] = '\0';

根本不需要。事实上,如果 arg1 少于 10 个字符,它就有潜在的危险。

答案 1 :(得分:3)

对于初学者来说,函数应该像这样声明

char * concatStrings( const char* arg1, const char* arg2 );

因为指针 arg1arg2 指向的字符串在函数内没有改变。

在这个内存分配中

char *ret = malloc(strlen(arg1) + strlen(arg2));

您忘记为空终止符 '\0' 保留内存。你必须写

char *ret = malloc( strlen(arg1) + strlen(arg2) + 1 );

在此调用中使用幻数 10

strncpy(ret,arg1,10);

没有意义。

如果你会写例如

strncpy(ret,arg1,strlen(arg1));

然后下一个调用

strncat(ret,arg2,strlen(arg2));

将调用未定义的行为,因为调用 strncpy 没有将空终止字符“\0”附加到指针 ret 指向的字符串。

至少写一下会好得多

strcpy( ret, arg1 );

在任何情况下,您的函数实现都是低效的。例如,对于参数 arg2

有两次调用函数 strlen
char *ret = malloc(strlen(arg1) + strlen(arg2));
//...
strncat(ret,arg2,strlen(arg2));

同样,strncat 的调用也是低效的,因为该函数需要遍历整个目标字符串以找到它的终止零。

可以通过以下方式定义函数

char * concatStrings( const char* arg1, const char* arg2 )
{
    size_t n1 = strlen( arg1 );
    char *ret = malloc( n1 + strlen( arg2 ) + 1 );

    if ( ret != NULL )
    {
        strcpy( ret, arg1 );
        strcpy( ret + n1, arg2 );
    }

    return ret;
}

这是一个演示程序。

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

char * concatStrings( const char* arg1, const char* arg2 )
{
    size_t n1 = strlen( arg1 );
    char *ret = malloc( n1 + strlen( arg2 ) + 1 );

    if ( ret != NULL )
    {
        strcpy( ret, arg1 );
        strcpy( ret + n1, arg2 );
    }

    return ret;
}

int main(void) 
{
    const char *argv1 = "Hello ";
    const char *argv2 = "World!";
    
    char *print = concatStrings( argv1, argv2 );
    
    if ( print != NULL ) puts( print );
    
    free( print );
    
    return 0;
}

程序输出为

Hello World!

最好在函数内用 strcpy 的第一次调用替换 memcpy。那就是函数也可以看起来像

char * concatStrings( const char* arg1, const char* arg2 )
{
    size_t n1 = strlen( arg1 );
    char *ret = malloc( n1 + strlen( arg2 ) + 1 );

    if ( ret != NULL )
    {
        memcpy( ret, arg1, n1 );
        strcpy( ret + n1, arg2 );
    }

    return ret;
}

答案 2 :(得分:0)

我忘记了 snprintf 的存在,并认为我必须使用 strcpy strcat 或其他一些字符串或内存管理函数来做到这一点

char* concatStrings(char* arg1, char*arg2){
  size_t n1 = strlen(arg1), n2 = strlen(arg2);
  char *ret = malloc(n1 + n2 + 1);
  snprintf(ret,n1 + n2 + 1, "%s%s", arg1, arg2);
  return ret;
}

答案 3 :(得分:0)

问题

  1. 没有为空字符分配空间。 1 点关闭。

  2. strncpy(ret, arg1, strlen(arg1))空字符终止ret,所以下一个strncat(ret,...未定义行为,如{{ 1}} 需要指向一个字符串

一些替代方案:

ret
相关问题