struct * и char * ошибка сегментации на malloc

Вот код, с которым у меня возникла проблема:

Структура:

struct AtoB
{
    char * strA;
    char * strB;
};

Функция для создания структуры. Вызывается из внешнего файла.

AtoB * atob_create(char * a)
{
    struct AtoB * atob = (struct AtoB *)malloc(sizeof(struct AtoB));
    atob->strA = malloc(sizeof((char *)a));
    strcpy(atob->strA, a);

    atob->strB = NULL;
    /* HERE IS PROBLEM NO 1 - SEGMENTATION ERROR OCCURS */
    atob->strB = (char*)malloc(1);
    strB = "\0";

    for(int i = 0; i < (int)strlen(atob->strA); i++)
    {
        char token = atob->strA[i];
        /*
        : 
            append(AtoB * atob, const char a) MAY be called, so atob->strB &
            atob->StrA will not be the same length 
        */
        append(atob, (char)token);
    }
}

Функция для добавления char к char * в пределах struct. Вызывается рекурсивно.

void append(AtoB * atob, const char a)
{
    size_t sz = strlen(atob->strB);
    /* HERE IS ANOTHER ISSUE:
       1: is this the right way to increase the size of the char * ?
       2: does the struct also need to be realloc here to accommodate? 
    */
    atob->strB = (char*)realloc(atob->strB, sz + 1);
    atob->strB[sz - 1 = a];
    atob->[sz] = "\0";
}

Функция для получения окончательной строки. Вызывается из внешнего файла.

char * (AtoB * atob)
{
    return (atob->strB);
}

Функция освобождения всей выделенной памяти. Вызывается из внешнего файла.

void free(AtoB * atob)
{
    free(atob->strA);
    free(atob->strB);
    free(atob);
}

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

У меня нет проблем с созданием структуры или размещением первого char * strA. Проблема возникает, когда я пытаюсь выделить память (даже один символ) для char * strB.

Я получаю ошибку сегментации.

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

Даже если я попытаюсь realloc создать структуру, я не смогу инициализировать strB чем-то большим, чем NULL. Мне нужно иметь возможность добавлять к atob->strB во время выполнения, окончательная длина строки отличается от длины atob->strA.

Следуя примерам в Интернете, я подумал, что это (как самая простая инициализация) будет работать... но это не так:

atob->strB = NULL;
atob->strB = (char*)malloc(1);
atob->strB[0]  "\0";

Это как-то связано со структурой, в которой он находится?

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

Теперь я просто сбит с толку и у меня нет времени, чтобы решить эту проблему. Если кто-то может сказать мне фактический код, который мне нужен, тогда я смогу прочитать его и понять, что вы сделали и почему это правильный метод. Я прокомментировал области, где мне не ясно, и был бы очень признателен за правильное решение.


person oblong    schedule 04.12.2016    source источник
comment
Вам не нужно вводить вызов malloc. Большинство программистов на C++ делают это, и в этом нет необходимости.   -  person Deanie    schedule 04.12.2016
comment
Вы можете использовать отладчик и показать обратную трассировку вашего сбоя?   -  person eyalm    schedule 04.12.2016
comment
Зачем минусовать этот? Если в вопросе не хватает ясности, попросите оператора объяснить больше.   -  person sjsam    schedule 04.12.2016
comment
Я сделал пример программы, которая как бы имитирует вашу ситуацию. Проверьте [ этот ] фрагмент кода.   -  person sjsam    schedule 04.12.2016
comment
@sjsam: по моему мнению, отрицательный голос предназначен для прямого запроса решения, а не для руководства к решению. Хотя это не вопрос дай мне-те-кодез, стоит избегать даже небольшого сходства с такими подходами!   -  person halfer    schedule 06.12.2016
comment
@halfer хм, я вроде как согласен с тем, что вы сказали, но этот вопрос показывает хорошее усилие новичка, даунвотер должен был попросить оператора перефразировать. В любом случае, я против минусования без обратной связи. ????   -  person sjsam    schedule 06.12.2016
comment
@sjsam: я слышу вас, но это много обсуждалось на Meta, и ответ заключается в том, что избиратели никогда не будут вынуждены техническими средствами предоставить комментарий или причину - голосование всегда будет анонимным. . Тем не менее, я согласен, что было бы неплохо, если бы избиратели предложили причину добровольно!   -  person halfer    schedule 06.12.2016


Ответы (2)


Этот код не делает того, что вы думаете:

atob->strA = malloc(sizeof((char *)a));
                    ^ sizeof(char *) is fixed. probably 4 or 8.

Итак, если a указывает на более длинную строку, у вас будет переполнение буфера.

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

Отладочная информация:

скомпилируйте, убедитесь, что он скомпилирован с отладочными символами (добавьте -g к команде gcc)

$ gcc -g -Wall -Werror a.c -o a.out

запустите его с помощью gdb

$ gdb ./a.out

после сбоя ищите обратную трассировку

(gdb) bt

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

так как мы имеем дело с проблемами обработки памяти, используйте valgrind:

$ valgrind --leak-check=full ./a.out

Посмотрите на вывод и почистите ошибки.

person eyalm    schedule 04.12.2016
comment
извините, ребята, как я уже сказал, я читал различные темы, форумы и следовал примерам и в итоге получил код, который я указал. Он много раз менялся в различных формах. Почему первоначальный malloc (strA) работает, а второй (strB) — нет, даже если они абсолютно одинаковы (т. е. копируют a в каждый)? Можете ли вы сказать мне, каким должен быть код для выделения 1 символа в char * strB, а затем как добавить символ в функцию добавления? - person oblong; 04.12.2016
comment
Ошибка, показанная в терминале, просто «получен сигнал 11 (ошибка сегментации)». - person oblong; 04.12.2016
comment
для отладки: - скомпилируйте свою программу с параметром -g, чтобы разрешить символы отладки - person eyalm; 04.12.2016
comment
make-файл содержит CFLAGS = -std=c99 -c -g. и затем компилирует gcc $(CFLAGS). Это единственная ошибка, которая выводится на терминал... - person oblong; 04.12.2016
comment
см. мой другой ответ об отладке - person eyalm; 04.12.2016
comment
хорошо, я попробую это - person oblong; 04.12.2016
comment
между прочим, даже если atob-›strA = malloc(sizeof((char *)a)); strcpy(atob-›strA, a); вызывает переполнение буфера, я понятия не имею, как это исправить... кто-нибудь может мне сказать? Это метод, который я скопировал из примера, если он не делает то, что говорит мне пример, я в полном тупике! Спасибо за вашу помощь - person oblong; 04.12.2016

Когда вы инициализируете strB пустой строкой, вы используете:

strB = "\0";

Но вместо этого вы должны использовать либо:

strB = '\0';

or:

strB[0] = '\0'.

Строковый литерал "\0" представляет собой массив из трех chars: {'\', '0', '\0'}. Вы делаете то же самое в других местах (здесь, похоже, вы пропустили имя поля struct):

atob->strB[sz] = "\0";

и (здесь я предполагаю, что вы случайно пропустили оператор присваивания):

atob->strB[0] = "\0";

Существует проблема с тем, как вы наращиваете строку. strlen() указывает количество chars в массиве перед терминатором NUL, поэтому, если вы хотите добавить еще char, вам нужно выделить sz + 2 chars. Затем новый char нужно поместить в strB[sz], где был старый NUL, а NUL нужно поместить в конец строки, то есть strB[sz + 1]. Вы можете NUL завершить строку strB, изменив свой код на:

atob->strB = realloc(atob->strB, sz + 2);
atob->strB[sz] = a;
atob->strB[sz + 1] = '\0';

Кроме того, realloc() может возвращать NULL, вызывая утечку памяти. Вы должны использовать временную переменную для хранения результата вызова realloc():

char *temp = realloc(atob->strB, sz + 2);
if (temp)
    atob->strB = temp;
person ad absurdum    schedule 04.12.2016
comment
да Дэвид, извините, это опечатка, это atob-›strB[sz] = \0; (пишу не копипаст). - person oblong; 04.12.2016
comment
@eyalm - исправлен размер THANKYOU (char *). вероятно, 4 или 8. Затем я понял, что это на самом деле терпит неудачу ДО выделения strB... - person oblong; 05.12.2016
comment
@sjsam - ваш пример имеет смысл, и теперь я понимаю: после объяснения eyalm я увидел, что strA вызывает проблему, но я все еще не мог понять, как я должен был выделить правильную длину. atob-›strA=malloc((l+1)*sizeof(char)); Я понимаю! strlen просто говорил мне, сколько было партий по 4 байта, а не фактическое количество, которое необходимо было выделить в памяти. СПАСИБО за ваше объяснение и, БОЛЕЕ ВАЖНО, за то, что вы прочитали мой вопрос и ответили именно на то, что я спросил. - person oblong; 05.12.2016
comment
@oblong-- обратите внимание, что вам не нужно использовать sizeof(char) в malloc. char — это один байт, а malloc() выделяет указанное вами количество байтов, поэтому malloc(1) выделяет место для 1 char. На самом деле использование sizeof(char) в вызове malloc() считается плохой практикой. - person ad absurdum; 05.12.2016
comment
привет @David, спасибо за это - но как тогда вы распределяете пространство, если вы не знаете, сколько вам нужно до времени выполнения? т. е. в случае, если 'a' имеет неизвестную длину, как atob->strA=malloc((l+1)*sizeof(char)); выполняться, если вы не использовали символы? или это доступно только для символов? то есть вы можете использовать его для массива целых чисел? - person oblong; 05.12.2016
comment
@oblong - char гарантированно будет 1 байтом, поэтому, если вам нужно выделить n char, вы используете malloc(n). Чтобы выделить ints, вы используете оператор sizeof(): malloc(sizeof(int) * n). Здесь просто избыточно использовать sizeof(char). - person ad absurdum; 05.12.2016
comment
понял... спасибо, Дэвид, sjsam и eyalm. Теперь я понимаю, что я делаю и почему, особенно на примере sjsam, который прояснил ситуацию, и теперь код работает именно так, как требуется. - person oblong; 05.12.2016