Выделение памяти для рекурсивной функции strcat() в C

Я пытаюсь создать симулятор грамматики без контекста, используя совет из другого вопроса но у меня проблема с выделением достаточного количества памяти.

Основной код:

char * print_S ( )
{
    int los = zero_or_one();
    if ( los == 1 )
            return "1";
    else
            return strcat( print_A(), print_A() );
}

char * print_A ( )
{
    int los = zero_or_one();
    if ( los == 1 )
            return "0";
    else
            return strcat( print_S(), print_S() );
}

Возвращает ошибку сегментации, когда los = 0.
Затем я попробовал что-то с выделением памяти для строки и последующей передачей ее функциям, но все равно безуспешно:

char * out = (char *) malloc( 100 * sizeof(*out) );
printf("%s\n", print_S( out ) );

а также

char * print_S ( char * out )
{
    int los = zero_or_one();

    if ( los == 1 ) {
        strcpy (out, "1");
        return out;
    } else {
        return strcat( print_A(out), print_A(out) );
    }
}

char * print_A ( char * out )
{
    int los = zero_or_one();

    if ( los == 1 ) {
        strcpy (out, "0");
        return out;
    } else {
        return strcat( print_S(out), print_S(out) );
    }
}

Каков правильный подход? Спасибо


person pawel.ad    schedule 28.11.2013    source источник
comment
Если print_A примерно совпадает с print_S, то вы пытаетесь strcat использовать константную строку. Первый подход, безусловно, неправильный. То, что вы пробовали с malloc, должно работать, но вам нужно выделить достаточно памяти. Если у вас слишком много рекурсивных вызовов, у вас будет переполнение, и программа вылетит с segfault. Также есть проблема со строкой return strcat(print_A(out), print_A(out)), потому что один вызов print_A перезапишет работу другого print_A, поскольку вы передаете один и тот же адрес обоим вызовам.   -  person Cahu    schedule 28.11.2013
comment
Возможно, вы получаете ошибку сегмента, потому что условие завершения рекурсивной функции может быть неправильным. Я не понимаю, что вы пытаетесь сделать здесь...   -  person    schedule 28.11.2013
comment
@Cahu Я добавил остальную часть кода. Да, я знаю - первая версия - это черновик, который я получил из предыдущего вопроса. Но вторая версия по-прежнему не работает. И я запускаю его несколько раз, поэтому, если он был прав, часть вывода должна быть ‹ 100 символов, но каждый раз я получал ошибку сегментации.   -  person pawel.ad    schedule 28.11.2013
comment
@Nishith Jain M R То, что я пытаюсь сделать, объясняется в вопросе, указанном в начале, но у меня проблемы с реализацией этого на C.   -  person pawel.ad    schedule 28.11.2013
comment
Обратите внимание, что с такими вызовами, как strcat(print_A(), print_A()) (с аргументами или без них), неясно, вызывается ли сначала левый или правый экземпляр print_A(). Порядок слева направо (или справа налево) не гарантируется.   -  person Jonathan Leffler    schedule 28.11.2013


Ответы (1)


Учитывая контекст другого вопроса, я думаю, вам следует организовать первоначальный вызов функций, чтобы он задавал начало и конец массива, скажем, из 100 символов. По мере того, как вы работаете рекурсивно, вы увеличиваете начальный указатель, который вы передаете другим функциям соответствующим образом.

Этот код работает для меня:

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

/* One of these is sufficient; symmetry requires both */
static char *print_S(char *dst, char *end);
static char *print_A(char *dst, char *end);

static int zero_or_one(void)
{
    int rv = rand() % 2;
    return rv;
}

static char *print_S(char *dst, char *end)
{
    assert(dst <= end);
    if (dst < end)
    {
        if (zero_or_one() == 0)
            *dst++ = '0';
        else
        {
            dst = print_A(dst, end);
            dst = print_A(dst, end);
        }
    }
    *dst = '\0';
    return dst;
}

static char *print_A(char *dst, char *end)
{
    assert(dst <= end);
    if (dst < end)
    {
        if (zero_or_one() == 1)
            *dst++ = '1';
        else
        {
            dst = print_S(dst, end);
            dst = print_S(dst, end);
        }
    }
    *dst = '\0';
    return dst;
}

int main(void)
{
    srand(time(0));
    for (int i = 0; i < 25; i++)
    {
        char buffer[100];
        (void)print_A(buffer, buffer + sizeof(buffer) - 1);
        printf("%s\n", buffer);
        (void)print_S(buffer, buffer + sizeof(buffer) - 1);
        printf("%s\n", buffer);
    }
    return 0;
}

Обратите внимание, что последовательности часто очень короткие, но могут быть и довольно длинными. Один пример запуска:

01011
0
1
0
1
0
00
11
1
0
1
0
1
1110
00
101110101000000011100001110000100111011011000110001000000111010001000110000100010000111111001100011
1
0
11000011111000110011001000011100
0
1
1000000100001000
011
0
110
0
1
0
1
001
1
0110110011000
11110011110101100101100000111101011010001000110110010100011001100
10111001100101
1
100
100010
0
00
0
011
11
0000100010110
0
1
11
00
0
1
0

Некоторые прогоны примеров были усечены до 99 символов — как и длинный в этом примере. Я также пробовал с размерами 300 и 1000; в обоих случаях я получил пробные прогоны, которые были усечены.

Если вы хотите сделать динамическое выделение памяти, вы должны быть немного хитрее:

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

/* One of these is sufficient; symmetry requires both */
static char *print_S(char **buffer, size_t *len, char *dst);
static char *print_A(char **buffer, size_t *len, char *dst);

static int zero_or_one(void)
{
    int rv = rand() % 2;
    return rv;
}

static void add_space(char **buffer, size_t *len, char **dst)
{
    assert(*dst < *buffer + *len);
    if (*dst == *buffer + *len - 1)
    {
        char *newbuf = realloc(*buffer, 2 * *len);
        if (newbuf == 0)
        {
            fprintf(stderr, "Out of memory (%zu)\n", 2 * *len);
            exit(1);
        }
        *len *= 2;
        *buffer = newbuf;
        *dst = *buffer + strlen(*buffer);
    }
}

static char *print_S(char **buffer, size_t *len, char *dst)
{
    add_space(buffer, len, &dst);

    if (zero_or_one() == 0)
        *dst++ = '0';
    else
    {
        dst = print_A(buffer, len, dst);
        dst = print_A(buffer, len, dst);
    }
    *dst = '\0';
    return dst;
}

static char *print_A(char **buffer, size_t *len, char *dst)
{
    add_space(buffer, len, &dst);
    if (zero_or_one() == 1)
        *dst++ = '1';
    else
    {
        dst = print_S(buffer, len, dst);
        dst = print_S(buffer, len, dst);
    }
    *dst = '\0';
    return dst;
}

int main(void)
{
    srand(time(0));
    size_t len = 100;
    char *buffer = malloc(len);
    for (int i = 0; i < 25; i++)
    {
        (void)print_A(&buffer, &len, buffer);
        printf("%zu: %s\n", strlen(buffer), buffer);
        (void)print_S(&buffer, &len, buffer);
        printf("%zu: %s\n", strlen(buffer), buffer);
    }
    free(buffer);
    return 0;
}

При этом я получил один запуск, в котором длина выходной строки составляла 14 473 874 байта. Я не буду печатать его здесь; достаточно сказать, что в нем были 1 и 0.

person Jonathan Leffler    schedule 28.11.2013
comment
Вау, большое спасибо, добрый сэр. Мне придется больше учиться, чтобы полностью понять это, но в этом и суть :-) Я перейду к делу. Еще раз спасибо! - person pawel.ad; 28.11.2013
comment
Хорошо, у меня все заработало - нет повторяющихся строк и все напечатано по порядку. Спасибо! - person pawel.ad; 29.11.2013