Ошибка сегментации strcpy/strncmp в полях структуры

Я пытаюсь добавить новый узел в структуру с полем char* (word)

Определение listT:

enum boolean {false, true};
struct list {
    enum boolean sorted;
    union{
        int words;
        char *word;
    };
    struct list* next;
    struct list* previous;
};
typedef struct list listT;

Функция add_word_node вызывается main как: add_word_node(read_word, list_head) где read_word задается пользователем с помощью scanf. Word передается как строка, но после strncpy нет завершающего байта.

>Debugger:
     add_word_node (word=0xbffff0fa "ally", head=0x804c038) at prog.c    
>Debugger:
     (gdb) p newnode->word


     $2 = 0x804c068 "allyP\224\373\267\377\377\377\377" 




 listT *add_word_node(char word[], listT *head) {
    listT *newnode;
    listT *curr = NULL;//sorted
    //listT *prev;//sorted

    newnode = malloc(sizeof(listT)); /* allocate new node and check */
    newnode->word = malloc(sizeof(char)* WORDLEN);
    strncpy(newnode->word, word, strlen(word));
    //newnode->word = strndup(word, strlen(word));


    if (newnode == NULL) {
        return (NULL);
    }

    if (head->sorted == false){ //eisagwgh sto telos ths listas
        newnode->next = head;
        head->previous->next = newnode;
        newnode->previous = head->previous;
        head->previous = newnode;
    }else {
        if(head->next == head){
            newnode->next = head;
            newnode->previous = head;
            head->next = newnode;
            head->previous = newnode;

        }else{
            for (curr = head->next; curr->next != NULL; curr = curr->next){//for( curr = head->next; ;){
                if(curr == NULL) break;
                if(strncmp(curr->word,newnode->word,strlen(word)) > 0) break;
                //else curr= curr->next;
            }
            if(strncmp(curr->word,newnode->word,strlen(word))== 0){
                return(curr);
            }
            newnode->next = curr;
            newnode->previous = curr->previous;
            newnode->previous->next = newnode;
            newnode->next->previous = newnode;
        }
    }

    return (newnode);

}

Я прочитал некоторые другие темы об этой проблеме и изменил функцию, чтобы использовать слово [] вместо char *, но она все еще не работает. Пожалуйста, сообщите мне, если вам нужна дополнительная информация. Кроме того, когда я использую strndup, иногда он работает без ошибок.


person user3163175    schedule 27.12.2014    source источник
comment
Вы уверены, что когда curr = head->next этот head-›next не равен NULL? Потому что в этом случае curr-›word точно выдаст ошибку seg.. Да, в этом проблема.   -  person Igor    schedule 27.12.2014
comment
Я добавил оператор if, чтобы разорвать цикл for в случае, если curr == NULL, но все равно получаю ту же ошибку seg.   -  person user3163175    schedule 27.12.2014
comment
В вашем цикле for я бы проверил действительность curr, а не просто переназначил бы его в else: for (cur = head->next; curr->next; curr = curr->next) было бы немного безопаснее   -  person Elias Van Ootegem    schedule 27.12.2014
comment
Я изменил, как вы предложили, но я все еще получаю seg. for (curr = head->next; curr->next != NULL; curr = curr->next) if(curr == NULL) break; if(strncmp(curr->word,newnode->word,strlen(word)) > 0) break;   -  person user3163175    schedule 27.12.2014
comment
Опубликовать определение listT   -  person chux - Reinstate Monica    schedule 27.12.2014
comment
strncpy(newnode->word, word, strlen(word)+1); неправильно.   -  person wildplasser    schedule 27.12.2014
comment
Не могли бы вы объяснить, что не так с этой строкой?   -  person user3163175    schedule 27.12.2014
comment
Функция add_word_node никогда не вызывается, поэтому в этой программе ничего не может произойти. Кроме того, структура listT не определена, поэтому этот код даже не компилируется.   -  person barak manos    schedule 27.12.2014
comment
@ user3163175: это ложная безопасность. Он делает то же самое, что и strcpy(newnode->word, word);, и не предлагает дополнительной защиты (strncpy никогда этого не делает). Также отсутствует определение для вашей структуры List. (является ли newnode-›word указателем или массивом? Это может быть даже массив переменной длины)   -  person wildplasser    schedule 27.12.2014
comment
@barak manos listT определен, и я разместил его на 3 комментария выше. Также это функция программы, а не отдельной программы.   -  person user3163175    schedule 27.12.2014
comment
Почему вы не указали это в вопросе, где это место?   -  person wildplasser    schedule 27.12.2014
comment
Ну, во-первых, размещайте соответствующую информацию в вопросе, а не в комментариях. Во-вторых, мне не приходило в голову, что эта функция и есть вся ваша программа. Я пытался вежливо намекнуть, что вы не разместили всю необходимую информацию, необходимую для ответа на ваш вопрос. Вы должны хотя бы показать, с какими параметрами вы вызываете эту функцию.   -  person barak manos    schedule 27.12.2014
comment
@barakmanos Надеюсь, я не обидел тебя (я не хотел). Вы правы, и я извиняюсь за недостаточно ясность, я добавлю больше информации.   -  person user3163175    schedule 27.12.2014


Ответы (2)


Как вы говорите, у вас есть char *word, тогда этому указателю должна быть выделена память

newnode->word = malloc(sizeof(char) * (WORDLEN+1)); /* Please access the structure elements accordingly */

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

person Gopi    schedule 27.12.2014
comment
Разве это не: newnode = (listT *) malloc(sizeof(listT) + WORDLEN); выделяет память для слова char*? - person user3163175; 27.12.2014
comment
@user3163175 user3163175 Я не вижу, что такое WORDLEN в коде, но это не нужно, просто выделите память для вашей структуры и выделенной памяти указатель внутри структуры - person Gopi; 27.12.2014
comment
Спасибо за информацию. Я изменил его, но не думаю, что это решит проблему, поскольку WORDLEN больше, чем фактическая длина слов. - person user3163175; 27.12.2014
comment
@user3163175 user3163175 Пожалуйста, выделите память для хранения WORDLEN Проверить правки - person Gopi; 27.12.2014
comment
сделал но ничего не изменилось - person user3163175; 27.12.2014
comment
@ user3163175 Вы все еще видите ошибку сегментации? WORDLEN + 1 удерживать терминатор NULL? - person Gopi; 27.12.2014
comment
Я был неправ. Я изменил malloc, но теперь слово передается как альфа, но после strncpy нет \0 - person user3163175; 27.12.2014
comment
Я добавил пример в вопросе - person user3163175; 27.12.2014
comment
@user3163175 user3163175 Вы NULL завершаете его вручную. Добавить memset(newnode->word,'\0',(WORDLEN+1)) - person Gopi; 27.12.2014
comment
Memset не работал так, как я хотел (он добавил \0 в начале), но, поскольку слово представляет собой массив, я просто добавил его в конце. Большое спасибо за ваше время и помощь. - person user3163175; 27.12.2014

Чтобы немного расширить ответ @Gopi:

Ваше заявление

newnode = (listT *) malloc(sizeof(listT) + WORDLEN);

выделяет память указателю newnode. Это означает, что, например, если WORDLEN совпадает с sizeof(listT), то вы будете выделять память для двух элементов listT для newnode. Это похоже, скажем, на char *ptr = malloc(2);, который будет выделять память для двух элементов char для ptr.

TL;DR: эта память WORDLEN НЕ будет выделена для newnode->word, она выделена для newnode, и все. Чтобы решить вашу проблему, вы должны выделить память для newnode->word отдельно. newnode эта память не нужна, а newnode->word нужна.

strndup() работает, потому что выделяет память для вас.

Если вы все еще пытаетесь понять это, у вас могут быть некоторые неправильные представления об указателях.

newnode->word — это указатель. Он имеет фиксированный размер на определенной машине (например, 8 байт). Для простоты предположим, что listT определяется следующим образом:

typedef struct {
    char *word;
} listT;

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


Хотя это и не связано с вашей проблемой, я также хотел бы отметить, что вы не должны приводить возвращаемое значение malloc(). См. раздел приводить результат malloc?

person user12205    schedule 27.12.2014