EDIT: OK, I hear you guys, I've isolated the part of my code that's giving me problems, compiled it and made sure that it still gave me the same results, here it goes:
Like before, the segfault appears after the first instance of the for loop on
strcpy(replace[j]->utf8, strtok(data, "\t")); Thanks again!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#define max_chars 45
#define max_UTF 5
#define max_ASCII 7
#define max_word_length 30
#define max_line_length 70
#define max_texto_line 5000
typedef struct {
char utf8[max_UTF];
char ascii_seq[max_ASCII];
int count;
} Replac;
void getTable(FILE *f, char inputfile[],Replac **replace){
char data[max_line_length];
int j;
f = fopen( inputfile, "r" );
if (f == NULL) {
fprintf(stderr, "Can't open input file %s!\n",inputfile);
exit(1);
}
fgets(data,sizeof data,f);
for(j=0 ; strcmp(data,"\n") ; fgets(data,sizeof data,f), j++){
if (feof(f)) {
break;
}
strcpy(replace[j]->utf8, strtok(data, "\t"));
strcpy(replace[j]->ascii_seq, strtok(NULL, "\n"));
}
fclose(f);
}
int main( int argc, char *argv[] ){
Replac *replace=malloc(max_chars * sizeof(Replac));
FILE *fpr,*f,*fpw;
int carprocess = 0;
setlocale(LC_ALL,"pt_PT.UTF-8");
setlocale(LC_COLLATE,"pt_PT.UTF-8");
getTable(f,argv[1],&replace);
}
The text file that I'm copying the character from is formated something like this
UTFCHAR \tab asciichar
ex
Á 'A
END EDIT
-#-##-###-####-####+#####+####p
So I'm a beginner using C, and I've tried all I could think of, this seems like a pretty straight forward thing to do, but since I'm having such trouble clearly shows I have some gap in my knowledge...
I wont bother you with the full code since it is working perfectly, it's just that I wanted to do things differently and that's when the trouble started.
In short I'm doing a program that collects a set of chars of UTF8 type, and their ascii replacement, and stores them in a struct such as
typedef struct {
char utf8[max_UTF];
char ascii_seq[mac_ASCII];
} Replac;
then in main I did the malloc like this
Replac *replace=malloc(max_chars * sizeof(Replac));
If my thought process is correct, this would create a block of available memory to which *replace is pointing to the starting address.
Then I made a function that scans a few UTF8 chars and their replacement and stores them in the struct, something like
void getTable(FILE *f, char inputfile[],Replac **replace)
now, following the debugger, it seems that I'm creating new variable replace of the type Replace** that's on a completely different address, but inside that address is stored the value to the original malloced struct that I passed through the param.
After that I do a
strcpy(replace[0]->utf8, something I got from the table);
following the debugger and searching through the memory adresses, I see that the first time I do this, the first position of the malloc struct is indeed filled with the right data.
followed by
strcpy(replace[0]->ascii_seq, corresponding ascii sequence to the previous UTF8 char);
and that fills the next memory position in the memory block.
So I get something like while debugging on my variables watch
address replace = (Replac **) 0xbf8104fc that contains 0x0878a008
address *replace = (Replac *) 0x0878a008 that contains the whole struct
so inside the address 0x0878a008 I get the data of the utf8 char and then at the address 0x0878a00d I get the ascii seq.
The problem in on the next instance of the loop, when it's time to
strcpy(replace[1]->utf8, something I got from the table);
I get a segmentation fault after that instruction.
So what do you guys think? Am I approaching things correctly, and I'm getting screwed over by syntax or something like that, or is it the base of my knowledge flawed?
Thanks, and a late happy holidays!
f = fopen( inputfile, "r" );
...
typedef struct
{
char utf8[max_UTF];
char ascii_seq[max_ASCII];
int count;
} Replac;
...
fgets(data,sizeof data,f);
You are mixing binary and text format.
Depending on the compiler, sizeof(Replac) will be 16. This includes sizeof(int) which is always 4. There may also be padding if size is not a multiple of 4.
If your data is stored as text, then it will be something like this:
ABCDE\tABCDEFG123456\n
Note that the size of integer in decimal format is anywhere between 0 to 10, so the size is not fixed. And there are (or there should be) new line \n characters.
So you don't want to read exactly 16 characters. You want to write and then read 3 lines for each record. Example:
ABCDE\n
ABCDEFG\n
123456\n
If you are reading in binary, then open the file in binary and use fwrite and fread. Example:
f = fopen( inputfile, "rb" );
Replac data;
fread(f, sizeof(data), 1, f);
This all depends on how your file was created. If you are writing the file yourself, then show the code you used for writing the data.
Also, ASCII is a subset of Unicode. A in ASCII has the exact same representation as A in UTF8.
strcpy(replace[j]->utf8, strtok(data, "\t"));
I get a segmentation fault after that instruction.
You just got the dereferencing order wrong. You first subscripted with [j] and then dereferenced with ->, as if we had an array of pointers to Replacs. But we rather have a pointer to (the first element of) an array of Replacs, hence we must dereference the pointer first and subscript thereafter, i. e. instead of
replace[j]->utf8
we have to write
(*replace)[j].utf8
or the equivalent
(*replace+j)->utf8
Related
I fairly new to C Programming, but fprintf() & printf() is behaving strangely and I'm so confused on why--I need some help understanding and diagnosing this issue.
fprintf() Deleting Element of Array
First off, I'm passing in a populated malloc allocated four element char** array into a simple function that will write to a file, everything in the array appears normal and all four elements contain the correct data. The function call in main() looks like this. My array in question is header.
Note: I had to cast this normal (char** array) as a constant in this function parameter, due to the function header parameter. Our professor gave us the header file and we cannot change anything in them.
pgmWrite((const char**) header, (const int**) matrix,
rowPixels, colPixels, outFile);
Next, stopping debugger just before it executes the fprintf() & printf() functions, screenshot showing the array is still populated with my 4 elements.
pgmWrite() - Showing array is still fine
Observe the 4th element of the array after execution of fprintf().
After fprintf() executes, element 3 memory is wiped out.
When run, printf() executes the printing of the array exactly what is shown in the debugger, ending at the 3rd element. Often printing nothing in that spot or in rare cases garbage characters. The behavior of printf() is exactly the same as how fprintf() is working as well.
I'm at a loss here guys, please help me understand what I'm doing wrong. I can only provide these two screenshots, based on me being a new member. I'll try to provide as much information as possible. Thank you. Here is a simplified version of my program. Keep in mind, the professor gave us the function declarations and told us we cannot change them. So, I have to work with what I have here. Also, since this is fileIO, you need to find a *.pgm file to test this.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define rowsInHeader 4
#define maxSizeHeadRow 200
int ** pgmRead( char **header, int *numRows, int *numCols, FILE *in ){
// INITIALIZING
char *headArr[rowsInHeader][maxSizeHeadRow];
char buffer[100];
int r = 0;
fpos_t pos;
// CREATE: Header
while (r < 4){
// IF: Row in pgm file header lists the dimensions of matrix
if (r == 2){
// CURSOR: Saving pointer location in file (see notes in header for method reference)
fgetpos(in, &pos);
// ASSIGN: Dereference column and row pointers from file
fscanf(in, "%d %d", numCols, numRows);
// CURSOR: Moving back to saved pointer location (see notes in header for method reference)
fsetpos(in, &pos);
}
// ASSIGN: Copying header row into array
fgets(buffer, maxSizeHeadRow, in);
strcpy((char*)headArr[r], buffer);
// POINTER: Reference pointer to headArr[]
header[r] = (char*)headArr[r];
// TRAVERSE: To next row in file
r++;
}
// NOTE: Placeholder for return type
return 0;
}
int pgmWrite( const char **header, const int **pixels, int numRows, int numCols, FILE *out ){
// INITIALIZING
int i = 0;
// WRITE: Header
for (i = 0; i < rowsInHeader; i++){
fprintf(out, "%s", header[i]);
printf("%s", header[i]);
}
return 0;
}
int main(int argc, char *argv[]){
char **header = (char**)malloc(rowsInHeader * sizeof(char));
FILE *inFile = fopen("smallFile.pgm", "r");
FILE *outFile = fopen("TestPicture.ascii.pgm", "w");;
int rowPixels = 0;
int colPixels = 0;
int **matrix = NULL;
// READ & WRITE
matrix = pgmRead(header, &rowPixels, &colPixels, inFile);
pgmWrite((const char**)header, (const int**)matrix, rowPixels, colPixels, outFile);
// FINALIZING
fclose(inFile);
free(header);
return 0;
}
You are not allocating your array correctly. This line:
char **header = (char**)malloc(rowsInHeader * sizeof(char));
makes header point to an uninitialized region of memory , size 4 bytes.
Then inside your PGM function you write:
header[r] = (char*)headArr[r];
The code header[r] means to access the r'th pointer stored in the space pointed to by headArr. But since that space is only 4 bytes big, you're actually writing off into the wild blue yonder.
Also, (char *)headArr[r] is a mistake. If you did not use the cast, your compiler would have warned you about this mistake. You should avoid using casts in your code, especially using them to make warnings go away. You're saying to the compiler "Ssh, I know what I'm doing" when in fact you don't know what you are doing.
The entire approach with headArr is flawed from the start: even if you had actually written the right code to implement what you were trying, you'd be returning pointers into space which is deallocated when the function returns.
Basically the whole pgmRead function is a complete mess and it'd be easier to start from scratch. But this time, think carefully about when and where you are allocating memory, and what the types are of your expressions, and don't use casts. Let the pgmRead function do all the allocation.
(Unfortunately, based on your description it looks like you will have to use your casts to call the pgmWrite function since that has a mistake in its signature. const int ** should be const int * const *, and similarly for const char **. I'd recommend to actually change pgmWrite's signature accordingly, get your program working, and then once everything is good, then go back to the broken version that you are forced to use.)
Reading C FAQ - arrays and pointers might be useful too.
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));
I am trying to store a string data file into a multi-dimensional array using C.My sample data looks as below.I am thinking to use below char declaration for storing my data.Please kindly advise me if there is any other method.
char *array[6][10];
53,v42,p11,51097,310780,ok
56,v45,p11,260,1925,ok
68,v42,p11,51282,278770,ok
77,v50,p11,46903,281485,ok
82,v46,p12,475,2600,ok
84,v48,p12,433,3395,ok
96,v49,p14,212,1545,ok
163,v50,p20,373819,1006375,ok
204,v50,p26,36917,117195,ok
241,v70,p33,21777,91360,ok
Looks fine, if you want it to be strings.
What I suggest is to not allocate a string for every single field. Instead, read a line from the file, allocate a single string for it, tokenise out the commas, and store the pointers in your 2D array.
Or indeed, if your data file fits easily into memory, there's no reason not to read the entire thing and tokenise. Effectively, you just use your array as an index into the buffer.
There are many other ways to do it; some will depend on whether you've learned about structures yet.
One point to note: C uses 'row-major' ordering for arrays, so most programmers would write char *array[10][6]; for 10 rows of 6 columns each.
You could decide that instead of using char *, you allow, say, 10 characters per string, and use:
char array[10][6][10];
This would fit the data shown (with a little room to spare; the second 10 could be as small as 8 for the data shown). It would require a lot less dynamic memory management.
Alternatively, you could define a structure that represents a line:
struct csv_line
{
int col1;
char col2[4];
char col3[4];
int col4;
int col5;
char col6[4]; // Correct length uncertain...
};
And then have a 1-dimensional array of these:
struct csv_line array[10];
This is more or less how I'd do it. Note that the structure uses 24 bytes per line compared with 48 per line in char array[10][6][8];, and compared with 48 bytes for pointers plus the actual strings and storage overhead for the char * versions on a 64-bit machine. The total storage for the char pointer version could easily be 144 bytes per row on a 64-bit machine.
Clearly, this analysis of size assumes that you have separate allocations for each string. If you read each line of data into a separately allocated line and then store pointers to the parts of the line, the storage overhead is (dramatically) reduced. A lot will depend on how you are going to use the data. If you're going to treat the numeric-looking fields as numbers, then I'd use the structure; it will save on data conversions later.
I hope this sample program will help you to address your issue. I used structure and sscanf. This is the nice way to address this issue.
#include <stdio.h>
struct data
{
char date[100];
char state[100];
char profit[100];
char revenue[100];
};
int main()
{
char line[4096] = {'\0'};
char t1[100], t2[100], t3[100], t4[100];
struct data d[2];
int i = 0;
while(fgets(line, 4096, stdin) != NULL) {
sscanf(line, "%[^','],%[^','],%[^','],%s", &t1, &t2, &t3, &t4);
strcpy(d[i].date, t1);
strcpy(d[i].state, t2);
strcpy(d[i].profit, t3);
strcpy(d[i++].revenue, t4);
}
i = 0;
while(i<2){
printf("%s %s %s %s\n", d[i].date, d[i].state, d[i].profit, d[i].revenue);
i++;
}
}
$> a./exe < file
$> cat file
12/2/2012,TN,1200,14000
12/3/2012,KA,2333554,424
I need to build an array of pointers to dynamically allocated structures (DBrecord) and fill that array with input from another file. Not sure how to approach this.
The data file will have the number of entries first, followed by entries in a specific order.
numOfEntries
lastName firstName studentID year gpa expGradYear
example:
1
Doe John 12345678 senior 3.14159 2015
Here's the code I have so far:
class.h
typedef enum {firstYear, sophomore, junior, senior, grad} class;
main.c
#include <stdio.h>
#include <stdlib.h>
#include "class.h"
int main(){
//DBrecord is name for structure
struct DBrecord{
int DBrecordID; //ID for each entry, range 0-319
char *last; //student last name
char *first; //student first name
char studentID[8]; //student ID
int age; //student age
class year; //year in school
float gpa; //GPA
int expGradYear; //expected graduation year
};
int numEntries; //total number of entries, first num in data file
struct DBrecord **ptrToDB;
//scan first int in data file and assign to numEntries
scanf("%d", &numEntries);
//allocate memory for structures, each is 36 bytes
*ptrToDB = malloc (sizeof(struct DBrecord) * numEntries);
//free allocated memory
free(ptrToDB);
//build an array of pointers to dynamically allocated structures
//fill that array with input from data file
//build 7 arrays of pointers to DBrecords, one for each field except DB ID
//sort each a different way
//note the 7 arrays are pointers, no copying
//print each of the 7 sorted arrays
return 0;
}
I can give you some snippets on how to look at this problem.
First - I would avoid using class name for any variable, because in many object-oriented programming languages (including C++) it is a keyword and can't be a name of variable.
Structure DBrecord
It might be a good idea to use typedef. Then you could declare a struct variable without using "struct DBrecord", just "DBrecord". But that's optional. This is how it would look:
typedef struct {
int DBrecordID; // ID for each entry
char *lastName;
char *firstName;
char studentID[8];
...
} DBrecord;
Loading from file
In this homework you have the number of records at the beginning of the file, so you don't need to take "extra" care about it. Just load it.
Let's assume the file is like this:
2
Doe John 12345678 senior 3.14159 2015
Carl Boss 32315484 junior 2.71 2013
Therefore the first thing you do with your file is to open it.
Portable way of working with files is by using FILE pointer. Let me show it (stdio.h must be included):
FILE *filePtr; // Define pointer to file
if((filePtr = fopen("records.txt", "r")) == NULL) // If couldn't open the file
{
printf("Error: Couldn't open records.txt file.\n"); // Printf error message
exit(1); // Exit the program
}
Then you can read from your file by line using fgets() to read by lines or fgetc() to read by characters. This is how you can read number of records (remember that it's on the first line and we've just opened the file - we are at the beginning of the file):
char buffer[100]; // Define the buffer
fgets(buffer, 100 /* size of buffer */, filePtr);
Now buffer contains the first line (without \n character) - number of records. Continue with converting the num's characters into integer (here stdlib.h also has to be included):
int numOfRecords = atoi(buffer);
Allocating enough DBrecords
Now you know the number of records, you can allocate enough space for them. We will use array of pointers.
DBrecord **recs;
recs = (DBrecord **) malloc(sizeof(DBrecord *) * numOfRecords);
Now we have created array of pointers, so now we need to allocate every individual pointer as a DBrecord. Using cycle:
int i;
for(i = 0; i < numOfRecords; i++)
{
recs[i] = (DBRecord *) malloc(sizeof(DBrecord));
}
Now you can acces array elements (= individual records) like this:
recs[0]->lastname /* two possibilities */
*(recs[0]).lastname
an so on.
Filling array with values from file
Now you know everything to get the homework done. This way you fill the array:
int i;
for(i = 0; i < numOfRecords; i++)
{
// Content of cycle reads one line of a file and parses the values into recs[i]->type...
/* I give you small advice - you can use fgetc(filePtr); to obtain character by character from the file. As a 'deliminer' character you use space, when you hit newline, then the for cycle continues.
You know the order of stored values in the file, so it shouldn't be hard for you.
If you don't get it, let me now in comments */
}
Is it somehow clearer now?
EDIT: File name as main's argument
There are usually two ways of 'passing' arguments (values) to a program. They are:
./program < records.txt // Here the file's name is passed to program on stdin
./program records.txt // Here the file's name is passed as value in `argv`.
If you can choose, I strongly recommend you the second one. Therefore you need to have main defined as this:
int main(int argc, char *argv[]) // this is important!
{
// code
return 0;
}
argc is integer which says, how much arguments were passed to the program. argv is array storing them. Remember, that the first argument is name of the program. Therefore if you need to check for it, do it:
if(argc != 2)
{
printf("Number of arguments is invalid\n");
exit(1); // exit program
}
Then you only put argv[1] into fopen function, instead of the string "records.txt".
EDIT 2: Reading file's name from stdin
Another approach must be done, if the name of the records file is passed to the program via ./program < records.txt, which means that "records.txt" (without quotes) will be passed (redirected) to program's standard input.
Therefore to handle that, you can do this:
char filename[50]; // buffer for file's name
scanf("%s", &filename); // reads standard input into 'filename' string until white character appears (new line, blank, tabulator).
Then you have your desired file's name in filename string.
Where to start, where to start.....
//allocate memory for structures, each is 36 bytes
mem = (double *)malloc(36*numEntries);
malloc should be malloc (sizeof (struct DBRecord) * numEntries);
don't cast the result of malloc
2a. you forgot stdlib.h
Why include class.h?
your array of pointers are not double, they are instead
struct DBRecord **ptrToDB;
*ptrToDB = malloc (sizeof (struct DBRecord) * numEntries);
This should get you started.
Next, free() should be the last thing you do before leaving your function (and yes, main is a function)
You'll have to insert some code for the next part, I can't do the homework for you.
I have a structure with the following definition:
typedef struct myStruct{
int a;
char* c;
int f;
} OBJECT;
I am able to populate this object and write it to a file. However I am not able to read the char* c value in it...while trying to read it, it gives me a segmentation fault error. Is there anything wrong with my code:
//writensave.c
#include "mystruct.h"
#include <stdio.h>
#include <string.h>
#define p(x) printf(x)
int main()
{
p("Creating file to write...\n");
FILE* file = fopen("struct.dat", "w");
if(file == NULL)
{
printf("Error opening file\n");
return -1;
}
p("creating structure\n");
OBJECT* myObj = (OBJECT*)malloc(sizeof(OBJECT));
myObj->a = 20;
myObj->f = 45;
myObj->c = (char*)calloc(30, sizeof(char));
strcpy(myObj->c,
"This is a test");
p("Writing object to file...\n");
fwrite(myObj, sizeof(OBJECT), 1, file);
p("Close file\n");
fclose(file);
p("End of program\n");
return 0;
}
Here is how I am trying to read it:
//readnprint.c
#include "mystruct.h"
#include <stdio.h>
#define p(x) printf(x)
int main()
{
FILE* file = fopen("struct.dat", "r");
char* buffer;
buffer = (char*) malloc(sizeof(OBJECT));
if(file == NULL)
{
p("Error opening file");
return -1;
}
fread((void *)buffer, sizeof(OBJECT), 1, file);
OBJECT* obj = (OBJECT*)buffer;
printf("obj->a = %d\nobj->f = %d \nobj->c = %s",
obj->a,
obj->f,
obj->c);
fclose(file);
return 0;
}
When you write your object, you're writing the pointer value to the file instead of the pointed-to information.
What you need to do is not just fwrite/fread your whole structure, but rather do it a field at a time. fwrite the a and the f as you're doing with the object, but then you need to do something special with the string. Try fwrite/fread of the length (not represented in your data structure, that's fine) and then fwrite/fread the character buffer. On read you'll need to allocate that, of course.
Your first code sample seems to assume that the strings are going to be no larger than 30 characters. If this is the case, then the easiest fix is probably to re-define your structure like this:
typedef struct myStruct{
int a;
char c[30];
int f;
} OBJECT;
Otherwise, you're just storing a pointer to dynamically-allocated memory that will be destroyed when your program exits (so when you retrieve this pointer later, the address is worthless and most likely illegal to access).
You're saving a pointer to a char, not the string itself. When you try to reload the file you're running in a new process with a different address space and that pointer is no longer valid. You need to save the string by value instead.
I would like to add a note about a potential portability issue, which may or may not exist depending upon the planned use of the data file.
If the data file is to be shared between computers of different endian-ness, you will need to configure file-to-host and host-to-file converters for non-char types (int, short, long, long long, ...). Furthermore, it could be prudent to use the types from stdint.h (int16_t, int32_t, ...) instead to guarantee the size you want.
However, if the data file will not be moving around anywhere, then ignore these two points.
The char * field of your structure is known as a variable length field. When you write this field, you will need a method for determining the length of the text. Two popular methods are:
1. Writing Size First
2. Writing terminal character
Writing Size First
In this method, the size of the text data is written first, followed immediately by the data.
Advantages: Text can load quicker by block reads.
Disadvantages: Two reads required, extra space required for the length data.
Example code fragment:
struct My_Struct
{
char * text_field;
};
void Write_Text_Field(struct My_Struct * p_struct, FILE * output)
{
size_t text_length = strlen(p_struct->text_field);
fprintf(output, "%d\n", text_length);
fprintf(output, "%s", p_struct->text_field);
return;
}
void Read_Text_Field(struct My_STruct * p_struct, FILE * input)
{
size_t text_length = 0;
char * p_text = NULL;
fscanf(input, "%d", &text_length);
p_text = (char *) malloc(text_length + sizeof('\0'));
if (p_text)
{
fread(p_text, 1, text_length, input);
p_text[text_length] = '\0';
}
}
Writing terminal character
In this method the text data is written followed by a "terminal" character. Very similar to a C language string.
Advantages: Requires less space than Size First.
Disadvantages: Text must be read one byte at a time so terminal character is not missed.
Fixed size field
Instead of using a char* as a member, use a char [N], where N is the maximum size of the field.
Advantages: Fixed sized records can be read as blocks.
Makes random access in files easier.
Disadvantages: Waste of space if all the field space is not used.
Problems when the field size is too small.
When writing data structures to a file, you should consider using a database. There are small ones such as SQLite and bigger ones such as MySQL. Don't waste time writing and debugging permanent storage routines for your data when they have already been written and tested.