I am working with the following text file: https://mega.nz/#!EN4iRJxA!VtKFEl9dlQHWHIzcgfOfpXTt9_ill_YkgsLWL3nORLg
And the following code:
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
int id;
char lastname[25];
char name[20];
float average;
}student;
int main(){
int i;
student *pointer= (student*) malloc(3);
FILE *file= fopen("Struct.txt", "r");
fread(pointer, sizeof(student), 3, file);
for(i= 0; i< 3; i++){
printf("Id: %d\nName: %s\nLast name: %s\nAverage: %.2f\n",
(pointer+ i) -> id,
(pointer+ i) -> name,
(pointer+ i) -> lastname,
(pointer+ i) -> average);
}
fclose(file);
system("pause");
return 0;
}
When I delete the fclose function it works but I checked it and seems to be correct. What am I missing?
It has nothing to do with your fclose, instead you need to fix two things:
You need to check the return error of fopen:
if( NULL == file )
{
printf( "Cannot open\n" );
exit( 1 );
}
Your malloc code doesn't look right, should be:
student *pointer= malloc(3 * sizeof(student));
Your program isn't allocating memory the way you think it is. malloc(3) returns three bytes of memory. Most likely the file pointer is immediately after it in memory
[ | | ][*FILE]
When you read in the file, the data first goes in to the three bytes of memory you've allocated. But since it doesn't fit, it keeps going and overwrites the memory containing the pointer to the file too.
[student data student data]
Then calling fclose closes the file at dat or whatever was read in from the file. This is a programming error, sometimes called a "wild pointer dereference", and if the data read in doesn't point to something that can be closed like a file can (as is happening to you) the program will crash.
The fixes suggested by artm should resolve your problem, but I thought this explanation might help you understand why you saw the crash.
Related
I have to make a program which will print the name and the score of a student found on a text file. The text file itself will follow this format:
number of students (int)
score name (of student number one)
score name (of student number two)
So for example, the students.txt file you will see I am using for this program should be something like this:
2
45 George
23 John
The reading part of the program (for the structures) should be done using fread. I have tried what you will see below and the problem is that when I run the program the console just remains blank. Note that the program is not complete yet, since I am trying to learn how a few things work first (for example I am only trying to print the scores of the student just so I can see whether I know what I am doing) before doing it properly and I know some parts like checking for NULL return are missing. I am also wondering whether since I am using fread the txt file should be considered binary or not.
Here is what I have tried:
#include <stdio.h>
#include <stdlib.h>
#define FILENAME students.txt
#define MAX 50
typedef struct st {
float score;
char name[MAX];
} student;
void read(FILE *fp, student *p, int size);
int main()
{
int number;
student *ptr;
FILE *ifp;
ifp = fopen("FILENAME", "rb");
fscanf(ifp, "%d", &number);
ptr = (student *)malloc(number * sizeof(student));
read(ifp, ptr, number);
}
void read(FILE *fp, student *p, int size)
{
int num;
fscanf(fp,"%d", &num);
fread(p, sizeof(student), size, fp);
for(int i=0; i<size; i++)
printf("%f", p[i].score);
}
You are opening a file named "FILENAME", not "students.txt", which is why your command line is remaining blank. Change
#define FILENAME students.txt
into
#define FILENAME "students.txt"
and
fopen("FILENAME", "rb");
into
fopen(FILENAME, "r");
Furthermore, fread requires that the caller knows the exact size of the elements they are reading, but since the student names have a variable length, this is not the case. You should use fgets instead. You can read more about fgets here.
fread should only be used in the case of binary input/output, usually memory blocks (arrays or structs) that have a size known to the caller.
I have a binary file which contains 3 differents structs and a christmas text. On the first line of the binaryfile have they provided me with a int which represents the size of a package inside the file. A package contains 3 structs ,the chistmastext and the size.
The structs lies in a file called framehdr.h and the binary file I'm reading is called TCPdump.
Now am I trying to create a program att will read each package at a time and then withdraw the text.
I have started with something like this:
#pragma warning(disable: 4996)
#include <stdio.h>
#include <stdlib.h>
#include "framehdr.h"
#include <crtdbg.h>
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
FILE *fileOpen;
char *buffer;
size_t dataInFile;
long filesize;
// The three structs
struct ethernet_hdr ethHdr;
struct ip_hdr ipHdr;
struct tcp_hdr tcpHDr;
fileOpen = fopen("C:\\Users\\Viktor\\source\\repos\\Laboration_3\\Laboration_3\\TCPdump", "rb");
if (fileOpen == NULL)
{
printf("Error\n");
}
else
{
printf("Success\n");
}
char lenOf[10];
size_t nr;
// Reads until \n comes
fgets(lenOf, sizeof(lenOf), fileOpen);
sscanf(lenOf, "%d", &nr);
// Withdraw the size of a package and check if it's correct
printf("Value: %d\n", nr);
printf("Adress: %d\n", &nr);
void *ptr;
fread(&ptr, nr, 1, fileOpen);
int resEth = 14;
printf("resEth: %d\n", resEth);
int resIP = IP_HL((struct ip_hdr*)ptr);
printf("ResIP: %d\n", resIP);
int resTcp = TH_OFF((struct tcp_hdr*)ptr);
printf("tcpIP: %d\n", resTcp);
int res = resEth + resIP + resTcp;
printf("Total: %d", res);
fclose(fileOpen);
//free(buffer);
system("pause");
return 0;
}
I know that the first struct ethernet will always have the size of 14 but I need to get the size of the other 2 and I'm suppose to use IP_HL and TH_OFF for that.
But my problems lies in that I can't seem to read the entire package to one
void * with the fread. I get noting in my *ptr.
Which in turn makes the code break when I try to convert the void * to one of the structs ones.
What I'm doing wrong with the void *?
Two problems:
First you should not really use text functions when reading binary files. Binary files doesn't really have "lines" in the sense that text file have it.
Secondly, with
void *ptr;
fread(&ptr, nr, 1, fileOpen);
you are passing a pointer to the pointer variable, you don't actually read anything into memory and then make ptr point to that memory. What happens now is that the fread function will read nr bytes from the file, and then write it to the memory pointed to by &ptr, which will lead to undefined behavior if nr > sizeof ptr (as then the data will be written out of bounds).
You have to allocate nr bytes of memory, and then pass a pointer to the first element of that:
char data[nr];
fread(data, nr, 1, fileOpen);
You should also get into the habit of checking for errors. What if the fread function fails? Or the file is truncated and there isn't nr bytes left to read?
You can check for these conditions by checking what fread returns.
And not only check for fread, there are more functions than fopen that can fail.
I am trying to write a program that reads data from a file and puts it into a struct array. I have succeeded in putting it into the array but I then get a segmentation fault. I believe I have to use malloc to initialize the struct array but I am new to programming and don't really understand how to do this. Thanks for your help! I have copied pieces of my code so you can see what I've done. I have not included my functions in this code.
struct Weather
{
char location;
int daynum;
double temp;
double precip;
};
int main (void)
{
FILE*openFile;
char buffer[COLS][ROWS];
int i = 0;
struct Weather loc1; //initialize here?
for (i = 0; i <200; i++)
{
fgets (buffer[i], ROWS, openFile);
parseLine(buffer[i], &loc1);
printf ("%d %c %d %.2lf %.2lf\n",i, loc1.location, loc1.daynum, loc1.temp, loc1.precip);
}
}
Your file stream (openFile) is not initialized; it does not actually point to a file. As yano said in his comment, use fopen() in order to properly initialize the file pointer.
You must initialize the file stream with fopen() before any I/O operations!
int main()
{
char filename[64];
FILE *fp;
snprintf(filename, sizeof(filename), "hello1234.txt");
if(NULL == (fp = fopen(filename, "r")))
{
printf("err, failed when fopen(), %s %s[%d].\n", __FILE__, __FUNCTION__, __LINE__);
return -1;
}
//your code here
return 0;
}
Initialize the struct
Note that malloc() cannot initialize the struct.
two methods:
M0:
struct Weather loc1;
memset(&loc1, 0, sizeof(struct Weather));
M1:
struct Weather loc1 = {0};
man malloc or click the link for a malloc manual.
Multiple problems in your code:
The stream pointer openFile is uninitialized, calling fgets() for it invokes undefined behavior. You want to open a file for fopen() or set the value of openFile to the standard input stream stdin.
The 2D char array should be defined in the other order:
char buffer[ROWS][COLS];
you should use the same constant for the loop counter and the 2D array definition: ROWS might be defined to something less than 200.
the size of the line buffer is COLS, pass that to fgets().
you should test the return value of fgets(): it returns NULL at end of file and the contents of the destination array is indeterminate in this case.
whether or not to initialize loc1 depends on what the parseLine() function does. It would make sense that parseLine() make no assumptions about the contents of the destination structure, but the source has not been posted, so we cannot know for sure.
the printf format specifier for type double is %f, the extra l is simply ignored.
So, I have some code that reads a file line by line.
Here is that code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct alumnus {
int *yearGraduated;
char firstName[30];
char lastName[30];
} Alumns;
void printer ( Alumns *a ) {
printf("Year: %*d", a->yearGraduated);
printf(" First Name: %s", a->firstName);
printf(" Last Name: %s", a->lastName);
}
int main(int argc, const char * argv[])
{
Alumns a;
char *home = getenv("HOME");
char path[100] = "/Desktop/Alumni.txt";
strcat(home, path);
FILE *fp;
fp = fopen(home, "r");
while ( fp ) {
fscanf(fp, "%d,%s,%s", a.yearGraduated, a.firstName, a.lastName);
printer(&a);
}
return 0;
}
I am getting an error # line 2 in the printer function
printf("Year: %d", a->yearGraduated);
My IDE says the following about this line:
Format Specifies type 'int' but the argument has type 'int *'
Another error only appears when I compile the code. Error is at line:
a.yearGraduated, a.firstName, a.lastName);
My IDE says this about it:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)
Yes, the file IS existent, and yes, it is formatted the right way.
Any ideas on how to debug this?
I was recommended to make the int inside the structure not a pointer, but I'm not sure.
yearGraduated is a pointer. You need to allocate space for it and dereference it in the printf statement.
So...
a.yearGraduated = malloc(sizeof(int));
fscanf(fp, "%d,%s,%s", a.yearGraduated, a.firstName, a.lastName);
free(a.yearGraduated);
and in the function
printf("Year: %d", *a->yearGraduated);
or you could make the int in the structure not a pointer and it will not be an issue.
Regarding the first error: all the information you need is in the error message. You provide a pointer to an int where printf expects an int. Unless you have a reason to make yearGraduated a pointer you should change it to an int:
typedef struct alumnus{
int yearGraduated;
...
} Alumns;
If you do this you need to change the first argument to fscanf to pass the address of a.yearGraduated: &a.yearGraduated.
If you choose to keep it a pointer you must allocate it:
Alumns a;
a.yearGraduated = malloc(sizeof a.yearGraduated);
Then in the printer you must dereference it to printf:
printf("Year: %*d", *a->yearGraduated);
The reason the code crashes at the fscanf is because the memory at a.yearGraduated was not allocated so fscanf tries to write to unallocated memory, which is a bad idea.
ADD: (in response to your comment)
The reason the program loops forever is because while(fp) will loop forever (unless fopen fails, in which case it won't loop at all). I'm assuming your reasoning is that fp will become false when the end of the stream is reached. But fp is just a (pointer-)value and fscanf won't (can't) change it. To stop the loop when the end of the stream is reached use feof
My Function That writes to the file:
Record_t * load_Record(FILE * infile)
{
Record_t *r;
char title[TITLE_SIZE];
char [MEDIUM_SIZE];
int ID, rating;
if ((fscanf(infile,"%d: %s %d", &ID, medium, &rating) != 3 && fgets(title, TITLE_SIZE, infile))){
return NULL;
}
printf("medium is: %s title is: %s\n", medium, title);
r = create_Record(medium, title);
set_Record_rating(r, rating);
return r;
}
where Record_t is defined as:
typedef struct Record {
int ID;
char * title;
char * medium;
int rating;
} Record_t;
My main:
#include "Record.h"
#include <stdlib.h>
int main()
{
char * title = "The Queen";
char * medium = "DVD";
FILE * pfile ;
struct Record *t = create_Record(medium, title); //creates a record
struct Record *s;
set_Record_rating (t, 3);
print_Record(t);
pfile = fopen("output.txt", "w");
save_Record(t, pfile);
fclose(pfile);
destroy_Record(t); //de-allocates memory
pfile = fopen("output.txt", "r");
if(!(s = load_Record(pfile))){
return 1;
}
print_Record(s);
fclose(pfile);
destroy_Record(s);
return 0;
}
output.txt after being written to file:
1: DVD 3 The Queen //checked for excess whitespace(has newline however)
Terminal output:
1: The Queen DVD 3
medium is: DVD title is: � //title being saved inappropriately
#
2: �
# DVD 3
now my fgets function is wrong! For some reason, the title is being saved inappropriately
i am compiling with the following flags:
gcc -ansi -std=c89 -pedantic -Wmissing-prototypes -Wall test.c Record.c -o test
where test.c is my main
char * medium;
This should be
char medium[SOME_CONSTANT]; // or char* medium = malloc(x); if you need the
// memory to persist after the function returns
So that you actually have medium pointing to some memory you own. As it is now, your pointer is pointing to garbage and you're expecting fscanf to save a string in the memory it points to.
If a function ever appears to return a pointer to some magically created memory, you better check the documentation twice (unless that function happens to be the stupid strdup). The function either actually expects a pointer to some already-allocated memory, or returns a pointer that points to a block of memory allocated with someone from malloc's family, in which case you need to take responsibility for deallocating it.
Only in very rare circumstances do functions return a pointer to memory without taking a preallocated buffer in and without having mallocd it (especially when the string that is returned is of unpredictable size like it is for fscanf).
You've not allocated a buffer for medium:
char * medium;
That just creates a pointer to a char called medium, you've not reserved any memory space to read into. This would allocate 256 bytes (allowing you to read up to 256 characters) for medium:
medium = malloc(256);
Or you can allocate on the stack:
char medium[256];
Given the issues you're having, I would suggest using allocation on the stack, then just read and write the struct to the file — it saves you from having to parse the fields yourself etc. at expense of disk space (you could be writing out a lot of blank characters) but this wastage would be negligible in this scenario.
fwrite(t, sizeof(Record_t), 1, pFile);
fread(t, sizeof(Record_t), 1, pFile);
Several ways:
You've not allocated the space for it to read the string into. You need:
char medium[100];
You don't check for errors properly:
if (!(fscanf(infile,"%d: %s %d", &ID, medium, &rating)
should be:
if (fscanf(infile,"%d: %s %d", &ID, medium, &rating) != 3 ...
You need to explicitly test that you got all the values you expected to read.
That's scratching the surface without a deep analysis of all the code. Note that you'll need to be sure that you are not trying to return medium to the calling code. This should be OK if create_record() does a reasonable job. It is odd that create_record() isn't told the record ID.