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.
Related
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 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.
I am pretty new to C and memory allocation in general. Basically what I am trying to do is copy the contents of an input file of unknown size and reverse it's contents using recursion. I feel that I am very close, but I keep getting a segmentation fault when I try to put in the contents of what I presume to be the reversed contents of the file (I presume because I think I am doing it right....)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int recursive_back(char **lines, int lineNumber, FILE *input) {
char *input_line = malloc(sizeof(char) * 1000);
lines = realloc(lines, (lineNumber) * 1000 * sizeof(char));
if(fgets(input_line, 201, input) == NULL) {
*(lines + lineNumber) = input_line;
return 1;
}
else {
printf("%d\n", lineNumber);
return (1+recursive_back(lines, ++lineNumber, input));
}
}
void backward (FILE *input, FILE *output, int debugflag ) {
int i;
char **lines; //store lines in here
lines = malloc(1000 * sizeof(char *) ); //1000 lines
if(lines == NULL) { //if malloc failed
fprintf(stderr, "malloc of lines failed\n");
exit(1);
}
int finalLineCount, lineCount;
finalLineCount = recursive_back(lines, 0, input);
printf("test %d\n", finalLineCount);
for(i = finalLineCount; i > 0; i--) {
fputs(*(lines+i), output); //segfault here
}
}
I am using a simple input file to test the code. My input file is 6 lines long that says "This is a test input file". The actual input files are being opened in another function and passed over to the backward function. I have verified that the other functions in my program work since I have been playing around with different options. These two functions are the only functions that I am having trouble with. What am I doing wrong?
Your problem is here:
lines = realloc(lines, (lineNumber) * 1000 * sizeof(char));
exactly as #ooga said. There are at least three separate things wrong with it:
You are reallocating the memory block pointed to by recursive_back()'s local variable lines, and storing the new address (supposing that the reallocation succeeds) back into that local variable. The new location is not necessarily the same as the old, but the only pointer to it is a local variable that goes out of scope at the end of recursive_back(). The caller's corresponding variable is not changed (including when the caller is recursive_back() itself), and therefore can no longer be relied upon to be a valid pointer after recursive_back() returns.
You allocate space using the wrong type. lines has type char **, so the object it points to has type char *, but you are reserving space based on the size of char instead.
You are not reserving enough space, at least on the first call, when lineNumber is zero. On that call, when the space requested is exactly zero bytes, the effect of the realloc() is to free the memory pointed to by lines. On subsequent calls, the space allocated is always one line's worth less than you think you are allocating.
It looks like the realloc() is altogether unnecessary if you can rely on the input to have at most 1000 lines, so you should consider just removing it. If you genuinely do need to be able to reallocate in a way that the caller will see, then the caller needs to pass a pointer to its variable, so that recursive_back() can modify it via that pointer.
Switching to C from Java, and I'm having some troubles grasping memory management
Say I have a function *check_malloc that behaves as such:
// Checks if malloc() succeeds.
void *check_malloc(size_t amount){
void *tpt;
/* Allocates a memory block in amount bytes. */
tpt = malloc( amount );
/* Checks if it was successful. */
if ( tpt == NULL ){
fprintf(stderr, "No memory of %lu bytes\n", amount);
exit(1);
}
return tpt;
}
I also have the following variables to work with:
FILE *f = fopen("abc.txt", "r"); // Pointer to a file with "mynameisbob" on the first line and
// "123456789" on the second line
char *pname; // Pointer to a string for storing the name
}
My goal is to use *check_malloc to dynamically allocate memory so that the String pointed to by *pname is just the correct size for storing "mynamisbob", which is the only thing on the first line of the text file.
Here is my (failed) attempt:
int main(int argc, char *argv[]){
FILE *f = fopen("abc.txt", "r"); // A file with "mynameisbob" on the first line and
// "123456789" on the second line
char *pname; // Pointer to a string for storing the name
char currentline[150]; // Char array for storing current line of file
while(!feof(f)){
fgets(currentline,100,f);
pname = ¤tline;
}
But I know this probably isn't the way to go about this, because I need to use my nice check_malloc* function.
Additionally, in my actual text file there is a "<" symbol before the name on the first line.But I just want the *pname to point to a String saying "mynameisbob" without the "<" symbol. This isn't that important now, it just is reinforcement to me that I know I can't just set the pointer to point straight to currentline.
Can anyone help me fix my thinking on this one? Thanks a lot.
In C you need to copy chars, not the "strings" (which are just pointers). Check out strcpy() and strlen(). Use strlen() to determine how long the line actually is which fgets has read, then use your malloc() to allocate exactly that (plus 1 for the 0). Then copy the chars over with strcpy().
There are several problems in your code, see my comments in this example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Checks if malloc() succeeds.
void *check_malloc (size_t amount) {
void *tpt;
/* Allocates a memory block in amount bytes. */
tpt = malloc( amount );
/* Checks if it was successful. */
if (tpt == NULL) {
fprintf (stderr, "No memory of %lu bytes\n", amount);
exit (EXIT_FAILURE);
}
return tpt;
}
// To avoid subtle errors I have defined buffer size here
#define BUFFER_SIZE 150
// I have used the (void) version of main () here, while not strictly neccessary, you where not using argc and argv anyway, so the can be left out in this case
int main (void) {
// It might be a good idea to make the filename a char[] as well, but I leave that as an exercise to the reader.
FILE *f = fopen("abc.txt", "r"); // A file with "mynameisbob" on the first line and
// "123456789" on the second line
// You have to check whether the file was *actually openend*
if (f == NULL) {
fprintf (stderr, "Could not open file abc.txt\n"); // '"...%s\n", filename);' might better.
exit (EXIT_FAILURE);
}
char *pname; // Pointer to a string for storing the name
char currentline[BUFFER_SIZE]; // Char array for storing current line of file
while(!feof (f)) {
char *res = fgets (currentline, BUFFER_SIZE, f);
// fgets returns NULL when EOF was encountered before the next '\n'
if (res) {
size_t read = strlen (res);
// The line might have been empty
if (read) {
// Better use "sizeof *varname", while char is always 1 byte it is a good practice
pname = check_malloc ((read + 1) * sizeof *pname); // + 1 because we have to provide an extra char für '\0'
strncpy (pname, currentline, read); // You have to use strcpy or strncpy to copy the contents of the string rather than just assigning the pointer
// What was allocated must be freed again
free (pname);
}
}
}
fclose(f); // Always close everything you open!
return EXIT_SUCCESS;
}
Actually you really don't have to use pname in this simple case, because currentline already contains the line, but since you're trying to learn about memory management this should give you a general idea of how things work.
In your code you had this line:
pname = ¤tline;
There are two problems here:
As already mentioned in my code assigning currentline to pname only copies the pointer not the contents.
The correct assignment would be pname = currentline (without the address operator &), because currentline is also a pointer under the hood (it behaves like char *currentline even though it's statically allocated).
VARIABLES AREN'T SET IN STONE YET! Excuse if if no indention. I am new to this site. Anyway, I have a text document of a list of games in five different categories, and I need to some help with memory allocation VIA typedef. How would one do it? So far, this is what I have:
/*
Example of text document
2012 DotA PC 0.00 10
2011 Gran Turismo 5 PS3 60.00 12
list continues in similar fashion...
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//function prototype here
char **readFile(char *file);
char *allocateString(char temp[]);
typedef struct
{
int year;
char name[100];
char system[10];
float price;
int players;
}game;
int main(void)
{
char **list;
system("pause");
return 0;
}
//function defined here
char **readFile(char *file) //reads file and and allocates
{
FILE* fpIn;
int i, total=0;
fpIn = fopen("list.txt", "r");
if (!fpIn)
{
printf("File does not exist");
exit(101);
}
/*
allocate memory by row here VIA for loop with the total++ to keep track of the
number of games
*/
/*
allocate memory individually for each item VIA "allocateString by using going
to set list[i] = allocateStrng(tmpList) using for loop the for loop will have
for (i=0; i<total; i++)
*/
return;
}
//allocateString here
char *allocateString(char temp[]);
{
char *s;
s = (char*)calloc(strlen(temp+1), sizeof(char)));
strcpy(s, temp);
return s;
}
Usually you'd allocate a decent amount of memory up front, detect situations where that amount is not enough, and enlarge the allocation in those cases using realloc (or malloc followed by memcpy and free). This advice holds for both the buffer into which you read the current line (to be passed as temp to allocateString) and the array to hold the sequence of all lines.
You can detect an insufficient buffer size for the line buffer when after calling fgets(buf, bufsize, fpIn) the strlen(buf) == bufsize - 1 but still buf[bufsize - 2] != '\n'. In other words, when reading filled the whole buffer, but still didn't reach a newline. In that case, the next read will continue the current line. You might want an inner loop to extend the buffer and read again for as long as it takes.
Note that your allocateString pretty much duplicates strdup, so you might want to use that instead.
The links in the above text mainly come from the manual of the GNU C library. cppreference.com is another good source of C function documentation. As are the Linux man pages.
s = (char*)calloc(strlen(temp+1), sizeof(char)));
//the name of the array is a pointer, so you are doing pointer arithmetic.
//I think you want strlen(*temp+1, sizeof(char)));
// or strlen(temmp[1]) it isn't clear if this is a pointer to a string or an array
// of strings
//you need the length of the string *temp is the content which temp points to
//strcpy(s, temp);