Как динамически перераспределить таблицу строк неизвестных и их количества и их размеров, в C? - PullRequest
0 голосов
/ 10 ноября 2011

ОБНОВЛЕНИЕ 2 : Дэйв добавил проверку ошибок в своем предложении, я принял ее и добавил немного расширенную функциональность, в отдельном ответе.

ОБНОВЛЕНИЕ 1 : Проблема решена (это был не realloc (), это был факт, что я не выделял дополнительный слот таблицы для завершения NULL ... Я исправил это).См. Также предложение Дейва в 1-м ответе

Исходное сообщение :

Возможно ли динамически перераспределить совершенно неизвестную таблицу строк в c, не используя промежуточныйструктуры данных?

То, что я хочу сделать, это создать функцию sarr_make_tokens () для токенизации произвольной строки c и возврата таблицы (завершенных NULL) токенов строки c.

ДляНапример, с s [] = "Привет жестокий мир!"и вызов: char ** tokens = s_make_tokens (s, "\ t!");Я хотел бы получить таблицу токенов, заканчивающуюся на NULL ...

tokens[0] : "hello"
tokens[1] : "cruel"
tokens[2] : "world"
tokens[3] : NULL

Часть кода, приведенная ниже, не работает (в строке realloc (), я думаю), но я не могу вспомнить размер, который должен передатьчтобы сохранить все уже сохраненные токены.

Возможно ли это вообще, или мне нужно сначала получить количество токенов с помощью цикла strok () на локальной копии s, затем mallocсколько слотов в таблице токенов, а затем применить другой цикл strtok (), чтобы сохранить фактические токены в таблице?

Я мог бы использовать промежуточный связанный список для хранения токенов, прежде чем копировать их в токены.таблица, но если есть способ использовать realloc с правильным размером, было бы намного лучше!

Буду признателен за любую помощь!Вот проблемный код ... на самом деле он работает, но ошибки при попытке освободить () полученную таблицу токенов в функции вызывающей стороны.

#define S_FREE(p) \
    do \
        if ( (p) ) { \
            free( (p) ); \
                (p) = NULL; \
        } \
while (0)

/* --------------------------------------------- */
int sarr_free( char *sarr[] )
{
    register char **cpp = sarr;

    /* sanity check */
    if ( !sarr ) { errno = EFAULT; return 0; }

    while ( *cpp )
        free( *cpp++ );
    free( sarr );

    return 1;           /* TRUE                                   */
}


/* --------------------------------------------- */
char **sarr_make_tokens( char *s, const char *delims )
{
    char **tokens = NULL, **ppchar = NULL;
    size_t toksize = 0;
    register char *cp = NULL;
    register int i=0, j=0;

    /* sanity checks */
    if ( !s || !delims ) { errno = EFAULT; return NULL; }
    if ( !*s || !*delims ) { errno = EINVAL; return NULL; }

    i = 0;
    cp = strtok( s, delims );
    while ( cp != NULL )
    {
            /* add a new slot in the array */
            ppchar = realloc( tokens, (i+1) * sizeof(char *) );
            if ( !ppchar ) {
                    for (j=i-1; j > -1; j--)
                            free( tokens[j] );
                    S_FREE( tokens );
                    errno = ENOMEM;
                    return NULL;
            }
            tokens = ppchar;

            /* make room for the token & copy it into the slot */
            toksize = strlen( cp ) + 1;
            tokens[i] = calloc( toksize, sizeof(char) );
            if ( !tokens[i] ) {
                    for (j=i-1; j > -1; j--)
                            free( tokens[j] );
                    free( tokens );
                    errno = ENOMEM;
                    return NULL;
            }
            memcpy( tokens[i], cp, toksize );

            /* get next token */
            cp = strtok( NULL, delims );

            i++;
    }

    if ( i != 0 ) {         /* while-loop run at least once             */
            ppchar = realloc( tokens, (i+1) * sizeof(char *) );
            if ( !ppchar )
                /* handle error here */
            tokens = ppchar;
            tokens[ i ] = NULL; /* ... NULL terminate the array of tokens   */
    }

    else                /* while-loop did not run at all            */
            errno = ERANGE;     /* ... flag failure of 1st strtok()         */

    return tokens;

}

Ответы [ 3 ]

1 голос
/ 10 ноября 2011

Это довольно просто ... Используя strtok(), strdup() и realloc(), функция довольно проста.

//EDIT: Now handles errors.
char **tok(char *s, char *delim)
{
    char *str, **arr, **ap;
    int cap=3, fill=0;
    if((str=strdup(s))==NULL) //in case s is read-only.
         goto NoMem;
    if((arr = malloc(cap*sizeof(char*)))==NULL)
         goto NoMem;
    for(s=strtok(str, delim); s; s=strtok(NULL, delim)){
        if(cap<=fill+1)
            if(ap = realloc(arr, (cap=(cap*3)/2)*sizeof(char*)))
                 arr=ap;
            else 
                 goto NoMem;
        if((arr[fill++] = strdup(s))==NULL)
            goto NoMem;
        arr[fill] = NULL;
    }
    free(str);
    return arr;
NoMem:
    if(str) free(str); 
    if(arr){
        for(ap=arr; *ap; ap++)
            free(*ap);
        free(arr);
    }
    return NULL;
}

Изменяя размер массива на 150% каждый раз, когда он собираетсяпереполнение, требуется только log(n) изменение размеров, а эффективность использования пространства по-прежнему лучше, чем у любого связанного списка.

0 голосов
/ 11 ноября 2011

Принятие предложения Дейва и немного расширение функциональности исходной функции, позволяющее программисту решать, следует ли считать s доступными только для чтения (медленнее) или нет (быстрее) с помощью параметра extar, I ' Мы придумали следующее ... это немного избыточно в проверке ошибок, что вроде как противоречит «более быстрому» утверждению, но хорошо, я еще не много думал об этом ... но, надеюсь, он не содержит ошибок.

#define S_FREE(p)            \
    do                       \
        if ( (p) ) {         \
            free( (p) );     \
            (p) = NULL;      \
        }                    \
    while (0)

#define SARR_BACKFREE(sarr, ifailed)        \
    do {                                    \
        register int i=0;                   \
        for (i=(ifailed)-1; i > -1; i--)    \
            S_FREE( (sarr)[i] );            \
        S_FREE( (sarr) );                   \
    } while(0)

/* -------------------------------------- */
char *s_strdup( const char *src )
{
    char *s = NULL;
    size_t ssize = 0;

    /* sanity check */
    if ( !src ) { errno = EFAULT; return NULL; }

    ssize = strlen( src ) + 1;
    if ( !(s = malloc(ssize)) ) {
        errno = ENOMEM;
        return NULL;
    }

    return memcpy( s, src, ssize );
}

/* -------------------------------------- */
char **sarr_make_tokens( char *s, char *delims, const int readonly )
{
    char *str = NULL, *cp = NULL;
    int cap = 3, itok = 0;
    char **sarr = NULL, **ppchar = NULL;

    /* sanity checks */
    if ( !s || !delims ) { errno = EFAULT; return NULL; }
    if ( !*s || !*delims ) { errno = EINVAL; return NULL; }

    /* treat s as a string literal? */
    if ( readonly ) {
        if ( (str = s_strdup( s )) == NULL )
            goto ret_nomem;
    }
    else    str = s;

    /* allocate table of strings sarr */
    if ( (sarr = malloc( cap * sizeof(char *) )) == NULL )
        goto ret_nomem;

    /* tokenize str and store tokens in sarr */
    for ( cp=strtok(str, delims); cp; cp=strtok(NULL, delims) )
    {
        if( cap <= itok+1 )
        {
            ppchar = realloc(sarr, (cap=(cap*3)/2) * sizeof(char *) );
            if ( !ppchar )
                goto clean_and_ret_nomem;
            sarr = ppchar;
        }

        if ( (sarr[ itok ] = s_strdup( cp )) == NULL )
            goto clean_and_ret_nomem;

        itok++;
    }

    /* NULL terminate sarr */
    sarr[ itok ] = NULL;

    if ( readonly )
        S_FREE( str );
    return sarr;

clean_and_ret_nomem:
    SARR_BACKFREE(sarr, itok);
ret_nomem:
    if ( readonly )
        S_FREE( str );          /* S_FREE() works ONLY if str != NULL */
    errno = ENOMEM;
    return NULL;
}
0 голосов
/ 11 ноября 2011

В конце концов, проблема была не в realloc (), а в том, что я НЕ выделял дополнительный слот таблицы для завершения NULL перед выходом из функции (я исправил ее и стер соответствующий текст в исходном сообщении) .

Более элегантное (но не переносимое) решение было также предложено Дейвом: Как динамически перераспределить таблицу строк неизвестных как по их количеству, так и по размерам, в C?

Спасибо всем за отзыв!

...