Чтение и запись структур [C] - PullRequest
1 голос
/ 04 марта 2010

ВАЖНОЕ РЕДАКТИРОВАНИЕ:

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

Опять же, вопрос, который я задавал: «Если вы читаете в структуре, читаете ли вы также данные, которые она содержит, или вам нужен другой доступ к ней».

Извините за путаницу

Для выполнения задания мне была поручена программа, которая записывает и считывает структуры на диск (используя fread и fwrite).

У меня проблемы с пониманием концепции. Допустим, у нас есть такая структура:

typedef struct {
    short nameLength;
    char* name;
}attendenceList;

attendenceList names;

теперь предположим, что мы даем ему эти данные:

names.name = "John Doe\0";
names.nameLength = strlen(names.name); /*potentially -1?*/

и затем мы используем fwrite ... учитывая указатель файла fp.

fwrite(&names,sizeof(names),1,fp);

Теперь мы закрываем файл и позже открываем его для чтения в структуре. вопрос в следующем: когда мы читаем в структуре, мы также читаем в переменных, которые она хранит?

Можем ли мы теперь сделать что-то вроде:

if(names.nameLength < 10)
{
 ...
}

Или мы должны придумать что-то большее, чем просто структуру, или назначить их как-нибудь? Предполагая, что фред:

fread(&names,sizeof(names),1,fp);

Также при условии, что мы определили структуру в нашей текущей функции, как указано выше.

Спасибо за помощь!

Ответы [ 5 ]

3 голосов
/ 04 марта 2010

У вас есть проблема здесь:

fwrite(&names,sizeof(names),1,fp);

Так какsentDence сохраняет список как char *, это просто выдает указатель, а не фактический текст.Когда вы прочитаете это обратно, в памяти, на которую ссылается указатель, скорее всего будет что-то еще.

У вас есть два варианта:

  1. Поместить массив символов (char names[MAXSIZE]) в список посещаемости.
  2. Не записывайте структуру необработанных данных, а пишите необходимые поля.
1 голос
/ 04 марта 2010

Вы пишете структуру памяти структуры, которая включает ее членов.

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

Вашname элемент объявляется просто как символ, поэтому вы не можете хранить в нем строку.

Если бы name был указателем, подобным этому:

typedef struct {
    short nameLength;
    char *name;
}attendenceList;

Вы действительно должныне читать / записать структуру в файл.Вы напишите структуру, как она выложена в памяти, и она включает значение, если указатель name.

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

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

Если вы объявите name как массив, все будет в порядке, так как массив и его содержимое являются частью структуры.

typedef struct {
    short nameLength;
    char name[32];
}attendenceList;

Как всегда, убедитесь, что вы не пытаетесь скопировать строку - включая ее нулевой терминатор - в name, который больше 32. И при повторном чтении.установите yourstruct.name [31] = 0;так что вы уверены, что буфер завершен нулем.

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

attendenceList my_list;

//initialize my_list
if(fwrite(&my_list,sizeof my_list,1,f) != 1) {
 //handle error
}

И снова прочитать ее:

attendenceList my_list;

//initialize my_list
if(fread(&my_list,sizeof my_list,1,f) != 1) {
 //handle error
}

}

0 голосов
/ 04 марта 2010

Несколько вещей.

Структуры - это просто куски памяти. Это просто берет кучу байтов и рисует на них границы. Доступ к элементам структуры - это просто удобный способ получить конкретное смещение памяти в виде данных определенного типа

Вы пытаетесь присвоить строку типу char. Это не будет работать. В C строки - это массивы символов с байтом NULL в конце. Самый простой способ заставить это работать - установить сторону фиксированного буфера для имени. Когда вы создаете свою структуру, вам нужно скопировать имя в буфер (будьте очень осторожны, чтобы не записать больше байтов, чем содержит буфер). Затем вы можете записать / прочитать буфер из файла за один шаг.

struct attendanceList {
     int  namelen;
     char name[256]; //fixed size buffer for name
}

Другой способ сделать это, указав имя на строку. Это делает то, что вы пытаетесь сделать, более сложным, потому что для записи / чтения структуры в / из файла вам нужно будет учесть, что имя хранится в другом месте в памяти. Это означает две записи и две операции чтения (в зависимости от того, как вы это делаете), а также правильное назначение указателя name, куда бы вы ни прочитали данные для имени.

struct attendanceList {
    int   namelen;
    char* name; //the * means "this is a pointer to a char somewhere else in memory"
}

Есть третий способ, которым вы могли бы сделать это, с динамически изменяемой структурой, использующей трюк с массивом нулевой длины в конце структуры. Как только вы узнаете, как долго длится имя, вы выделяете правильную сумму (sizeof (struct ParticipanceList) + длина строки). Тогда у вас есть это в одном непрерывном буфере. Вам просто нужно помнить, что sizeof (struct ParticipanceList) не тот размер, который вам нужен для записи / чтения. Это может быть немного запутанным как начало. Это также своего рода хак, который поддерживается не всеми компиляторами.

struct attendanceList {
   int namelen;
   char name[0];  //this just allows easy access to the data following the struct.  Be careful!

}

0 голосов
/ 04 марта 2010

Вы спрашиваете:

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

Нет.sizeof (names) - это постоянное значение, определенное во время компиляции.Он будет таким же, как

sizeof(short) + sizeof(void*) + some_amount_of_padding_to_align_things

, он будет НЕ включать размер, на который указывает names.name, он будет включать только размер самого указателя.

Итак, у вас есть две проблемы при записи этого файла.

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

Поскольку ваш код в данный момент написан, когда вы читаете имена, names.name будет указывать куда-то, но не будет указывать на "John Doe \ 0".

Что вынужно написать строку, на которую указывает names.name вместо значения указателя.

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

typedef struct {
    short nameLength;
    char  name[1]; // this will be variable sized at runtime.
}attendenceListFlat;

int cbFlat = sizeof(attendenceListFlat) + strlen(names.name);
attendenceListFlat * pflat = malloc(cbFlat);
pflat->nameLength = names.nameLength;
strcpy(pflat->name, names.name);

fwrite(pflat, cbFlat, 1, fp);

Сглаженная структура заканчивается массивом, который имеет минимальный размер 1, но когда мы malloc, мы добавляем strlen (names.name), чтобы мы могли обработать этов виде массива strlen (names.name) +1 размера.

0 голосов
/ 04 марта 2010

Полагаю, вы имели в виду char* name вместо char name. Также sizeof(name) вернет 4, потому что вы получаете размер char*, а не длину массива char. Поэтому вы должны написать strlen(name), а не sizeof(name) внутри вашего fwrite.

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

Если вы читаете только строку из файла и записали точный размер без нулевого завершения. Затем вам нужно вручную завершить нулевой буфер, после того как вы прочитаете данные в. Поэтому убедитесь, что вы выделяете как минимум размер данных, которые вы читаете, в плюс 1.
Затем вы можете установить последний байт этого массива на '\0'.

Если вы записываете целую структуру за раз в буфер, вы должны быть осторожны из-за заполнения. Заполнение не всегда может быть одинаковым.

когда мы читаем в структуре, мы также читаем в переменных, которые она хранит?

Да, но проблема в том, что, как я упоминал выше, вы будете хранить указатель char * (4 байта), а не фактический массив char. Я бы порекомендовал хранить элементы структуры индивидуально.

...