Ошибка чтения типа char* из файла .DAT с помощью C

Итак, по какой-то причине мне нужно создать внешний файл (.DAT) для хранения данных, добавив новый в конец старых данных.

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

int main () {
    typedef struct {
        char *Name;
        int Index;
    } DataFile;

    static FILE *file;
    size_t result;
    DataFile *DataTable;

    file = fopen("database.DAT","ab");
    DataTable = (DataFile *) malloc (sizeof(DataFile));
    DataTable[0].Name = "somefile.txt";
    DataTable[0].Index = 7;
    printf("%s %d \n",DataTable[0].Name,DataTable[0].Index);
    result = fwrite(DataTable,sizeof(DataFile),1,file);
    fclose(file);
    free(DataTable);
    return 0;
}

После запуска приведенного выше кода я проверяю, правильно ли сохранены данные. Итак, я делаю этот код ниже.

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

int main () {
    typedef struct {
        char *Name;
        int Index;
    } DataFile;

    static FILE *file;
    size_t result;
    long size;
    int i;
    DataFile *DataTable;

    file = fopen("database.DAT","rb");
    if (file == NULL) printf("Error1");

    // Determine the size of file
    fseek(file,0,SEEK_END);
    size = ftell(file);
    rewind(file);

    DataTable = (DataFile *) malloc ((size/sizeof(DataFile)) * sizeof(DataFile));
    if (DataTable == NULL) printf("Error2");

    result = fread(DataTable,sizeof(DataFile),size/sizeof(DataFile),file);
    fclose(file);

    for (i=0; i<result; i++) {
        printf("%s %d \n",DataTable[i].Name,DataTable[i].Index);
    }
    free(DataTable);
    return 0;
}

Однако он дает вывод

somefile.txt 7

из первого блока кода и

Error1 7

из второго блока кода. Я замечаю, что проблема не в том, что сбой либо при открытии файла .DAT, либо при выделении памяти для DataTable. Кроме того, он работает для типа int (индекс), но не для типа char* (имя) при чтении из файла .DAT. Я понятия не имею, что делать, чтобы решить эту проблему чтения типа char * (и откуда берется «error1»). (даже гугл не дает мне ответа.)


person user1825826    schedule 15.11.2012    source источник
comment
Как обычно, DataTable = (DataFile *) malloc (sizeof(DataFile)); лучше писать как DataTable = malloc(sizeof *DataTable);. Не переводите возвращаемое значение malloc() в C и не повторяйтесь без необходимости.   -  person unwind    schedule 15.11.2012


Ответы (3)


Ваша структура DataFile хранит один указатель и одно целое число. Когда вы записываете его в файл, вы записываете определенный указатель программы на строку и целое число.

При чтении из него вы просто пополняете свою структуру указателем и целым числом, что означает, что DataFile.Name будет указателем на вероятно неинициализированный сегмент памяти. Но поскольку вы создали свой файл, указывающий на первую жестко закодированную строку ("filename.txt"), происходит некоторое неопределенное, но понятное поведение, и ваш указатель в этом случае указывает на первую жестко закодированную строку, которую вы написали во второй программе (которая в вашем случае это Error1)

Что вы действительно хотите сделать, так это записать настоящую строку в свой файл.

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

typedef struct {
        char Name[512];
        int Index;
    } DataFile;

затем инициализируйте свои данные с помощью

strncpy(DataTable[0].Name, "somefile.txt", sizeof(DataTable[0].Name) - 1); // just to make sure you dont overflow your array size
DataTable[0].Name[sizeof(DataTable[0].Name) - 1] = '\0';

и пересмотрите свои данные так, как вы это сделали.

person tomahh    schedule 15.11.2012
comment
Спасибо, @Tom, твой ответ попал в цель. Меня всегда путали с указателями и строками. :) - person user1825826; 15.11.2012

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

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

person MvG    schedule 15.11.2012

В вашем коде написания вы не выделили память для char *Name. Когда вы выполняете инструкцию DataTable[0].Name = "somefile.txt", вы на самом деле не копируете "somefile.txt" в память, на которую указывает Name, а фактически присваиваете Name значение, указывающее на строку постоянных символов (более того, она станет оборванным указателем, поскольку строка является значением r, т.е. не у меня нет памяти для обращения через). То же самое касается вашего кода чтения файлов. Тебе нужно:

  1. Выделите место для вашего Name.
  2. Скопируйте строку, используя memcpy или аналогичную, в выделенное хранилище.
person SomeWittyUsername    schedule 15.11.2012