Запись массива Struct в двоичный файл - PullRequest
0 голосов
/ 21 марта 2012

Я не понимаю, что является ошибочным в программе. Я определяю указатель на массив структур. У Маллока достаточно памяти для этого. Инициализировал элементы массива. Затем использовал fwrite для записи массива в двоичный файл. Затем попытка прочитать то же самое, вернуться к другому указателю на аналогичный массив, который имеет достаточно памяти, выделенной для него.

#include<stdio.h>

typedef struct ss{
int *p;
char c;
double d;
char g;
float f;
} dd;

main(){

dd (*tt)[5];
int i=0,a[5]={4,1,6,9,3};
tt=malloc(sizeof(struct ss[5]));
for(i=0;i<5;i++){
   tt[i]->p=malloc(sizeof(int));
   tt[i]->p=&a[i];
   tt[i]->c=(char)('a'+i);
   tt[i]->d=(double)(5.234234+i);
   tt[i]->g=(char)('A'+i);
   tt[i]->f=(float)(15.234234+i);
}

FILE *F;
F=fopen("myfile","w+b");
size_t l;
l=fwrite(tt,sizeof(*tt),1,F);
fseek(F,0,SEEK_SET);
//printf("sizeof(dd)=%d   sizeof(*tt) =%d bytes written %d\n",sizeof(dd),sizeof(*tt),l);

dd (*xx)[5];

xx=malloc(sizeof(struct ss[5]));
l=fread(xx,sizeof(*xx),1,F);

for(i=0;i<5;i++){
printf("%d, %c,%f,%c,%f\n",*(xx[i]->p),xx[i]->c,xx[i]->d,xx[i]->g,xx[i]->f);
}
printf("Date Read %d \n",l);
for(i=0;i<5;i++){
free(xx[i]->p);
}
free(xx);
free(tt);
fclose(F);
remove("myfile");
}

Выход:
4, а, 5,234234, А, 15,234234
Ошибка сегментации

Ответы [ 2 ]

1 голос
/ 21 марта 2012

Вы не записывали свои данные там, где, как вы думали, были, потому что вы неправильно обращались к tt. Ваш неверный доступ был непротиворечивым, и, следовательно, вы могли прочитать первую запись, но вторая запись была далеко не там, где вы думали, что она была фактически записана в неинициализированную память и никогда не сохранялась. Попытка доступа к загруженным данным показывает это. Кроме того, ИНТ * в вашей структуре не мог быть написано правильно, как вы написали это, но это спорный вопрос, потому что, как ваша программа структурированной было бы неправильно, если вы пытаетесь загрузить файл в отдельном счете программы. fwrite и fread не могут следовать за вашим int*, потому что он только смотрит на вашу структуру как битовый паттерн - он точно восстанавливает ваш указатель, но теперь у вас есть указатель на случайный кусок памяти, который вы не сделали на самом деле делать что-нибудь с! В этом случае, однако, ваши указатели остаются действительными, потому что вы никогда не перезаписывали данные, но это характерно для сценария записи файла, без очистки памяти и чтения его обратно без закрытия программы, что не является реалистичный сценарий для написания файлов. Есть еще один вопрос StackOverflow, который более подробно объясняет эту ошибку.

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

dd (*tt)[5];
//...
tt=malloc(sizeof(struct ss[5]));
for(i=0;i<5;i++){
   tt[i]->p=malloc(sizeof(int));
   tt[i]->p=&a[i];
   //...
}

Объявления C читаются с помощью Правило спирали по часовой стрелке , поэтому давайте посмотрим, что мы сказали о tt, и сравним его с тем, как мы его используем.

tt - имя переменной. Справа от него закрывающая скобка, поэтому мы продолжаем обрабатывать текущую область. Мы встречаем *, а затем совпадающее слово, затем статический размер массива, а затем тип. Используя Правило спирали по часовой стрелке, tt является указателем на массив (размер 5) с dd. Это означает, что если вы разыменовываете tt (используя (*tt)), вы получаете dd[5], или, если вы предпочитаете думать об этом таким образом (безусловно, так делает C), указатель на начало блока памяти, достаточно большой держать вашу структуру. Что еще более важно, это то, что вы сказали, что это. C на самом деле не очень требователен к типам указателей, и именно поэтому ваш код компилируется, даже если вы совершаете серьезную ошибку типа.

Ваш оператор malloc верен: он инициализирует tt с областью памяти, которая, как обещала операционная система, имеет достаточно места для ваших пяти ss. Поскольку C не занимается глупыми вещами, такими как проверка границ размера массива, 5-элементный массив, равный struct ss, гарантированно будет в пять раз больше размера одного struct ss, так что вы могли бы написать malloc(5 * sizeof(dd)), но в любом случае это нормально.

Но давайте посмотрим, что здесь происходит:

tt[i]->p=malloc(sizeof(int));

Ой-ой. tt - это указатель на массив структуры dd, но вы только что обработали его как массив указателей на структуру dd.

Что вы хотели:

  • Разыменование tt
  • Найдите i -й элемент в массиве указателей на dd
  • Перейти к полю p
  • Назначьте ему указатель на пробел для int

Что вы на самом деле получили:

  • Найдите i -й элемент в массиве указателей на массивы dd
  • Разыменяем его, трактуя его как указатель на dd, поскольку C не знает разницы между массивами и указателями
  • Перейти косвенно к полю p
  • Назначьте ему указатель на пробел для int

Когда i равно 0, это работает правильно, потому что нулевой элемент в массиве и сам массив находятся в одном месте. (Массив не имеет заголовка, и C _ не понимает разницы между массивами и указателями и позволяет использовать их взаимозаменяемо, поэтому он компилируется вообще.)

Когда i не равно 0, вы запутываете память.Теперь вы пишете в любую память, которая следовала вашему указателю!Это на самом деле указатель, но вы сказали C, что это массив, и он поверил вам, добавил 1 элемент ширины к своему местоположению и попытался выполнить все эти операции.Вы используете массивы именно там, где вы должны использовать указатели, и указатели точно там, где вы должны использовать массивы.

Вы записываете только в память, выделенную для элемента 0. Помимо этого, вы пишете в несвязанныепамять, и это удача (неудача, в вашем случае), которая предотвратила крах вашей программы.(Если бы это было так, вам было бы легче найти это как строку виновности.) Когда вы fwrite используете выделенную память, первый элемент действителен, остальное - мусор, а ваши fread приводят к даннымструктура, которая имеет один действительный элемент, затем случайный мусор кучи, который привел к падению, когда вы попытались разыменовать указатель (это было бы допустимо только потому, что программа не завершилась).

Вот правильный способ доступаваш указатель на массив:

(*tt)[i].p=malloc(sizeof(int)); ...

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

*((*tt)[i].p)=a[i]

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

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

0 голосов
/ 21 марта 2012

Вы используете указатель неверно.В этом фрагменте кода:

dd (*xx)[5];

xx=malloc(sizeof(struct ss[5]));
l=fread(xx,sizeof(*xx),1,F);

for(i=0;i<5;i++){
printf("%d, %c,%f,%c,%f\n",*(xx[i]->p),xx[i]->c,xx[i]->d,xx[i]->g,xx[i]->f);
}

Вы объявляете xx как указатель на массив из 5 структур 'dd'.Вот где это становится странным.Это указатель на пять структур, а не массив из пяти структур.

It would look something like this in memory:

dd[0] = [{p, c, d, g, f}, {p, c, d, g, f}, {p, c, d, g, f}, {p, c, d, g, f}, {p, c, d, g, f}]
dd[1] = [{p, c, d, g, f}, {p, c, d, g, f}, {p, c, d, g, f}, {p, c, d, g, f}, {p, c, d, g, f}]
...
dd[4] = [{p, c, d, g, f}, {p, c, d, g, f}, {p, c, d, g, f}, {p, c, d, g, f}, {p, c, d, g, f}]

Instead of the intended:
dd[0] = {p, c, d, g, f}
dd[1] = {p, c, d, g, f}
...
dd[4] = {p, c, d, g, f}

Поскольку вы перебираете от 0 до 5, каждый доступ к массиву увеличивает ваш размер байта (ss [5]) в памяти вместо этого.размера (ss) байтов.Уберите дополнительный указатель.

dd* xx;
xx = (dd*)malloc(sizeof(dd) * 5);
l = fread(xx, sizeof(dd), 5, F);

for(i = 0; i < 5; ++i) {
  printf("%d, %c, %f, %c, %f\n", xx[i].p, , xx[i].c, xx[i].d, xx[i].g, xx[i].f);
}

Кроме того, у вас есть проблемы с вашей структурой.Если он предназначен для прямой записи на диск, он не может содержать указатели.Таким образом, ваш 'int * p;'член должен вместо этого быть 'int p;'.В противном случае, если вы прочитаете этот файл из отдельного приложения, сохраненный вами указатель больше не будет указывать на целое число, а на нераспределенную память.

Writing application:
    int *p = 0x12345 ---> 5
0x12345 gets stored in the file for p.

Writing application reads the file.
    int *p = 0x12345 ---> 5
The pointer still points at the same memory because it is still the same memory
  layout.

New application reads the file.
    int *p = 0x12345 ---> ?????
The pointer doesn't point to a known piece of memory because the memory layout
  has changed in this new instance of the application. This could crash or
  cause a security issue.
...