【问题标题】:Dynamic string concatenation with strcat in CC中使用strcat的动态字符串连接
【发布时间】:2020-01-07 10:14:55
【问题描述】:

我在使用 strcat 时遇到了一个问题,但是使用 realloc,strcat 会覆盖目标字符串

char *splitStr(char *line) {
        char *str_;
        str_ = (char *) malloc(1);
        char *ptr = strtok(line,"\n");
        int a;
        while (ptr != NULL) {
              if (ptr[0] != '$') {
                        printf("oncesi %s\n", str_);
                        a = strlen(ptr) + strlen(str_) + 1;
                        str_ = realloc(str_, a);
                        strcat(str_, ptr);
                        str_[a] = '\0';
                        printf("sontasi:%s\n", str_);
              }
              ptr = strtok(NULL, "\n");
        }
        printf("splitStr %d\n", strlen(str_));
        printf("%s", str_);
        return str_;
}

我的输入值为 ;

*4
$3
200
$4
4814
$7
SUCCESS
$4
3204

所以我想通过 strtok 拆分这个输入值; strtok(line,'\n');

并将所有没有开始“$”字符的行连接到新字符。但是,此代码给出以下输出;

line: *4
oncesi 
sontasi:*4
oncesi *4
200tasi:*4
200esi *4
4814asi:*4
4814si *4
SUCCESS:*4
SUCCESS*4
3204ESS:*4
splitStr 25

似乎覆盖了源字符串。 你知道为什么会发生这个问题吗?

【问题讨论】:

  • 看起来你的文件中有一些\r
  • 未定义的行为...str_ 在它指向的内存未初始化时使用
  • 未定义的行为...str_[a]='\0'; 在分配的内存之外写入
  • str_[a] = '\0'; 不正确,因为它超出了分配的内存。它应该是str_[a-1] = '\0';(但这是不必要的,因为strcat 处理它)。当程序流第一次进入ifblock 时,字符串str_ 应该指向未初始化并调用未定义的行为。它必须像 str_[0] = '\0'; 一样在 malloc 之后初始化。

标签: c string-concatenation strcat


【解决方案1】:

以下建议的代码:

  1. 干净编译
  2. 执行指定的功能
  3. 为提高输出的可读性而略微重新设计
  4. 检查来自malloc()realloc() 的错误
  5. 展示了如何初始化 str[] 数组,这是 OPs 发布的代码中的问题。
  6. 函数:strlen() 返回 size_t,而不是 int。所以正确的输出格式转换说明符是:%zu
  7. 变量名不使用尾随下划线

现在,建议的代码:

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


char *splitStr( char *line ) 
{
    printf("original line: %s\n", line);

    char *str = malloc(1);
    if( !str )
    {
        perror( "malloc failed" );
        exit( EXIT_FAILURE );
    }

    str[0] = '\0';   // critical statement
    char *token = strtok(line,"\n");


    while( token ) 
    {
        if( token[0] != '$') 
        {
            char* temp = realloc( str, strlen( token ) + strlen( str ) + 1 );
            if( ! temp )
            {
                perror( "realloc failed" );
                free( str );
                exit( EXIT_FAILURE );
            }

            str = temp;   // update pointer

            strcat(str, token);
            printf( "concat result: %s\n", str );
        }
        token = strtok(NULL, "\n");
    }

    printf("splitStr %zu\n", strlen(str));
    return str;
}


int main( void )
{
    char  firstStr[] = "$abcd\n$defg\nhijk\n";
    char *firstNewStr = splitStr( firstStr );
    printf( "returned: %s\n\n\n\n", firstNewStr );
    free( firstNewStr );

    char  secondStr[] = "abcd\ndefg\nhijk\n";
    char *secondNewStr = splitStr( secondStr );
    printf( "returned: %s\n\n\n\n", secondNewStr );
    free( secondNewStr );
}

运行建议的代码会导致:

original line: $abcd
$defg
hijk

concat result: hijk
splitStr 4
returned: hijk



original line: abcd
defg
hijk

concat result: abcd
concat result: abcddefg
concat result: abcddefghijk
splitStr 12
returned: abcddefghijk

【讨论】:

  • 这确实回答 OP 的问题。但是,由于更好的代码,没有否决票。 (甚至你的间距也不一致。)
  • @thebusybee,此声明:str[0] = '\0'; // critical statement 在 OP 发布的代码中缺失。我想我在发表评论时已经说清楚了:// critical statement 另外,由于splitStr() 函数的 OPs 版本中的if( token[0] != '$') 语句,main() 使用两个略有不同的字符串调用splitStr(),以便突出显示该语句的两个条件
【解决方案2】:

您的输入包含 Windows/DOS 行尾编码 "\r\n"

由于strtok() 只是将'\n' 替换为'\0',因此'\r' 保留在字符串中。在输出时,它会将光标向左移动,并且其他字符会覆盖旧字符,至少在视觉上是这样。

但是,您的连接字符串应该没问题。计算字符数,不要忘记为每一行添加一个 '\r'"*4\r200\r4814\rSUCCESS\r3204\r" 是 25 个字符,如输出 splitStr 25 所示。


补充说明:

  • 正如其他人已经说过的,str_ = (char *) malloc(1); 确实初始化str_ 指向的空间。您需要自己执行此操作,例如 str_[0] = '\0';
  • 不要这样使用下划线。
  • 您不需要转换malloc() 的结果,它是一个与char*(和任何其他)兼容的void*

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-08-19
    • 1970-01-01
    • 1970-01-01
    • 2014-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多