1

我想修改一个这样的字符串:—</p>

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);

}

奇怪的是,这个函数正在工作,但有时会导致带有段错误的核心转储。

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

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

编辑 1

字符串声明:

char MainString[200];

调用是这样的:

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

1 回答 1

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并将其作为参数之一传递来调用未定义的行为。那简直是危险的。你有一定的几率侥幸逃脱,但“侥幸逃脱”是一个有效的术语——不能保证它会奏效。

当您将新号码格式化为空间不足时,会出现覆盖问题。

工作代码

下面的代码似乎工作:

#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(并添加了一些动态内存分配),它提出了一个干净的健康状况。

注意诊断打印的样式。它显示了输入和输出,这很有帮助。并且它将字符串包含在一个独特的标记中(此处[]),以便可以更容易地发现杂散空间等。

于 2013-03-21T07:51:13.637 回答