LINUX.ORG.RU

Ошибка в коде...


0

0

как всегда, есть код (приведен ниже).
при запуске программы вылетает:
*** glibc detected *** malloc(): memory corruption (fast): 0x0804f070 ***
Aborted

что должна делает программа: в функцию char ** get_elements() передается строка "3, 4, 5, 6". эта функция должна создать char **, раскидав в каждый char * строки: "3", " 4", " 5", " 6".
т.е.

char ** rrr = NULL;
rrr = get_elements("3, 4, 5, 6");

после должно быть так:

rrr[0] = "3";
rrr[1] = " 4";
rrr[2] = " 5";
rrr[3] = " 6";
rrr[4] = NULL;

вся суть в функции get_elements() - вот только где там ошибка я так и не понял.
подскажите, плиз.
ЗЫ: функции add_char_to_string() и safe_free() никогда не подводили.

void safe_free(char ** str)
{
        if (!(*str))
                return;

        free(*str);
        *str = NULL;
};

char * add_char_to_string(char * str, char byte)
{
        char * result = NULL;
        unsigned int len = 0;

        if (!str)
                len = 0;
        else
                len = strlen(str);

        result = (char*) calloc(len+2, sizeof(char));

        if (!result)
                return NULL;

        if (len != 0)
                strcat(result,str);

        result[len] = byte;
        result[len+1] = '\0';

        if (str)
                safe_free(&str);

        return result;
};

char ** get_elements(char * params)
{
        char ** result = NULL;
        unsigned int len = 0, i = 0, k = 0;
        char * temp = NULL;

        if (!params)
                return NULL;

        if (strlen(params) == 0)
                return NULL;

        len = strlen(params);

        while (1) {

                for(; i < len; i++) {

                        if (params[i] == ',')
                                break;

                        temp = add_char_to_string(temp, params[i]);
                }
                k++;
                i++;

                if (!result)
                        result = (char **) calloc(k + 1, sizeof(char *));
                else
                        result = (char**) realloc(result, k + 1);

                result[k] = 0;
                result[k-1] = temp;

                if (i >= len) {
                        break;
                }

                temp = NULL;
        }

        return result;
}

int main(int argc, char *argv[])
{
        char ** rrr = NULL;

        rrr = get_elements("3, 4, 5, 6");

        if (!rrr) {
                printf("Wrong\n");
        }
        else {
                printf("Not bad\n");
        }

        return EXIT_SUCCESS;
};

я изменил блок:

               if (!result)
                        result = (char **) calloc(k + 1, sizeof(char *));
                else
                        result = (char**) realloc(result, k + 1);

на:

                if (!result)
                        result = (char **) calloc(k + 1, sizeof(char *));
                else {
                        result_tmp = (char **) calloc(k + 1, sizeof(char *));

                        for (c = 0; c < k; c++)
                                result_tmp[c] = result[c];

                        result = result_tmp;
                }

result_tmp имеет тип char **.
т.е., верно ли то, что realloc() неверно делал свое дело ?
в мане к нему нет подсказок насчет этого ?

nial
() автор топика
Ответ на: комментарий от nial

void *realloc(void *ptr, size_t size);
---
result = (char**) realloc(result, k + 1);
                                  ^^^^^
дальше объяснять? :)
И вообще, разве gdb уже отменили

phoenix ★★★★
()
Ответ на: комментарий от nial

> на:

Теперь у тебя утечка памяти.

> верно ли то, что realloc() неверно делал свое дело ?

Да, но поправить это можно было намного проще:

- result = (char**) realloc(result, k + 1); + result = (char**) realloc(result, (k + 1)*sizeof(char*));

Вообще, если посмотреть, то calloc у тебя везде лишний, в том смысле, что выполняет лишнюю работу по инициализации памяти нулями. Используй malloc. В add_char_to_string замени strcat на strcpy.

Хотя, один фиг, это слабо все поможет :) Дерьмовый код, надо сказать.

watashiwa_daredeska ★★★★
()
Ответ на: комментарий от watashiwa_daredeska

gdb - да нет, не отменяли - активно им пользуюсь.

> Дерьмовый код, надо сказать
стесняюсь спросить, он почему он такой ?

nial
() автор топика
Ответ на: комментарий от nial

> он почему он такой ?

Эффективность ниже плинтуса. Очень много выделений памяти. Если эффективность не нужна, то зачем тогда C и весь этот геморрой? На Python это пишется в 1 строку:

rrr = "3, 4, 5, 6".split( "," )

watashiwa_daredeska ★★★★
()
Ответ на: комментарий от nial

а потому что если его переписать с помощью strtok и str(n)cpy, то он будет в 10 раз короче и понятнее :) А если еще посчитать количество "," заранее, то и от realloc можно избавиться.

phoenix ★★★★
()
Ответ на: комментарий от watashiwa_daredeska

> Очень много выделений памяти
я за этим очень строго слежу, и ради теста пропустил этот код.

хотя, спорить не буду, я не програмист.

nial
() автор топика
Ответ на: комментарий от nial

> я за этим очень строго слежу

За чем, за этим? За количеством выделений? А чего за ним следить, его надо уменьшать, выделение памяти - дорогая операция.

> я не програмист

Тем более, нафига C?

watashiwa_daredeska ★★★★
()
Ответ на: комментарий от watashiwa_daredeska

Раз не программист, вот тебе пример:

char** get_elements( char* params )
{
    int n=1;
    char *it, *beg=NULL;
    char **result;

    /* Check argument */
    if( params==NULL || *params==0 )
        return NULL;

    /* Counting the number of commas in the string. */
    for( it=params; *it != 0; ++it )
        if( *it == ',' )
            ++n;

    /* Allocate result. */
    result = (char**)malloc( (n+1) * sizeof( char* ) );
    result[n] = NULL;

    /* Fill result */
    for( it=params, n=0; *it != 0; ++it )
    {
        if( beg == NULL )
            beg = it;
        
        if( *it == ',' )
        {
            result[n++] = strncpy( (char*)malloc( (it-beg+1)*sizeof(char) ), beg, it-beg );
            beg = NULL;
        }
    }
    if( beg == NULL )
        beg = it;
    result[n++] = strncpy( (char*)malloc( (it-beg+1)*sizeof(char) ), beg, it-beg );

    return result;
}

watashiwa_daredeska ★★★★
()
Ответ на: комментарий от watashiwa_daredeska

нет, слежу за освобождением памяти.
действительно, я не думаю об времени выполнения этой операции.

нафига С ? - пишу в основном на нем ).
прога маленькая - не хотел использовать интерпритаторы.

nial
() автор топика
Ответ на: комментарий от nial

Странно, обычно маленькие программы, где время выполнения некритично, как раз и пишутся на интерпретируемых языках - в разы короче и понятней.

watashiwa_daredeska ★★★★
()
Ответ на: комментарий от watashiwa_daredeska

> it-beg
красиво, никогда бы не додумался так сделать ))

...ты прав, перепишу на php (python не знаю).

nial
() автор топика
Ответ на: комментарий от nial

// вот еще один вариант
char ** get_params(char * str) {
    char * new_str = strdup(str);
    char * p = 0;
    unsigned i = 0;

    unsigned len = 2;

    for (p=new_str; *p; p++) {
        if (*p == ',')  len++;
    }

    char ** dst = (char **)malloc(len*sizeof(char *));
    dst[len] = 0;
    dst[0] = new_str;

    for (p=new_str; *p; p++) {
        if (*p == ',') {
            *p = 0;
            dst[++i] = p+1;
        }
    }

    return dst;
}

zhuk
()
Ответ на: комментарий от zhuk

> вот еще один вариант

С этим вариантом неудобно освобождать память. Вот если засунешь массив указателей и сами строки в один блок памяти, то это будет лучше.

watashiwa_daredeska ★★★★
()
Ответ на: комментарий от watashiwa_daredeska

Может быть, но проще создать функцию что-то типа params_free(char **), и в мануале написать что для освобождения памяти надо использовать ее.

zhuk
()
Ответ на: комментарий от watashiwa_daredeska

>Вот если засунешь массив указателей и сами строки в один блок памяти, то это будет лучше.

Круто! Вот полный код:

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

char ** get_params(char * str) {
    char * new_str = 0;
    char * p = 0;
    unsigned i = 0;

    unsigned str_len = strlen(str)+1;
    unsigned len = 2;
    
    for (p=str; *p; p++) {
        if (*p == ',')  len++;
    }

    char ** dst = (char **)malloc( len*sizeof(char *) + str_len*sizeof(char) );
    dst[len] = 0;
    
    new_str = (char *)dst + len*sizeof(char *) + 1;
    strncpy(new_str, str, str_len-1);
    
    dst[0] = new_str;

    for (p=new_str; *p; p++) {
        if (*p == ',') {
            *p = 0;
            dst[++i] = p+1;
        }
    }

    return dst;
}

void free_params(char **params) {
    free(params);
}

int main() {
    char ** params = get_params("1a,2b,3c,4d,5f");

    int i = 0;
    for (;params[i];i++) {
	printf("%d:\t%s\n", i, params[i]);
    }
    
    free_params(params);
}

anonymous
()
Ответ на: комментарий от zhuk

> роще создать функцию что-то типа params_free(char **), и в мануале написать что для освобождения памяти надо использовать ее.

Тогда надо дополнительно еще что-то изобретать. Ибо, кто мне помешает переколбасить порядок элементов в массиве (отсортировать, например)? В общем, по любому, геморрой.

watashiwa_daredeska ★★★★
()
Ответ на: комментарий от watashiwa_daredeska

Любой дополнительный сервис понижает быстродействие :)

Если надо переколбасить строку в 10 метров, то это самое то.

Ну а по большому счету при современных мощностях овчинка выделки не стоит. Хотя не зря ведь JavaVM откусывает 30-40 метров памяти, после чего сама себе там что-то творит.

zhuk
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.