在C中重复修改字符串

时间:2013-03-21 06:46:08

标签: c segmentation-fault

我想修改一个这样的字符串: -

MainString="",            ToUpdate="ABC" -> return# "ABC=1"
MainString="ABC=1",       ToUpdate="ABC" -> return# "ABC=2"
MainString="ABC=2",       ToUpdate="ABC" -> return# "ABC=3"
MainString="ABC=3",       ToUpdate="XYZ" -> return# "ABC=3:XYZ=1"
MainString="ABC=3:XYZ=1", ToUpdate="XYZ" -> return# "ABC=3:XYZ=2"
MainString="ABC=3:XYZ=2", ToUpdate="XYZ" -> return# "ABC=3:XYZ=3"

我有以下功能:

void UpdateString(char *MainString, char ToUpdate[20])
{
    char *pData[50][2];
    char *saveptr1=NULL;
    int i=0,j=0,nIsPresentFlag=0;
    unsigned int CdrCnt=1;
    char workbuf1[200];
    char workbuf[200];


    memset(workbuf,0,200);
    memset(workbuf1,0,200);

    if(strlen(MainString)>0)
    {
        strcat(MainString,":");
    }

    strcpy(workbuf1,MainString);

    pData[i][0]=strtok_r(workbuf1,"=",&saveptr1);
    pData[i][1]=strtok_r(NULL,":",&saveptr1);

    if(pData[i][0]) {i++;pData[i][0]=NULL; pData[i][1]=NULL;}

    while((pData[i][0]=strtok_r(NULL,"=",&saveptr1)))
    {
        pData[i][1]=strtok_r(NULL,":",&saveptr1);
        i++;
    }

    for(j=0;j<i;j++)
        if(strncmp(ToUpdate,pData[j][0],strlen(pData[j][0]))==0)
        {
            CdrCnt=atoi(pData[j][1]);
            CdrCnt+=1;
            sprintf(pData[j][1],"%d",CdrCnt);
            nIsPresentFlag=1;
            break;
        }

    if(nIsPresentFlag==1)
        for(j=0;j<i;j++)
            sprintf(workbuf,"%s%s=%s:",workbuf,pData[j][0],pData[j][1]);
    else
        sprintf(workbuf,"%s%s=%d:",MainString,ToUpdate,1);

    workbuf[strlen(workbuf)-1]='\0';


    memset(MainString,0,200);
    strcpy(MainString,workbuf);

}

奇怪的是,这个功能正在运行,但有时会导致带有segfault的核心转储。

这段代码有什么问题?我能管理上述任务的更好方法吗?

=============================================== ===============================

编辑1

字符串声明:

char MainString[200];

电话就像:

UpdateString((char*)&MainString,"ABC");

1 个答案:

答案 0 :(得分:5)

初步观察

鉴于此测试代码:

static void chkit(char *s, char *u)
{
    printf("[%s] && [%s]", s, u);
    UpdateString(s, u);
    printf(" ==> [%s]\n", s);
}

int main(void)
{
    char MainString[200] = "";

    chkit(MainString, "ABC");
    chkit(MainString, "ABC");
    chkit(MainString, "ABC");
    chkit(MainString, "XYZ");
    chkit(MainString, "XYZ");
    chkit(MainString, "XYZ");
    chkit(MainString, "DEF");
    chkit(MainString, "ABC");
    chkit(MainString, "ABC");
    chkit(MainString, "ABC");
    chkit(MainString, "ABC");
    chkit(MainString, "ABC");
    chkit(MainString, "ABC");
    chkit(MainString, "ABC");
    chkit(MainString, "XYZ");
    chkit(MainString, "ABC");
    chkit(MainString, "GHI");
    return 0;
}

我得到的输出是:

[] && [ABC] ==> [ABC=1]
[ABC=1] && [ABC] ==> [ABC=2]
[ABC=2] && [ABC] ==> [ABC=3]
[ABC=3] && [XYZ] ==> [ABC=3:XYZ=1]
[ABC=3:XYZ=1] && [XYZ] ==> [ABC=3:XYZ=2]
[ABC=3:XYZ=2] && [XYZ] ==> [ABC=3:XYZ=3]
[ABC=3:XYZ=3] && [DEF] ==> [ABC=3:XYZ=3:DEF=1]
[ABC=3:XYZ=3:DEF=1] && [ABC] ==> [ABC=4:XYZ=3:DEF=1]
[ABC=4:XYZ=3:DEF=1] && [ABC] ==> [ABC=5:XYZ=3:DEF=1]
[ABC=5:XYZ=3:DEF=1] && [ABC] ==> [ABC=6:XYZ=3:DEF=1]
[ABC=6:XYZ=3:DEF=1] && [ABC] ==> [ABC=7:XYZ=3:DEF=1]
[ABC=7:XYZ=3:DEF=1] && [ABC] ==> [ABC=8:XYZ=3:DEF=1]
[ABC=8:XYZ=3:DEF=1] && [ABC] ==> [ABC=9:XYZ=3:DEF=1]
[ABC=9:XYZ=3:DEF=1] && [ABC] ==> [ABC=10:=3:DEF=1]
[ABC=10:=3:DEF=1] && [XYZ] ==> [ABC=10:=3:DEF=1:XYZ=1]
[ABC=10:=3:DEF=1:XYZ=1] && [ABC] ==> [ABC=11:3:DEF=1:XYZ=1]
[ABC=11:3:DEF=1:XYZ=1] && [GHI] ==> [ABC=11:3:DEF=1:XYZ=1:GHI=1]

当数字从1位增加到2位时,显然存在问题。

一个问题

在代码中:

if (nIsPresentFlag == 1)
    for (j = 0; j < i; j++)
        sprintf(workbuf, "%s%s=%s:", workbuf, pData[j][0], pData[j][1]);

通过写入workbuf并将其作为参数之一传递来调用未定义的行为。那简直太危险了。你有机会侥幸逃脱它,但是要离开&#39;是一个有效的术语 - 不保证它会起作用。

将新数字格式化为空间不足时会出现覆盖问题。

工作代码

以下代码似乎有效:

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

static void UpdateString(char *MainString, char ToUpdate[20])
{
    char *pData[50][2];
    char *saveptr1 = NULL;
    int   i = 0;
    int   nIsPresentFlag = 0;
    char  workbuf1[200];
    char  workbuf[200];
    char  extra[16];

    if (strlen(MainString) > 0)
        strcat(MainString, ":");

    strcpy(workbuf1, MainString);

    pData[i][0] = strtok_r(workbuf1, "=", &saveptr1);
    pData[i][1] = strtok_r(NULL, ":", &saveptr1);

    if (pData[i][0])
        i++;

    while ((pData[i][0] = strtok_r(NULL, "=", &saveptr1)) != 0)
    {
        pData[i][1] = strtok_r(NULL, ":", &saveptr1);
        i++;
    }

    for (int j = 0; j < i; j++)
    {
        if (strncmp(ToUpdate, pData[j][0], strlen(pData[j][0])) == 0)
        {
            unsigned int CdrCnt = atoi(pData[j][1]);
            CdrCnt += 1;
            pData[j][1] = extra;
            sprintf(pData[j][1], "%u", CdrCnt);
            nIsPresentFlag = 1;
            break;
        }
    }

    if (nIsPresentFlag == 1)
    {
        char *dst = workbuf;
        for (int j = 0; j < i; j++)
        {
            int n = sprintf(dst, "%s=%s:", pData[j][0], pData[j][1]);
            /* Broken if sprintf() returns -1 */
            dst += n;
        }
    }
    else
        sprintf(workbuf, "%s%s=%d:", MainString, ToUpdate, 1);

    workbuf[strlen(workbuf)-1] = '\0';

    strcpy(MainString, workbuf);
}

static void chkit(char *s, char *u)
{
    printf("[%s] && [%s]", s, u);
    UpdateString(s, u);
    printf(" ==> [%s]\n", s);
}

int main(void)
{
    char MainString[200] = "";

    chkit(MainString, "ABC");
    chkit(MainString, "ABC");
    chkit(MainString, "ABC");
    chkit(MainString, "XYZ");
    chkit(MainString, "XYZ");
    chkit(MainString, "XYZ");
    chkit(MainString, "DEF");
    chkit(MainString, "ABC");
    chkit(MainString, "ABC");
    chkit(MainString, "ABC");
    chkit(MainString, "ABC");
    chkit(MainString, "ABC");
    chkit(MainString, "ABC");
    chkit(MainString, "ABC");
    chkit(MainString, "XYZ");
    chkit(MainString, "ABC");
    chkit(MainString, "GHI");
    chkit(MainString, "PQRSTU");
    chkit(MainString, "I");
    chkit(MainString, "I");
    chkit(MainString, "I");
    chkit(MainString, "PQRSTU");

    return 0;
}

它省略了memset()次操作;一个空终止的字符串可以复制到任意数据上,只要你不要超越空终止符,你就不会有任何问题。变量extra用于存储新数字;当数字从N变为N + 1位数时,它避免了问题。函数sprintf()返回它写的字符数;用于安全地将数据添加到工作缓冲区。

示例输出

[] && [ABC] ==> [ABC=1]
[ABC=1] && [ABC] ==> [ABC=2]
[ABC=2] && [ABC] ==> [ABC=3]
[ABC=3] && [XYZ] ==> [ABC=3:XYZ=1]
[ABC=3:XYZ=1] && [XYZ] ==> [ABC=3:XYZ=2]
[ABC=3:XYZ=2] && [XYZ] ==> [ABC=3:XYZ=3]
[ABC=3:XYZ=3] && [DEF] ==> [ABC=3:XYZ=3:DEF=1]
[ABC=3:XYZ=3:DEF=1] && [ABC] ==> [ABC=4:XYZ=3:DEF=1]
[ABC=4:XYZ=3:DEF=1] && [ABC] ==> [ABC=5:XYZ=3:DEF=1]
[ABC=5:XYZ=3:DEF=1] && [ABC] ==> [ABC=6:XYZ=3:DEF=1]
[ABC=6:XYZ=3:DEF=1] && [ABC] ==> [ABC=7:XYZ=3:DEF=1]
[ABC=7:XYZ=3:DEF=1] && [ABC] ==> [ABC=8:XYZ=3:DEF=1]
[ABC=8:XYZ=3:DEF=1] && [ABC] ==> [ABC=9:XYZ=3:DEF=1]
[ABC=9:XYZ=3:DEF=1] && [ABC] ==> [ABC=10:XYZ=3:DEF=1]
[ABC=10:XYZ=3:DEF=1] && [XYZ] ==> [ABC=10:XYZ=4:DEF=1]
[ABC=10:XYZ=4:DEF=1] && [ABC] ==> [ABC=11:XYZ=4:DEF=1]
[ABC=11:XYZ=4:DEF=1] && [GHI] ==> [ABC=11:XYZ=4:DEF=1:GHI=1]
[ABC=11:XYZ=4:DEF=1:GHI=1] && [PQRSTU] ==> [ABC=11:XYZ=4:DEF=1:GHI=1:PQRSTU=1]
[ABC=11:XYZ=4:DEF=1:GHI=1:PQRSTU=1] && [I] ==> [ABC=11:XYZ=4:DEF=1:GHI=1:PQRSTU=1:I=1]
[ABC=11:XYZ=4:DEF=1:GHI=1:PQRSTU=1:I=1] && [I] ==> [ABC=11:XYZ=4:DEF=1:GHI=1:PQRSTU=1:I=2]
[ABC=11:XYZ=4:DEF=1:GHI=1:PQRSTU=1:I=2] && [I] ==> [ABC=11:XYZ=4:DEF=1:GHI=1:PQRSTU=1:I=3]
[ABC=11:XYZ=4:DEF=1:GHI=1:PQRSTU=1:I=3] && [PQRSTU] ==> [ABC=11:XYZ=4:DEF=1:GHI=1:PQRSTU=2:I=3]

我使用valgrind进行了一些基本检查(并添加了一些动态内存分配),它提出了一个干净的健康状况。

注意诊断打印的样式。它显示输入和输出,这是有帮助的。它用一个独特的标记(这里是[])包围字符串,这样就可以更容易地发现杂散空间等。