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

}

不思議なことに、この関数は機能していますが、segfaultでコアダンプが発生することがあります。

このコードの何が問題になっていますか?上記のタスクを管理するためのより良い方法はありますか?

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

編集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桁に増えると、明らかに問題が発生します。

1つの問題

コード内:

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

workbufパラメータの1つとして書き込み、渡すことにより、未定義の動作を呼び出しています。それは単に危険です。あなたがそれで逃げる可能性は中程度ですが、「逃げる」は有効な用語です—それが機能するという保証はありません。

新しい番号を不十分なスペースにフォーマットすると、上書きの問題が発生します。

作業コード

以下のコードは機能しているようです。

#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()ます。nullで終了する文字列は任意のデータにコピーでき、nullターミネータを超えない限り、問題は発生しません。変数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 に答える