Looking for a quick way to allocate a block of data to be managed from disk. I'm allocating a block of 50 structs, and while most of the memory allocates fine, when I read it all back I get junk messages returned in some of the fields that should be blank. I assume this is me allocating the space incorrectly somehow that allows some junk from memory to leak in there.
if ((fpBin = fopen(BINARYFILE, "w+b")) == NULL)
{
printf("Could not open binary file %s.\n", BINARYFILE);
return;
}
fwrite(fpBin, sizeof(struct student), 50, fpBin); //Write entire hash table to disk
struct definition
typedef struct student
{
char firstName[20]; //name
char lastName[20];
double amount; //amount owed
char stuID[5]; //4 digit code
}student;
Is how I was taught, yet I'm still getting some junk in my data instead of it being a clean slate. So question: How do I set all fields to blank?
Answer:
student tempStu[50] = {0};
fwrite(tempStu, sizeof(struct student), BUCKETSIZE, fpBin); //Write entire hash table to disk
fwrite(fpBin, sizeof(struct student), 50, fpBin);
You're writing your file pointer, not your student structs, to disk. That first fpBin should instead be a pointer to your data. That data can be an array of 50 student structs initialized to 0, perhaps with calloc or by defining it at file scope, but it has to be somewhere. Instead, you are writing 50*sizeof(struct student) bytes from your fpBin pointer, which is undefined behavior -- you'll either crash with an access violation or you'll write junk to disk. That junk is what you're getting when you read it back.
Also, using a constant like 50 is bad practice ... it should be a variable (or manifest constant) that holds the number of students that you're writing out.
BTW, on Linux and other POSIX systems, you could allocate a block of zeroes on disk just by writing the last byte (or in some other way making the file that large).
Related
I have two structs in C. One is called full_file, and the other is called data_holder. full_file contains a pointer to data_holder, and data_holder contains a pointer to itself. I use this to make a linked list of numbers.
struct full_file {
int magic_number;
struct data_holder *number_list;
};
struct data_holder {
unsigned long int number;
struct data_holder *next;
};
My problem is that once I have the linked list, and full_file.number_list points to its first element, I save full_file into a file using fwrite().
int write_size = fwrite((char *)&numbers,1,sizeof(struct full_file),out_file);
if (write_size != sizeof(struct full_file)) {
fprintf(stderr, "Error: failure to write data into '%s'.\n",out);
exit(8);
}
This doesn't seem to cause any problems. However, when I try to read these file contents into full_file again:
struct full_file numbers;
int read_size = fread((char *)&numbers,1,sizeof(struct full_file),in_file);
There is a segmentation fault. I tried changing sizeof(struct full_file) to 0, and this segmentation fault doesn't happen. Of course that'd mean that no data would be retrieved from the file. I don't know what's wrong here. What can I do to solve this error and read get the linked list back?
I don't know what's wrong here.
struct full_file numbers;
int read_size = fread((char *)&numbers,1,sizeof(struct full_file),in_file);
About the only way the fread could produce segmentation fault is if in_file is uninitialized, refers to fclosed file pointer, or is NULL.
Make sure to check fopen return (which is presumably what you used to initialize in_file) for errors.
Other random notes:
you have size and nmemb parameters to fread reversed (not that it matters much),
you should store result of fread in a size_t variable, not an int (it doesn't matter here either, but it's a good habit).
storing pointers in a file on disk (as your fwrite does) it generally a very bad idea: you can't use them for anything when you read them back in a different program, or a different run of the same program.
I'm doing a work in wish I have to read caracters from a File, and save them in a buffer. Later i have to output them, but for know I'm still struggling with the buffer thing.
I would like to know how can I initialize the buffer(empty of course) and then put caracters into that buffer.
I want to do it in another function who is called by int main(..).Here's waht I have:
buffer_t initBuffer(void) {
// Reserves space for the buffer, ends program if it cant reserve space.
buffer_t buffer = malloc(sizeof(*buffer));
if (buffer == NULL) {
printf("Error allocating bufefr\n");
exit(1);
}
after this, how can I initialize it with his counter at 0?
Definition of buffer_t:
typedef struct buffer_s *buffer_t;
struct buffer_s {
unsigned char buffer[BUF_SIZE];
counter;
}
There are two apprroaches for two different needs. Choose whichever suits your requirement.
If you want to initialize your allocated memory area to 0, you can use calloc() to directly do that (allocate and initialize to 0).
If you want to initialize your allocated buffer to some other value, you can use memset() after doing a malloc().
i am new to C so i believe there is a rookie mistake somewhere in my code due to lack of fundamentals in pointers and memory allocation.
I have a binary file representing numerical data, and i am trying to read and store that data.
This is first part of the code that opens the file, reads fisrt few numbers in file which are than used to allocate enough memory for the struct emxArray_real_T.
Struct:
struct emxArray_real_T
{
real_T *data;
int32_T *size;
int32_T allocatedSize;
int32_T numDimensions;
boolean_T canFreeData;
}
First part of main:
# include <stdio.h>
# include <stdlib.h> /*atoi*/
# include <assert.h>
int main(int argc, char** argv){
//Variable declaration
unsigned short numOfSums;
unsigned long chSum, countRSN, countPeriods;
int i,j;
FILE *file;
//Open file
file = fopen("testBin.bin","rb");
//Read first number that tells how many items to skip
fread(&numOfSums, 2, 1,file);
//Skip that many items
for (i=0;i<numOfSums;i++){
fread(&chSum,4,1,file);
}
//Read next two numbers
fread(&countRSN,4,1,file);
fread(&countPeriods,4,1,file);
//Allocate enaugh space based on the size of countRSN and countPeriods
struct emxArray_real_T* Sa_1 = malloc(sizeof(*Sa_1)*1);
assert(Sa_1 != NULL);
Sa_1->data=malloc(sizeof(real_T)*countRSN*countPeriods);
Sa_1->size=malloc(sizeof(int32_T)*2);
Sa_1->allocatedSize=(sizeof(int32_T)*1);
Sa_1->size[0]=countRSN;
Sa_1->size[1]=countPeriods;
struct emxArray_real_T *Sa_2;
Sa_2=(struct emxArray_real_T*)malloc(sizeof(struct emxArray_real_T)*1);
assert(Sa_2 != NULL);
Sa_2->data=(real_T*)malloc(sizeof(real_T)*countRSN*countPeriods);
Sa_2->size=malloc(sizeof(int32_T)*2);
Sa_2->allocatedSize=(sizeof(int32_T)*1);
Sa_2->size[0]=countRSN;
Sa_2->size[1]=countPeriods;
struct emxArray_real_T *sVs30;
sVs30=(struct emxArray_real_T*)malloc(sizeof(struct emxArray_real_T));
sVs30->data=malloc(sizeof(real_T)*countRSN);
sVs30->size=malloc(sizeof(int32_T)*1);
sVs30->allocatedSize=(sizeof(int32_T)*1);
sVs30->size[0]=countRSN;
Here is the problem. If i try to store my data and transpose it, because it's not in the right order, i get Segmentation fault,
for (i=0;i<countRSN;i++){
for (j=0;j<countPeriods;j++){
fread(&Sa_1->data[countRSN*j+i],8,1,file);
}
}
if i just try like this, it is working:
for (i=0;i<countRSN*countPeriods;i++){
fread(&Sa_1->data[i],8,1,file);
}
.
.
.
fclose(file);
free(Sa_1);
free(Sa_2);
free(sVs30);
return 0;
}
You are assuming size of types. Use sizeof everywhere you use 4, 2, 8 etc. Also make sure the fread will work with short int, which I doubt
With every call to fread you read 8 bytes and write them to the array. It seems to me you are storing them in the correct position. A Segmentation fault would have to be expected if sizeof(real_T) is smaller than 8.
If sizeof(real_T) is smaller than 8, say it's size 4*, &Sa_1->data[countRSN*countPeriods-1] will write 4 of the 4 bytes in a valid location, and the other 4 bytes will be written outside of the allocated range.
Why does the code crash in one case and not the other? First, by accessing unallocated memory you are in the territory of undefined behaviour. The program doesn't need to behave in a well defined way anymore. Second, fread doesn't write to the buffer if the file stream has already reached the end. The code which works in your example only writes to the last address if the file is long enough.
*In Matlab, real_T is either 4 or 8 bytes, depending on defines. If it's 4 bytes, the code given by the op should throw a segfault.
struct emxArray_real_T* Sa_1 = malloc(sizeof(*Sa_1)*1);
To
struct emxArray_real_T* Sa_1 = malloc(sizeof(struct emxArray_real_T));
This question already has answers here:
Writing and reading (fwrite - fread) structures with pointers
(3 answers)
Closed 8 years ago.
gcc (GCC) 4.7.0
c89
Hello,
I have the following structure that I am trying to fwrite and fread.
However, because my device and resource are pointers. The fwrite will read the pointer values and not the data. I cannot use a array for the device or resource. Only pointers as they have to be dynamically allocated.
I allocate all memory for the structure elements before I write. Not shown here as I want to keep the snippet short. Nor is free'ing.
In my fread function, I allocate the memory for the device and resource so that the fread will read into these memory locations. However, this will not work.
What is the best way to do this?
Many thanks for any advice,
struct data {
int id;
int set;
char *device;
char *resource;
};
struct database {
struct data **db_data;
size_t database_rows;
size_t database_data_size;
};
int database_write(FILE *fp, const struct database *db)
{
rewind(fp);
if(fwrite(*db->db_data, sizeof(struct data), 1, fp) == -1) {
return DATABASE_ERROR;
}
return 0;
}
struct database* database_read(FILE *fp, size_t db_rows, size_t db_data_size)
{
struct database *db = NULL;
size_t i = 0;
db = malloc(sizeof(struct database));
db->database_rows = db_rows;
db->database_data_size = db_data_size;
db->db_data = malloc(sizeof(struct data) * db_rows);
for(i = 0; i < db_rows; i++) {
db->db_data[i] = malloc(sizeof(struct data));
db->db_data[i]->device = malloc(db_data_size);
db->db_data[i]->resource = malloc(db_data_size);
}
rewind(fp);
if(fread(*db->db_data, sizeof(struct data), 1, fp) == -1) {
return NULL;
}
return db;
}
You seem to have answered your own question, fread and fwrite just look at what's in memory and put that in the file. This works great if you're writing things that don't have pointers (e.g. big arrays of numbers). It's not designed to write structs with pointers.
If this file has a format, you need to do what the format says. If you're making up a format as you go, then you should write each member one by one into the file. You will need some sort of buffer to read into (you may need to resize this if you don't have a maximum length specification). Also, your database_write function will need to be changed quite a bit as well.
If device and resource can have variable length you should write down the size of device and then the data. Do the same for resource.
When you read them back you can read the size, then allocate memory and finally read the value.
You have yourself described you problem. fwrite will write the address and not the value.
May be you can use a field for the length of device and resource in your structure "struct data".
Create a wrapper for fread() and fwrite() which reads/writes this length.
In this wrapper you can memcpy devices, resource in a temporary buffer and use fwrite() on it.
This is a simple and very basic solution.
While sending packets in networks, you will generally see a structures containing char pointers. The first 4/8 bytes store the length of the data and the remaining bytes contain the actual data.
User reading the packet, first reads the beginning 4/8 bytes. Depending on this, read() call is issued to read the remaining data.
You may refer
Is the "struct hack" technically undefined behavior?
I am trying to find how to store and process (search, add, remove) an array of linked lists on disk. For example in memory, it would like
struct list {
int a;
struct list *next;
}LIST
LIST *array[ARRAY_SIZE]
int main{
...
LIST *foo = array[pointer];
/* Search */
while(foo!=NULL){
...
foo=foo->next
}
}
I believe that by using fseek() I can point to a specific element/structure of the array in the file. But I cannot understand if all previous elements need to have been written or not.
Can this be done with dynamic allocation on disk?
How will I link on element to another one in the linked list?
Any example would certainly help!
Okay, as Amardeep says, this sounds like the sort of thing that would best be done practically using some kind of database, like, or, Berkeley DB. But let's answer the questions anyway.
you can indeed use fseek (or its system call, lseek) to determine where something is on the disk and find it later. If you use some method of computing the offset, you're implementing what we used to call a direct file; otherwise you might store an index, which leads you off toward the indexed sequential method.
whether it can be done with dynamic allocation sort of depends on the file system. Many UNIX file systems support * sparse* allocation, which means that if you allocate block 365 it doesn't have to allocate blocks 0 through 364. Some dont.
Let's say that you have a structure with a length k that looks more or less like this:
(trick the parse)
struct blk {
// some stuff declared here
long next; // the block number of the next item
};
You create the first item; set its block number to 0. Set next to some distinguished value, say -1.
// Warning, this is off the cuff code, not compiled.
struct blk * b = malloc(sizeof(struct blk));
// also, you should really consider the case where malloc returns null.
// do some stuff with it, including setting the block next to 1.
lseek(file, 0, SEEK_SET); // set the pointer at the front of the file
write(file, sizeof(struct blk), b); // write it.
free(b);
// make a new block item
malloc(sizeof(struct blk));
// Do some stuff with it, and set next to 2.
lseek(file, 0, SEEK_CUR); // leave the pointer where it was, end of item 0
write(file, sizeof(struct blk), b); // write it.
free(b);
You now have two items on disk. keep this up and you'll eventually have a thousand items on disk. Now, to find item # 513, you just
lseek(file, (sizeof(struct blk)*513), SEEK_SET);
You need a buffer; since we freed the previous ones we'll make another
b = malloc(sizeof(struck blk);
Read that many bytes
read(file, sizeof(struct blk), b);
And poof record 513 is in memory pointed to by b. Get the record following with
lseek(file, (sizeof(struct blk)*b->next), SEEK_SET);