Может ли неправильное назначение моего односвязного списка привести к неинициализированному значению где-нибудь? - PullRequest
0 голосов
/ 30 марта 2020

Здравствуйте и спасибо всем, кто потрудился прочитать! В настоящее время я работаю над программой, которая использует односвязный список для печати шаблона, основанного на первой букве цветового элемента и первого элемента в массиве адресов подробностей int, показанном ниже.

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

typedef struct Node{
    char* color;
    int** details;
    struct Node* next;
}Node;

Node* light_info(char *filename){
    Node* head = malloc(sizeof(Node));
    Node* second = malloc(sizeof(Node));
    Node* third = malloc(sizeof(Node));
    Node* fourth = malloc(sizeof(Node));
    Node* fifth = malloc(sizeof(Node));
    Node* sixth = malloc(sizeof(Node));

    head->next = second;
    second->next = third;
    third->next = fourth;
    fourth->next = fifth;
    fifth->next = sixth;
    sixth->next = NULL;

    FILE* fp = fopen(filename, "r");
    char* token;
    int num1, num2;
    int* ptr1 = &num1;
    int* ptr2 = &num2;
    char line[250];
    Node* current = head;

    if(head==NULL||second==NULL||third==NULL||fourth==NULL||fifth==NULL||sixth==NULL){
        printf("Error. Program has amnesia, not allocating memory...");
    }
    else{
        while((fgets(line,250,fp)!=NULL)&&(current!=NULL)){
            token = strtok(line, ",\n");
            current->color = malloc(20);
            current->color = token;

            current->details = malloc(sizeof(ptr1)*2);
            token = strtok(NULL, ",\n");
            num1 = atoi(token);
            current->details[0]=ptr1;

            token = strtok(NULL, ",\n");
            num2 = atoi(token);
            current->details[1]=ptr2;

            current = current->next;
        }
    }
    return head;    
}

/*Takes a Node pointer as a parameter, and iterates through the linked list,
  printing out a number of dashes for each light's brightness score.*/
void off(Node* h){
    printf("\n***Turning lights off:\n");

    while(h!=NULL){
        int i;
        int* ptr = h->details[0];
        int b = *ptr;

        for(i=0;i<b;i++){
            printf("-");
        }
        printf(" ");
        h=h->next;
    }
    printf("\n");
}

/*Takes a Node pointer as a parameter, and iterates through the linked list, 
  printing out the first letter of each color the amount of times each light is bright.*/
void on(Node* h){
    printf("\n***Turning lights on:\n");

    while(h!=NULL){
        int i;
        int* ptr = h->details[0];
        int b = *ptr;

        for(i=0;i<b;i++){
            printf("%c",h->color[0]);
        }
        printf(" ");
        h=h->next;
    }
    printf("\n");
}

/*Frees data using Node's head and prints exit message.*/
void Exit(Node* h){
    printf("Exiting...\n");

    Node* temp;
    while(h!=NULL){
        free(h->color);
        free(h->details);
        temp = h;
        h = h->next;
        free(temp);
    }
}


int main(int argc, char **argv){
    if(argc < 2||argc >= 3){
        printf("Error. Enter one filename and one filename only.");     
    }
    else{
        char* filename = argv[1];
        Node* head = light_info(filename);
        int vibe_check = 1;
        int Switch = 0;
        char resp[10];

        while(vibe_check){
            if(head == NULL){
                printf("Head memory not allocated...");
                vibe_check = 0;
            }
            else{
                scanf("%s", resp);
                if(strcmp(resp,"off")==0){
                    if(Switch==0){
                        printf("\n--Lights are already off.\n");
                    }
                    else{
                        Switch = 0;
                        off(head);
                    }
                }
                else if(strcmp(resp,"on")==0){
                    if(Switch==1){
                        printf("\n--Lights are already on.\n");
                    }
                    else{
                        Switch = 1;
                        on(head);
                    }
                }
                else if(strcmp(resp,"exit")==0){
                    Exit(head);
                }
                else{
                    printf("Invalid command, type in off, on, or exit.");
                }
            }
        }
    }
}

Данные для этой программы происходит файл, отформатированный следующим образом:

blue,2,0
green,1,0
red,1,0
red,2,1
yellow,2,1
blue,1,1

Ожидаемое значение для программы должно составлять bb g r rr yy b каждый раз, когда кто-то печатает с тире, заменяя буквы, когда кто-то печатает после того, как горит свет , Однако при запуске программы ничего не выводится, и после нескольких раундов отладки и valgrind я заметил две вещи:

  1. Структуры узла не назначаются построчно, что приводит к тому, что узлы имеют дубликаты последней полученной строки данных, а не отдельные фрагменты данных, основанные на прочитанной строке.
  2. Согласно valgrind, они являются неинициализированным значением в функции on, которую я не могу найти.

Извините, если это довольно долго, но любая помощь будет высоко ценится! Я открыт для редактирования моего поста и любой конструктивной критики в случае необходимости!

1 Ответ

0 голосов
/ 30 марта 2020

При обработке ввода в light_info имеется несколько ошибок.

Сначала current->color назначается некоторая выделенная память, затем эта память теряется при назначении на следующей строке (current->color = token; ). Это назначение не будет копировать строку, указанную token, но изменит значение color, чтобы оно указывало на то же место, которое находится в локальной переменной line, и будет зависать, когда ваша функция вернется. Вы захотите использовать strcpy (или более безопасную версию, strncpy), чтобы скопировать строку в выделенное пространство.

Вы делаете нечто подобное с current->details[0]=ptr1;. ptr1 указывает на локальную переменную num1, поэтому этот указатель также станет висящим, когда функция вернется. Я не уверен, почему details это массив указателей. Массив int (где вы можете хранить значения напрямую) может показаться лучшим выбором. (То же самое относится к details[1] и ptr2).

Не связанные с этой проблемой, проверки ошибок для одного из mallocs, которые не удалось, находятся не в том месте. Вы уже разыменовали все эти указатели, прежде чем проверять их на NULL. Эта проверка должна выполняться сразу после того, как вы сделали последний malloc, перед тем, как начать назначать все эти next указатели.

В других местах также отсутствует проверка ошибок.

...