Случайное изменение значений массива структур

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

Эта проблема случайным образом меняет все значения имени моей структуры на последнее назначенное имя. Также я не совсем понимаю, как преобразовать разделенный токен в плавающий из строки.

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


struct _data {
     char *name;
     long number;
};

int SCAN(FILE *(*stream)){
     int count;
     char dataString[50];
     int check = 1;
     count = 0;

     while(check){
          fscanf(*stream, "%s\n", dataString);
          fscanf(*stream, "%s\n", dataString);
          if (feof(*stream)){
               check = 0;
          }
          count++;
     }      
     return count;
}

struct _data *LOAD(FILE *stream, int size){
     int x;
     char *tempLine;
     size_t length = 0;
     const char delim[2] = " ";
     char *token;

     rewind(stream);
     struct _data *array = malloc(sizeof(struct _data) * size);  
     printf("this is the size: %d\n\n", size);
     for(x = 0; x < size; x++){
          getline(&tempLine, &length, stream);
          token = strtok(tempLine, delim);
          //printf("this is inside the for loop of load: %s\n", token);
          array[x].name = token;
          token = strtok(tempLine, delim);
          //printf("this is the token: %s\n", token);
          array[x].number = atol(token);
          printf("this is name %s, and phone number %ld\n", array[x].name, array[x].number);
     }
     printf("i am now outside the initial for loop in load\n\n");
     for(x = 0; x < size; x++){
               printf("this is name %s, and phone number %ld\n", array[x].name, array[x].number);
          }
     return array;

}

void SEARCH(struct _data *BlackBox, char *name, int size){
     int x;
     int check = 0;
     for(x = 0; x < size; x++){
          printf("BlackBox Name: %s, check name: %s\n", BlackBox[x].name, name);
          //printf("this is the check: %d\n", strcmp(BlackBox[x].name, name));
          if (0 == strcmp(BlackBox[x].name, name)){
               printf("*******************************************\n");
               printf("The name was found at the %d entry.\n", x);
               printf("*******************************************\n");
               check = 1;  
          }
     }
     if (check == 0){
          printf("*******************************************\n");
          printf("The name was NOT found.\n");
          printf("*******************************************\n");
     }
}

void FREE(struct _data *BlackBox, int size){
     free(BlackBox);
}

int  main(int argv, char **argc){
     FILE *fp;
     int size;
     int x;
     struct _data *BlackBox;
     if(argv < 2){
          printf("*******************************************\n");
          printf("* You must include a name to search for.  *\n");
          printf("*******************************************\n");
     }else{
          fp = fopen("hw5.data", "r");
          size = SCAN(&fp);
          BlackBox = LOAD(fp, size);
         /* for(x = 0; x < size; x++){
               printf("BlackBox Name: %s, check name: %s\n", BlackBox[x].name, argc[1]);
          }*/          
          SEARCH(BlackBox, argc[1], size);
          FREE(BlackBox, size);
     }
     return 0;
}

Вот мой вклад

ron 7774013
jon 7774014
tom 7774015
won 7774016
bonny 7774017

Вот мой вывод

ron 0
jon 0 
tom 0 
won 0 
bonny 0
i am now outside the initial for loop in load
bonny 0
bonny 0
bonny 0
bonny 0
bonny 0

person Jeffrey Hennen    schedule 18.02.2017    source источник
comment
array[x].name = token; копирует только указатель места в tempLine. Вероятно, вам нужна копия строки.   -  person chux - Reinstate Monica    schedule 18.02.2017


Ответы (1)


Вы продолжаете повторно использовать пространство, выделенное getline(), поэтому вы видите только последние значения. Вам нужно установить tempLine в NULL и length в 0 после цикла. Вы также должны проверить возвращаемое значение из getline(), чтобы убедиться, что вы действительно получили данные для чтения.

Ваш второй вызов strtok() должен использовать указатель NULL. Повторно указав tempLine, вы преобразуете имя в число.

      getline(&tempLine, &length, stream);
      token = strtok(tempLine, delim);
      //printf("this is inside the for loop of load: %s\n", token);
      array[x].name = token;
      token = strtok(tempLine, delim);
                     ^^^ should be NULL!

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

Этот код может работать так, как нужно, но он не был скомпилирован.

struct _data *LOAD(FILE *stream, int size)
{
    char *tempLine = NULL;
    size_t length = 0;
    const char delim[] = " ";
    char *token;
    int x;

    rewind(stream);
    struct _data *array = malloc(sizeof(struct _data) * size);
    printf("this is the size: %d\n\n", size);
    for (x = 0; x < size; x++)
    {
        if (getline(&tempLine, &length, stream) == -1)
        {
            free(tempLine);
            break;
        }
        token = strtok(tempLine, delim);
        // printf("this is inside the for loop of load: [%s]\n", token);
        array[x].name = token;
        token = strtok(NULL, delim);
        // printf("this is the token: [%s]\n", token);
        array[x].number = atol(token);
        printf("%d: this is name %s, and phone number %ld\n", x, array[x].name, array[x].number);
        length = 0;
        tempLine = NULL;
    }
    printf("i am now outside the initial for loop in load\n\n");
    for (int i = 0; i < x; i++)
    {
        printf("%d: this is name %s, and phone number %ld\n", i, array[i].name, array[i].number);
    }
    return array;
}

Также обратите внимание, что вам следует избегать создания имен, начинающихся с подчеркивания (например, struct _data). Многие такие имена зарезервированы для использования реализацией; проще всего вообще не создавать имена, начинающиеся с подчеркивания.

person Jonathan Leffler    schedule 18.02.2017
comment
Да!!! это сработало, спасибо!! Но да, это я использую предопределенные функции от моего профессора, поэтому у меня были такие имена (именно так он определил их в домашнем задании). - person Jeffrey Hennen; 18.02.2017
comment
Вы не можете помочь профессорам с плохими привычками кодирования. Отметьте проблему, спросите о ней, если вы смелы, и запомните ее для своей работы. - person Jonathan Leffler; 18.02.2017
comment
Итак, проблема заключалась в том, что у меня было имя для каждого из индексов, установленное на указатель. Из-за этого он перенаправлялся на адрес указателя, который, в свою очередь, имел последнее значение, я прав в этом предположении? - person Jeffrey Hennen; 18.02.2017
comment
Да — это в принципе правильно. Если бы у вас была действительно длинная строка, вам, возможно, удалось бы заставить getline() выделить более длинный буфер, и в этом случае у вас могло бы быть два набора значений в вашем массиве, но порог часто равен 256 для начального выделения, поэтому строка действительно было бы довольно долго. Сбрасывая длину и указатель на NULL на каждой итерации, вы заставляете getline() выделяться при каждом вызове. Распределение выполняется до обнаружения EOF, поэтому обнаружение EOF должно освободить выделенное пространство. - person Jonathan Leffler; 18.02.2017
comment
Сладкий, спасибо. вы действительно помогли мне узнать на самом деле, определенно один из лучших людей с ответами, которых я видел. - person Jeffrey Hennen; 18.02.2017
comment
Обратите внимание, что за кулисами скрывается неприятная проблема, если вы доберетесь до освобождения памяти. Предположим, что пользователь ставит перед вводом 2 пробела; тогда указатель, сохраненный в array[x].name, не будет указателем, возвращаемым malloc(), поэтому попытка его освобождения приведет к хаосу. Либо не освобождайте память, либо убедитесь, что нет начальных пробелов, либо сохраните запись указателя, возвращенного malloc(), а также имя, найденное strtok(). - person Jonathan Leffler; 18.02.2017