I wrote a code for breadth first search in C using a compressed sparse row data structure. The code seems to work well for one graph but returns an error for another graph file. It works well for this file but throws an error for this file Being new to C programming, I can't find the cause of the problem and will appreciate any help
I tried checking the condition for the while loop. The condition for the loop is true when the code hangs up and returns an error. I am running this code on CodeBlocks 16.01 with the mingw compiler.
#include <stdio.h>
#include <stdlib.h>
// Declaring a struct type to hold
int main(){
int n, m, counter, current, x, src, dst;
n=0, m=0, counter = 0, current =0, src = 0, dst = 0;
FILE *fp; //create a pointer to the file directory
fp = fopen("filename.graph","r"); //set the directory pointer to the path of the text file containing graph data
if ((fp == NULL)){ perror("Error, no such file exists \n"); exit(1);}
//If file not found, print error message and exit the program
else
{
fscanf(fp,"%d %d", &n,&m); //read first line of text file to get number of vertices and edges in graph
struct CSRgraph //Create CSR data structure
{
int heads[m]; //Stores heads of edges
int offsets[n+1]; //Stores information on the number of edges leaving each node
};
struct CSRgraph g; //Create an instance of the CSR graph data structure
g.offsets[0] = 0; //Set the initial offset value to 0
g.offsets[n] = m; //Set the offset value of 'phantom' node to the number of edges in graph
for(x=0;x<m;x++) //iterate over all lines containing edge information in text file
//Read file and create CSR data structure from information in text file
{
fscanf(fp,"%d %d",&src,&dst); //read source and head information from file
g.heads[x] = dst; //assign head information to the next available slot in data structure
if (src < n+1) //Check that node is valid
{
if (src == current) //check that current edge originates from same source as previous edge
{
counter++; //increment counter for the number of edges that originate from current source
}
else //Current edge does not originate from previous source. New source node encountered
{
g.offsets[src] = counter + g.offsets[src -1]; //Update offset value for previous source
counter = 1; //restart edge origin counter
current = src ; //set current to current source
}
}
}
fclose(fp); //Close file after use
int Discovered[n],Queue[n+1],Explored[n], *front_ptr,*end_ptr,*exp_ptr;
front_ptr = Queue; //Initialize the front pointer to the Queue array
end_ptr = Queue; //Initialize the end pointer to the Queue array
exp_ptr = Explored; //Initialize the explored pointer to the Explored array
for (x=0;x<n;x++)
{
Discovered[x] = 0; //An array to track discovered nodes. Not necessarily explored, but nodes that have showed up previously
}
// Advance the pointers in the direction you want
*end_ptr = 0; //setting the first element in the queue as the node 0
end_ptr++; //advancing the end pointer to the next available array spot
Discovered[0] = 1;
while (front_ptr != end_ptr)
{ //Queue is empty if front pointer is the same as end pointer
int p,curr;
curr = *front_ptr; //grab the front of the queue and set it as current node
front_ptr++; //equivalent to removing from element and pushing the next node in line to the front
*exp_ptr = curr; //set current node to explored
exp_ptr++; //advance the explored pointer one step
for (p = g.offsets[curr]; p < g.offsets[curr+1]; p++)
//iterate over all neighbors of current node
{
if (Discovered[g.heads[p]] == 0)
//if node is not already discovered, set it to discovered, add it to queue and advanced the end pointer of queue one step
{
Discovered[g.heads[p]] = 1;
*end_ptr = g.heads[p];
end_ptr++;
}
}
}
}
return 0;
}
Your use of int heads[m]; as a VLA of int with m = 108744 (along with the other 4 VLAs you allocate of 2950 integers) is likely causing a StackOverflow... (this will be compiler, OS and memory-model dependent). To fix the problem, change CSRgraph so that members heads and offsets are pointers and then dynamically allocate storage based on the numbers read from the first line of the file, e.g.
typedef struct { /* typdef for convenience */
int *heads,
*offsets;
} CSRgraph_t;
int main (int argc, char **argv) {
CSRgraph_t g; /* declare instance of struct */
...
/* allocate/validate g.heads & g.offsets */
if (!(g.heads = malloc (m * sizeof *g.heads))) {
perror ("malloc-g.heads");
return 1;
}
/* calloc used to zero g.offsets */
if (!(g.offsets = calloc ((n + 1), sizeof *g.offsets))) {
perror ("malloc-g.offsets");
return 1;
}
g.offsets[0] = 0;
g.offsets[n] = m;
...
(note: calloc is used on offsets to initialize it to all zero as you later compare p < g.offsets[curr+1])
Putting it altogether, you could do:
#include <stdio.h>
#include <stdlib.h>
typedef struct { /* typdef for convenience */
int *heads,
*offsets;
} CSRgraph_t;
int main (int argc, char **argv) {
CSRgraph_t g; /* declare instance of struct */
int n, m, counter, current;
n = m = counter = current = 0;
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
if (fscanf (fp, "%d %d", &n, &m) != 2) { /* validate EVERY read */
fputs ("error: invalid format n, m.\n", stderr);
return 1;
}
/* allocate/validate g.heads & g.offsets */
if (!(g.heads = malloc (m * sizeof *g.heads))) {
perror ("malloc-g.heads");
return 1;
}
/* calloc used to zero g.offsets */
if (!(g.offsets = calloc ((n + 1), sizeof *g.offsets))) {
perror ("malloc-g.offsets");
return 1;
}
g.offsets[0] = 0;
g.offsets[n] = m;
for (int x = 0; x < m; x++) {
int src; /* src is only needed within scope of loop */
if (fscanf (fp, "%d %d", &src, &g.heads[x]) != 2) {
fprintf (stderr, "error: invalid format - line %d.\n", x);
}
if (src < n+1) {
if (src == current)
counter++;
}
else {
g.offsets[src] = counter + g.offsets[src -1];
counter = 1; /* restart edge origin counter */
current = src; /* set current to current source */
}
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
int Discovered[n], Queue[n+1], Explored[n],
*front_ptr, *end_ptr, *exp_ptr;
front_ptr = Queue; /* front pointer to the Queue array */
end_ptr = Queue; /* end pointer to the Queue array */
exp_ptr = Explored; /* explored pointer to the Explored array */
for (int x = 0; x < n; x++)
Discovered[x] = 0;
/* Advance the pointers in the direction you want */
*end_ptr = 0;
end_ptr++;
Discovered[0] = 1;
while (front_ptr != end_ptr) {
int curr = *front_ptr;
front_ptr++;
*exp_ptr = curr;
exp_ptr++;
for (int p = g.offsets[curr]; p < g.offsets[curr+1]; p++) {
if (Discovered[g.heads[p]] == 0) {
Discovered[g.heads[p]] = 1;
*end_ptr = g.heads[p];
end_ptr++;
}
}
}
free (g.heads);
free (g.offsets);
return 0;
}
(note: avoid hardcoding filenames, e.g. fopen("filename.graph","r"), main() takes arguments, use them to pass the filename as the first argument to your program [and you can read from stdin by default if no argument is provided])
Memory Use/Error Check
$ valgrind ./bin/readgraphsfile ~/tmp/graphfile/large.graph
==17345== Memcheck, a memory error detector
==17345== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==17345== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==17345== Command: ./bin/readgraphsfile ~/tmp/graphfile/large.graph
==17345==
==17345==
==17345== HEAP SUMMARY:
==17345== in use at exit: 0 bytes in 0 blocks
==17345== total heap usage: 3 allocs, 3 frees, 447,332 bytes allocated
==17345==
==17345== All heap blocks were freed -- no leaks are possible
==17345==
==17345== For counts of detected and suppressed errors, rerun with: -v
==17345== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
It works fine with your large.graph file.
Related
Hey :) I need some help with my code, which I think is mostly correct but I am having trouble figuring out where I am going wrong.
#include <stdio.h>
#include <stdlib.h>
int num_count(FILE* ptr){
int count = 0;
int numHolder = 0;
while((fscanf(ptr, "%d", &numHolder)) == 1){
count++;
}
return count;
}
void load_nums(FILE* ptr, int *codedPtr, int ncount){
int number = 0;
ncount = ncount - 1;
for(int i = 0; i <= ncount; i++){
fscanf(ptr, "%d", &number);
printf("%d", number);
*(codedPtr + i) = number;
}
return;
}
void decode(int *codedPtr, int ncount, char *decodedPtr){
char temp;
ncount = ncount - 1;
for(int i = 0; i <= ncount; i++){
temp = ((*(codedPtr + i) + *(codedPtr + (ncount - i))) + '0');
*decodedPtr = temp;
decodedPtr++;
}
return;
}
int main(int argc, char *argv[]){
int *codedPtr;
char *decodedPtr;
FILE *fp;
if (argc == 2){
fp = fopen(argv[1], "r+");
}
if(argc <= 1){
printf("Invalid command line: cmd infile outfile\n");
}
int numCount = num_count(fp);
printf("%d", *codedPtr);
codedPtr = (int*)calloc(numCount, sizeof(int));
decodedPtr = (char*)calloc(numCount, sizeof(char));
load_nums(fp, codedPtr, numCount);
decode(codedPtr, numCount, decodedPtr);
printf("\n%s\n\n", decodedPtr);
fclose(fp);
return(0);
}
I added some print functions to trouble shoot, and during the load_nums function the printf functions continuously prints 0's, it is not reading in the correct integer values from the file pointed to.
Could any of you help particularly with the load_nums function? Thank you all and let me know if you need any extra information. "-6 -76 53 -34 32 79 142 55 177 78" is what is in the file pointed to.
You are making things much more complicated than they need to be. You are dynamically allocating storage for both codedPtr and decodedPtr, there is no need to make two passes through the file (one to count integers, and one to read after allocation). Your decode is much more complex than necessary and there is a logic error. Adding '0' (it's not necessary in this case -- though normally it is to convert a decimal digit to its ASCII character value)
To address load_nums, change the return type to int * and allocate for codedPtr within load_nums using realloc as needed to increase the size of your allocated block of memory. Then return a pointer to the allocated block of memory holding your int values. Pass ncount as a pointer (e.g. int *ncount) so you can update the value at that address with the number of integers read so that the count is available back in the calling function (main() here).
Approaching allocation in this manner reduces your file I/O to a single-pass through the file (and file I/O is one of the most time consuming operations) Further, you completely eliminate the need for a num_count() function.
Putting those pieces together, you could do:
/* read integers from fp, dynamically allocating storage as needed,
* return pointer to allocated block holding integers and make ncount
* available through update pointer value.
*/
int *load_nums (FILE* fp, int *ncount)
{
int *codedPtr, avail = 2; /* declare pointer & no. to track allocated ints */
*ncount = 0; /* zero the value at ncount */
/* allocate avail no. of int to codedPtr - validate EVERY allocation */
if (!(codedPtr = malloc (avail * sizeof *codedPtr))) {
perror ("malloc-codedPtr");
return NULL;
}
while (fscanf (fp, "%d", &codedPtr[*ncount]) == 1) { /* read each int */
if (++(*ncount) == avail) { /* check if realloc needed (count == avail) */
/* always realloc to a temporary pointer */
void *tmp = realloc (codedPtr, 2 * avail * sizeof *codedPtr);
if (!tmp) { /* validate that realloc succeeds */
perror ("realloc-codedPtr");
return codedPtr; /* original codedPtr vals available on failure */
}
codedPtr = tmp; /* assign new block of mem to codedPtr */
avail *= 2; /* update avail with no. of int allocated */
}
}
return codedPtr; /* return pointer to allocated block of memory */
}
You would call the function in main() as, codedPtr = load_nums (fp, &numCount). You can wrap it in an if(...) statement to determine whether the allocation and read succeeded or failed:
int *codedPtr = NULL, numCount = 0;
...
if (!(codedPtr = load_nums (fp, &numCount))) /* read file/validate */
return 1;
(there is no need to pass codedPtr from main(). You can further validate by checking numCount > 0 -- that is left to you)
For your decode function, simply set up the for loop use two loop variables to iterate from the beginning and end towards the middle. This greatly simplifies things, e.g.
void decode (int *codedPtr, int ncount, char *decodedPtr)
{
/* loop from ends to middle adding values, + '0' NOT required */
for (int i = 0, j = ncount - i - 1; i < j; i++, j--)
decodedPtr[i] = codedPtr[i] + codedPtr[j];
}
(i starts at the first integer value and j at the last. Don't use *(codePtr + i) instead use codePtr[i] -- though equivalent, index notation is easier to read)
In main() you can alternatively open the file provided as the first argument to your program or read from stdin by default if no argument is provided (this is the way many Linux utilities work). Adding a simple ternary is all you need. Whether you are reading input or allocating memory (or using any function that is necessary for the continued correct operation of your code), you cannot use that function correctly unless you check the return to determine if the operation succeeded or failed. Lesson: validate, validate, validate....
Putting it altogether, you could do:
#include <stdio.h>
#include <stdlib.h>
/* read integers from fp, dynamically allocating storage as needed,
* return pointer to allocated block holding integers and make ncount
* available through update pointer value.
*/
int *load_nums (FILE* fp, int *ncount)
{
int *codedPtr, avail = 2; /* declare pointer & no. to track allocated ints */
*ncount = 0; /* zero the value at ncount */
/* allocate avail no. of int to codedPtr - validate EVERY allocation */
if (!(codedPtr = malloc (avail * sizeof *codedPtr))) {
perror ("malloc-codedPtr");
return NULL;
}
while (fscanf (fp, "%d", &codedPtr[*ncount]) == 1) { /* read each int */
if (++(*ncount) == avail) { /* check if realloc needed (count == avail) */
/* always realloc to a temporary pointer */
void *tmp = realloc (codedPtr, 2 * avail * sizeof *codedPtr);
if (!tmp) { /* validate that realloc succeeds */
perror ("realloc-codedPtr");
return codedPtr; /* original codedPtr vals available on failure */
}
codedPtr = tmp; /* assign new block of mem to codedPtr */
avail *= 2; /* update avail with no. of int allocated */
}
}
return codedPtr; /* return pointer to allocated block of memory */
}
void decode (int *codedPtr, int ncount, char *decodedPtr)
{
/* loop from ends to middle adding values, + '0' NOT required */
for (int i = 0, j = ncount - i - 1; i < j; i++, j--)
decodedPtr[i] = codedPtr[i] + codedPtr[j];
}
int main(int argc, char *argv[]) {
int *codedPtr = NULL, numCount = 0;
char *decodedPtr = NULL;
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
if (!(codedPtr = load_nums (fp, &numCount))) /* read file/validate */
return 1;
if (fp != stdin) /* close file if not stdin */
fclose (fp);
if (!(decodedPtr = malloc (numCount + 1))) { /* allocate/validate */
perror ("malloc-decodedPtr"); /* don't forget room for '\0' */
return 1;
}
decode (codedPtr, numCount, decodedPtr); /* decode the message */
decodedPtr[numCount] = 0; /* nul-terminate */
puts (decodedPtr); /* output decoded message */
free (codedPtr); /* don't forge to free what you allocate */
free (decodedPtr);
}
Example Use/Output
Testing your program, you find the decoded message is "Hello", e.g
$ echo "-6 -76 53 -34 32 79 142 55 177 78" | ./bin/codedptr
Hello
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ echo "-6 -76 53 -34 32 79 142 55 177 78" | valgrind ./bin/codedptr
==32184== Memcheck, a memory error detector
==32184== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==32184== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==32184== Command: ./bin/codedptr
==32184==
Hello
==32184==
==32184== HEAP SUMMARY:
==32184== in use at exit: 0 bytes in 0 blocks
==32184== total heap usage: 7 allocs, 7 frees, 5,251 bytes allocated
==32184==
==32184== All heap blocks were freed -- no leaks are possible
==32184==
==32184== For counts of detected and suppressed errors, rerun with: -v
==32184== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
I'm new in c and sorry for my poor English.
I'm trying to write a program that ask to user if he want enter data (region, date of detection, mm of rain) using keyboard and save it in file or if he want give it file's name.
No problem at this time and file is written or read correctly.
File have this structure:
Texas 03/03/2015 1
California 06/02/2013 5
Utah 03/01/2014 10
....
Try with scanf() (not report main because there is no problem in it.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef enum mese_e {Gen=1, Feb, Mar, Apr, Mag, Giu, Lug, Ago, Set, Ott, Nov, Dic} tipo_mese;
typedef struct data_s
{
int giorno;
tipo_mese mese;
int anno;
} tipo_data;
typedef struct dati_file_s
{
char* regione;
tipo_data data;
int mm_pioggia;
} tipo_dati_file;
typedef struct ritorna_s
{
tipo_dati_file* array;
int count;
} tipo_ritorna;
int conta_righe(char* Nome_f)
{
int i=0;
char c;
FILE* file;
file=fopen(Nome_f,"r");
while ((c=fgetc(file))!=EOF)
{if(c=='\n')
i++;}
fclose(file);
return i;
}
void crea_array (char* Nome_f)
{
int i,n;
char* regione= (char*)malloc(sizeof(char));
tipo_data data;
int mm_pioggia;
tipo_ritorna risultati;
FILE* file;
n = conta_righe(Nome_f);
printf("%d\n",n);
tipo_dati_file* array = (tipo_dati_file*) malloc (n*sizeof (tipo_dati_file));
file = fopen(Nome_f,"r");
if( file==NULL )
{
printf("Errore in apertura del file!");
exit(1);
}
for(i=0; i<=n; i++)
{
fscanf(file,"%s %d/%d/%d %d\n",regione, &data.giorno, &data.mese, &data.anno, &mm_pioggia);
strcpy(array[i].regione, regione);
array[i].data.giorno=data.giorno;
array[i].data.mese= data.mese;
array[i].data.anno= data.anno;
array[i].mm_pioggia= mm_pioggia;
printf("%s %d/%d/%d %d\n",array[i].regione,array[i].data.giorno, array[i].data.mese,array[i].data.anno,array[i].mm_pioggia);
}
fclose(file);
}
try with fgets()
#include <stdio.h>
#include <stdlib.h>
#include <string.h> typedef enum mese_e {Gen=1, Feb, Mar, Apr, Mag, Giu, Lug, Ago, Set, Ott, Nov, Dic} tipo_mese; typedef struct data_s {
int giorno;
tipo_mese mese;
int anno; } tipo_data;
typedef struct dati_file_s {
char* regione;
tipo_data data;
int mm_pioggia; } tipo_dati_file;
typedef struct ritorna_s {
tipo_dati_file* array;
int count; } tipo_ritorna;
int conta_righe(char* Nome_f) {
int i=0;
char c;
FILE* file;
file=fopen(Nome_f,"r");
while ((c=fgetc(file))!=EOF)
{if(c=='\n')
i++;}
fclose(file);
return i;
} void crea_array (char* Nome_f, int v) {
int i=0,s;
char* r;
//tipo_ritorna risultati;
FILE* file;
//n = conta_righe(file);
tipo_dati_file* array = (tipo_dati_file*) malloc (v*sizeof (tipo_dati_file));
file = fopen(Nome_f,"r");
if( file==NULL )
{
printf("Errore in apertura del file!");
exit(1);
}
if (feof(file)==0)
{
char* buf= (char*) malloc(v*sizeof(char));
/*while ( fgets( buf,10000, file) != NULL )
{
r = sscanf( buf, "%s% d/%d/%d %d\n", array[i].regione, &array[i].data.giorno, &array[i].data.mese, &array[i].data.anno, &array[i].mm_pioggia);
printf("%s %d/%d/%d %d\n", array[i].regione, array[i].data.giorno, array[i].data.mese, array[i].data.anno, array[i].mm_pioggia);
i++;
}*/
while(1)
{
r=fgets( buf,1000, file);
if (r!=NULL)
{
printf("%s",buf);
sscanf( buf, "%s% d/%d/%d %d\n", array[i].regione, &array[i].data.giorno, &array[i].data.mese, &array[i].data.anno, &array[i].mm_pioggia);
printf("%s %d/%d/%d %d\n", array[i].regione, array[i].data.giorno, array[i].data.mese, array[i].data.anno, array[i].mm_pioggia);
i++;
}
else exit(1);
}
}
else exit(1);
fclose(file); }
You have two primary problems I see in crea_array,
You declare char* r; but then attempt to assign the return of sscanf (buf, "%s %d/%d/%d %d\n", ... (e.g. r = sscanf (.... This is incorrect. sscanf returns type int representing the number of successful conversions that took place as specified in your format string (e.g. "%s %d/%d/%d %d\n" would return 5 on success, and remove the '\n', it will cause problems). Your compiler should be screaming warnings at you. If not, you need to enable compiler warnings by adding -Wall -Wextra -pedantic as compiler options and do not accept code until it compiles without a single warning.
crea_array must be declared as type tipo_dati_file * and it must return array; at the end. You must assign the return to a pointer back in the caller. You must also free (buf); before the return or you have just created a memory leak as there is no way to free() the memory you allocated for buf after the function returns. (further, if you are simply allocating 1000-char each time, just use a fixed buffer, e.g. char buf[1000]; and eliminate the need to allocate buf completely.
Putting it altogether, you could do something similar to:
#define MAXC 1000 /* if you need a constant, #define one (or more) */
tipo_dati_file *crea_array (char* Nome_f, int v)
{
int i = 0,
s,
r;
char buf[MAXC] = "";
//tipo_ritorna risultati;
FILE* file;
//n = conta_righe(file);
tipo_dati_file *array = malloc (v * sizeof *array);
file = fopen (Nome_f, "r");
if (file == NULL) {
printf ("Errore in apertura del file!");
exit (EXIT_FAILURE);
}
if (!array) { /* if you allocate, you must validate - every time */
perror ("malloc-array");
exit (EXIT_FAILURE);
}
while (fgets (buf, MAXC, file) != NULL)
{
r = sscanf (buf, "%s %d/%d/%d %d", array[i].regione,
&array[i].data.giorno, &array[i].data.mese,
&array[i].data.anno, &array[i].mm_pioggia);
if (r != 5) { /* validate return of every (s)scanf funciton */
fput ("error: failed to parse buf.\n", stderr);
continue; /* get next line */
}
printf ("%s %d/%d/%d %d\n", array[i].regione, array[i].data.giorno,
array[i].data.mese, array[i].data.anno, array[i].mm_pioggia);
i++;
}
fclose (file);
return array;
}
Note: I have not compiled the code above.
Then in main, you could do something similar to:
tipo_dati_file *array = crea_array (name, v);
(note: you should also pass a 3rd parameter of type int *numelem so you can assign *numelem = i; before return making the number of elements filled available back in the caller if less than v are actually read)
If you will post a A Minimal, Complete, and Verifiable Example (MCVE), along with a sample data file (10 lines or so), I'm happy to help further and I can validate the code works -- as I will have something I can compile and run.
Edit Following Warnings Posted (in comments)
The two warnings are addressed in detail in the comments below the answer. Once those are resolved, you will run into a horrible SegFault as you are not allocating storage for array[i].regione.
In you nested set of structs:
typedef struct dati_file_s {
char* regione;
tipo_data data;
int mm_pioggia;
} tipo_dati_file;
regione is an uninitialized pointer that points to some indeterminate memory location (which you do not own). When you attempt to write characters there with sscanf (BOOM - SegFault -- most likely).
You have two choices (1) declare regione as a fixed array, e.g. char regione[CONST] (inefficient), or (2) read the string for regione into a temporary buffer and then allocate storage for strlen + 1 chars and copy the string from the temporary buffer to the new block of memory and assign the starting address for that block to regione (you can use strlen/malloc/memcpy or strdup -- if you have it, it does all three)
With that fix and a few tweaks to your crea_array function (like passing a pointer to int to hold the number of struct filled in array instead of v), you could do something like the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
#define MAXDATA 64
typedef enum mese_e { Genv= 1, Feb, Mar, Apr, Mag, Giu,
Lug, Ago, Set, Ott, Nov, Dic
} tipo_mese;
typedef struct data_s {
int giorno;
tipo_mese mese;
int anno;
} tipo_data;
typedef struct dati_file_s {
char* regione;
tipo_data data;
int mm_pioggia;
} tipo_dati_file;
typedef struct ritorna_s {
tipo_dati_file* array;
int count;
} tipo_ritorna;
tipo_dati_file *crea_array (char *Nome_f, int *nelem)
{
int i = 0,
r;
char region[MAXC] = ""; /* temp buffer to hold array[i].regione */
char buf[MAXC] = "";
FILE* file;
tipo_dati_file *array = malloc (MAXDATA * sizeof *array);
file = fopen (Nome_f, "r");
if (file == NULL) {
printf ("Errore in apertura del file!");
return NULL;
}
if (!array) { /* if you allocate, you must validate - every time */
perror ("malloc-array");
return NULL;
}
while (fgets (buf, MAXC, file) != NULL)
{
r = sscanf (buf, "%s %d/%d/%d %d", region,
&array[i].data.giorno, (int*)&array[i].data.mese,
&array[i].data.anno, &array[i].mm_pioggia);
if (r != 5) { /* validate return of every (s)scanf funciton */
fputs ("error: failed to parse buf.\n", stderr);
continue; /* get next line */
}
array[i].regione = strdup (region);
if (!array[i].regione) { /* strdup allocates - you must validate */
perror ("strdup-array[i].regione");
for (int j = 0; j < i; j++) /* on failure free prior mem */
free (array[j].regione); /* and return NULL */
free (array);
return NULL;
}
i++;
}
fclose (file);
*nelem = i; /* update nelem with number of struct filled */
return array;
}
int main (int argc, char **argv) {
int index = 0,
nelem = 0;
char *datafile = argc > 1 ? argv[1] : "dat/staterain.txt";
tipo_ritorna statistics[MAXDATA] = {{ .array = NULL }};
statistics[index].array = crea_array (datafile, &nelem);
if (statistics[index].array && nelem > 0) {
statistics[index].count = nelem;
for (int i = 0; i < statistics[index].count; i++) {
printf ("%-12s %02d/%02d/%4d %3d\n",
statistics[index].array[i].regione,
statistics[index].array[i].data.giorno,
statistics[index].array[i].data.mese,
statistics[index].array[i].data.anno,
statistics[index].array[i].mm_pioggia);
free (statistics[index].array[i].regione); /* free strings */
}
free (statistics[index].array); /* free array */
}
return 0;
}
Example Use/Output
$ ./bin/staterain
Texas 03/03/2015 1
California 06/02/2013 5
Utah 03/01/2014 10
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/staterain
==3349== Memcheck, a memory error detector
==3349== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3349== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==3349== Command: ./bin/staterain
==3349==
Texas 03/03/2015 1
California 06/02/2013 5
Utah 03/01/2014 10
==3349==
==3349== HEAP SUMMARY:
==3349== in use at exit: 0 bytes in 0 blocks
==3349== total heap usage: 5 allocs, 5 frees, 2,110 bytes allocated
==3349==
==3349== All heap blocks were freed -- no leaks are possible
==3349==
==3349== For counts of detected and suppressed errors, rerun with: -v
==3349== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
I have a large C code that works fine on 32 bit machine but fails on 64 bit machines with
*** glibc detected *** ./XXX.exe: corrupted double-linked list: error.
I have run Valgrind and have used print statements to exactly pinpoint where the error is coming from but am clueless about the fix.
The actual code is very big but I am giving the relevant portions here. Hoping someone can help me troubleshoot it.
The actual error is coming from the Buf_Close() module where it tries to free Buf_elm_p p as if (p != NULL) free(p);
These are functions that the main code calls and I am only giving this here as the error is somewhere here. The calling sequence from the main code is:
1. Buf_Init
2. Buf_New_p
3. Buf_Close
Buf_bf_p
Buf_Init( size_t elm_size,
int nelm_buf,
long nbuf )
/*******************************************************************************
!C
!Description: Buf_Init (buffer initialize) initializes buffer data structures
and buffers.
!Input Parameters:
elm_size element size (bytes)
nelm_buf number of elements per buffer
nbuf number of buffers
!Output Parameters:
(returns) pointer to the buffer data structure
or NULL for an error return
*****************************************************************************/
{
Buf_bf_p bf;
long buf_size;
long ibuf;
/* Calculate buffer size and check */
buf_size = ((long) elm_size) * nelm_buf;
/*Allocate the buffer data structure */
if ((bf = (Buf_bf_p) malloc(sizeof(Buf_bf_t))) == NULL)
{Buf_Error(&BUF_NOMEMORY, "Init"); return NULL;}
bf->key = BUF_KEY;
bf->elm_size = elm_size;
bf->nelm_buf = nelm_buf;
bf->nbuf = nbuf;
bf->buf_size = buf_size;
bf->fp = NULL;
bf->access = NO_FILE;
bf->nbuf_alloc = 1;
bf->ibuf_end = 0;
bf->ibuf_newest = 0;
bf->ibuf_oldest = 0;
bf->nelm = 0;
/* Allocate the buffer status data structure */
bf->nbstat = max(NBSTAT_START, bf->nbuf + 1);
if ((bf->bstat = (Buf_bstat_t *)
malloc(bf->nbstat * sizeof(Buf_bstat_t))) == NULL)
{Buf_Error(&BUF_NOMEMORY, "Init"); return NULL;}
/* Allocate the first buffer */
bf->bstat[0].loc = MEM_ONLY;
if( (bf->bstat[0].buf_p = (Buf_elm_p) malloc(bf->buf_size)) == NULL)
{ Buf_Error(&BUF_NOMEMORY, "Init");
return NULL;
}
else
{
/* initialize */
memset( bf->bstat[0].buf_p, '\0', bf->buf_size );
}
bf->bstat[0].newer = -1;
bf->bstat[0].older = -1;
/* Initialize the rest of the buffer status array */
printf("bf->nbstat %d\n", bf->nbstat);
for (ibuf = 1; ibuf < bf->nbstat; ibuf++) {
bf->bstat[ibuf].loc = NOT_ALLOC;
bf->bstat[ibuf].buf_p = NULL;
bf->bstat[ibuf].newer = -1;
bf->bstat[ibuf].older = -1;
bf->bstat[ibuf].initialized = 1;
}
return bf;
}
Buf_elm_p
Buf_New_p( Buf_bf_p bf,
long *ielm )
/*******************************************************************************
!C
!Description: Buf_New_p (new buffer element pointer) returns a memory
location and element number of a new element; elements are number
sequentially as they are allocated.
!Input Parameters:
bf pointer to the buffer data structure
!Output Parameters:
ielm new element number
(returns) pointer to memory location of new element
or NULL for error
!Notes:
1. 'Buf_Init' must be called before this routine to initialize
the buffer data structure.
2. If there is no more space in memory and disk write access is allowed,
the oldest buffer is written to disk and the memory is re-used.
3. If the file is opened with 'read only' access this routine will return
an error.
!END
******************************************************************************/
{
long ibuf, jelm, jbuf, kbuf;
long nbuf_cmplt;
Buf_elm_p p;
long dsk_loc, eof_loc;
/* New element number/location */
*ielm = bf->nelm++;
ibuf = *ielm / bf->nelm_buf;
jelm = *ielm % bf->nelm_buf;
/* Are we at the past the end of the last buffer? */
if (ibuf > bf->ibuf_end) {
if (ibuf != (bf->ibuf_end + 1))
{Buf_Error(&BUF_BADBUF, "New_p"); return NULL;}
/* Re-allocate buffer status data structure if not large enough */
if( ibuf >= bf->nbstat )
{
bf->nbstat += min(bf->nbstat, MAX_NEW_NBSTAT);
if( (bf->bstat = realloc(bf->bstat, bf->nbstat * sizeof(Buf_bstat_t)))
== NULL)
{ Buf_Error(&BUF_NOMEMORY, "New_p");
return NULL;
}
}
if (bf->nbuf_alloc < bf->nbuf || bf->access == NO_FILE) {
/* Allocate a new buffer */
if( (p = (Buf_elm_p) malloc(bf->buf_size)) == NULL)
{ Buf_Error(&BUF_NOMEMORY, "New_p");
return NULL;
}
else
{
/* initialize */
memset( p, '\0', bf->buf_size );
}
bf->nbuf_alloc++;
if (bf->nbuf < bf->nbuf_alloc) bf->nbuf = bf->nbuf_alloc;
} else {
/* Re-use an existing buffer */
/* Get the oldest buffer */
jbuf = bf->ibuf_oldest;
/* Delete oldest buffer from old/new pointer list */
p = bf->bstat[jbuf].buf_p;
bf->ibuf_oldest = bf->bstat[jbuf].newer;
bf->bstat[bf->ibuf_oldest].older = -1;
bf->bstat[jbuf].buf_p = NULL;
bf->bstat[jbuf].older = -1;
bf->bstat[jbuf].newer = -1;
bf->bstat[jbuf].initialized = 1;
}
/* Put current buffer in old/new pointer list */
bf->bstat[ibuf].loc = MEM_ONLY;
bf->bstat[ibuf].buf_p = p;
bf->bstat[ibuf].older = bf->ibuf_newest;
bf->bstat[ibuf].newer = -1;
bf->bstat[ibuf].initialized = 1;
bf->ibuf_end = ibuf;
bf->bstat[bf->ibuf_newest].newer = ibuf;
bf->ibuf_newest = ibuf;
}
/* Calculate pointer to memory location of element */
p = (unsigned char *) bf->bstat[ibuf].buf_p + (jelm * bf->elm_size);
return p;
}
int
Buf_Close( Buf_bf_p bf )
/*******************************************************************************
!C
!Description: Buf_Close (buffer cache file close) writes the remainder of the
cache to the disk cache file and closes the file and frees memory of the
buffer data structure and buffers.
!Input Parameters:
bf pointer to the buffer data structure
Notes:
1. 'Buf_Create' or 'Buf_Open' must be called before this routine to open
the file.
!END
*****************************************************************************/
{
int i;
long dsk_loc;
logical_t cmplt_flag;
/* int b; */
Buf_elm_p p;
long ibuf, nelm_wrt;
int nb;
unsigned char header[HEADER_SIZE];
/* Write remaining buffers which are still only in memory */
for (ibuf = 0; ibuf < (bf->ibuf_end + 1); ibuf++)
/* for (ibuf = 0; ibuf < (bf->ibuf_end); ibuf++)*/{
p = bf->bstat[ibuf].buf_p;
/* Free the buffer memory */
**THIS FOLLOWING LINE IS WHERE THE ERROR IS COMING FROM**
**VALGRIND SHOWS `Conditional jump or move depends on uninitialised value(s)` ERROR**
**BUT AM NOT SURE HOW `p` is coming out to be uninitialized`**
if (p != NULL) free(p);
}
/* Free the buffer status memory */
free(bf->bstat);
/* Free the buffer cache data structure */
bf->fp = (FILE *)NULL;
bf->key = 0;
free(bf);
printf("buf here 5\n");
return BUF_NORMAL;
}
I work on a project that has a lot of questionable practices (currently working toward cleaning them up). I ran into this error and went through everything I could imagine to ferret out where the problem was including clang sanitizers, a variety of valgrind tools, and a variety of other tricks.
The problem: exit() was being called in one thread about the same time as main() returned, so all the global/static constructors were being kicked off in two separate threads simultaneously. I'm actually kind of annoyed I didn't make the connection sooner.
This error also manifests as:
double free or corruption
... which may also be caused by another problem
segfault/sig11 inside exit()
Crashes inside malloc_consolidate with a call stack that looks like:
I guess you can't add code examples after a bullet item
#0 0xabcdabcd in malloc_consolidate () from /lib/libc.so.6
#1 0xabcdabcd in _int_free () from /lib/libc.so.6
#2 0xabcdabcd in operator delete (...)
#3 0xabcdabcd in operator delete[] (...)
(...)
Furthermore, I couldn't get it to exhibit this problem while running under valgrind -- whether it was a timing issue, or some artifact of how valgrind works that hid the problem I may never know.
It bit difficult to understand your program logic by static analysis. However Valgrind print "Conditional jump or move depends on uninitialized value" under the scenario
where program attempts to make use of uninitialized data in a way that might affect your program's externally-visible behaviour.
Sources of uninitialised data tend to be:
Local variables in procedures which have not been initialized.
The contents of heap blocks (allocated with malloc, new, or a similar function) before you (or a constructor) write something there.
To see information on the sources of uninitialised data in your program, you may use
the option --track-origins=yes. So You can run the program(./a.out) as follows:
valgrind --tool=memcheck --track-origins=yes ./a.out
This might be helpful and provide more useful information closer to your actual source of your problem.You can find more detailed information about it from the location:
http://valgrind.org/docs/manual/mc-manual.html
I am trying to write a function to clean up the hash table that is generated by this code
/*
* Markov chain random text generator.
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "eprintf.h"
enum {
NPREF = 2, /* number of prefix words */
NHASH = 4093, /* size of state hash table array */
MAXGEN = 10000 /* maximum words generated */
};
typedef struct State State;
typedef struct Suffix Suffix;
struct State { /* prefix + suffix list */
char* pref[NPREF]; /* prefix words */
Suffix* suf; /* list of suffixes */
State* next; /* next in hash table */
};
struct Suffix { /* list of suffixes */
char* word; /* suffix */
Suffix* next; /* next in list of suffixes */
};
State* lookup(char *prefix[], int create);
void build(char *prefix[], FILE*);
void generate(int nwords);
void add(char *prefix[], char *word);
State* statetab[NHASH]; /* hash table of states */
char NONWORD[] = "\n"; /* cannot appear as real word */
/* markov main: markov-chain random text generation */
int main(void)
{
int i, nwords = MAXGEN;
char *prefix[NPREF]; /* current input prefix */
int c;
long seed;
setProgName("markov");
seed = time(NULL);
srand(seed);
for (i = 0; i < NPREF; i++) /* set up initial prefix */
prefix[i] = NONWORD;
build(prefix, stdin);
add(prefix, NONWORD);
generate(nwords);
return 0;
}
const int MULTIPLIER = 31; /* for hash() */
/* hash: compute hash value for array of NPREF strings */
unsigned int hash(char* s[NPREF])
{
unsigned int h;
unsigned char *p;
int i;
h = 0;
for (i = 0; i < NPREF; i++)
for (p = (unsigned char *) s[i]; *p != '\0'; p++)
h = MULTIPLIER * h + *p;
return h % NHASH;
}
/* lookup: search for prefix; create if requested. */
/* returns pointer if present or created; NULL if not. */
/* creation doesn't strdup so strings mustn't change later. */
State* lookup(char *prefix[NPREF], int create)
{
int i, h;
State *sp;
h = hash(prefix);
for (sp = statetab[h]; sp != NULL; sp = sp->next) {
for (i = 0; i < NPREF; i++)
if (strcmp(prefix[i], sp->pref[i]) != 0)
break;
if (i == NPREF) /* found it */
return sp;
}
if (create) {
sp = (State *) emalloc(sizeof(State));
for (i = 0; i < NPREF; i++)
sp->pref[i] = prefix[i];
sp->suf = NULL;
sp->next = statetab[h];
statetab[h] = sp;
}
return sp;
}
/* addsuffix: add to state. suffix must not change later */
void addsuffix(State *sp, char *suffix)
{
Suffix *suf;
suf = (Suffix *) emalloc(sizeof(Suffix));
suf->word = suffix;
suf->next = sp->suf;
sp->suf = suf;
}
/* add: add word to suffix list, update prefix */
void add(char *prefix[NPREF], char *suffix)
{
State *sp;
sp = lookup(prefix, 1); /* create if not found */
addsuffix(sp, suffix);
/* move the words down the prefix */
memmove(prefix, prefix+1, (NPREF-1)*sizeof(prefix[0]));
prefix[NPREF-1] = suffix;
}
/* build: read input, build prefix table */
void build(char *prefix[NPREF], FILE *f)
{
char buf[100], fmt[10];
/* create a format string; %s could overflow buf */
sprintf(fmt, "%%%ds", sizeof(buf)-1);
while (fscanf(f, fmt, buf) != EOF)
add(prefix, estrdup(buf));
}
/* generate: produce output, one word per line */
void generate(int nwords)
{
State *sp;
Suffix *suf;
char *prefix[NPREF], *w;
int i, nmatch;
for (i = 0; i < NPREF; i++) /* reset initial prefix */
prefix[i] = NONWORD;
for (i = 0; i < nwords; i++) {
sp = lookup(prefix, 0);
if (sp == NULL)
eprintf("internal error: lookup failed");
nmatch = 0;
for (suf = sp->suf; suf != NULL; suf = suf->next)
if (rand() % ++nmatch == 0) /* prob = 1/nmatch */
w = suf->word;
if (nmatch == 0)
eprintf("internal error: no suffix %d %s", i, prefix[0]);
if (strcmp(w, NONWORD) == 0)
break;
printf("%s\n", w);
memmove(prefix, prefix+1, (NPREF-1)*sizeof(prefix[0]));
prefix[NPREF-1] = w;
}
}
Here is what I have so far for my clean function
/*Clean Function*/
void clean_up(State *sp)
{
State *temp;
Suffix *temp2, temp3;
for(int h = 0; h < NHASH; h++)
{
for (sp = statetab[h]; sp != NULL; sp = sp->next)
{
while(sp->suf != NULL)
{
temp2= sp->suf;
temp3= *temp2->next;
free(temp2);
sp->suf= &temp3;
}
}
}
}
I think im on the right track, I'm going through each index in the hash table, then going from state to state and freeing the suffixes. I'm not sure what to do about the prefixes, because I have to free them before I can free each state. Any help would be greatly appreciated.
In your code, you are copying into a temp3 node, which lives in automatic memory ("on the stack") pointing sp->suf to this memory will (on the next iteration of the loop) cause free to be called with the address of this object (which has not been obtained by malloc, and thus cannot be freed by free() )
void clean_up(State *sp)
{
State *temp;
Suffix *temp2, **pp;
for(int h = 0; h < NHASH; h++)
{
for (sp = statetab[h]; sp != NULL; sp = sp->next)
{
for (pp = &sp->suf; *pp; *pp = temp2)
{
temp2 = (*pp)->next;
free(*pp);
}
}
}
}
The example code is derived from the Markov program in The Practice of Programming by Kernighan and Pike, a most excellent book.
Given that you are trying to clean up the statetab, the main clean-up function doesn't need any argument. You do have to be careful not to free the states directly in statetab, but you do need to release auxilliary states chained off statetab[i].next.
typedef struct State State;
typedef struct Suffix Suffix;
struct State { /* prefix + suffix list */
char* pref[NPREF]; /* prefix words */
Suffix* suf; /* list of suffixes */
State* next; /* next in hash table */
};
struct Suffix { /* list of suffixes */
char* word; /* suffix */
Suffix* next; /* next in list of suffixes */
};
State* statetab[NHASH]; /* hash table of states */
static void free_state(State *state);
static void free_suffix(Suffix *suffix);
static void cleanup(void)
{
for (int i = 0; i < NHASH; i++)
free_state(statetab[i]);
}
static void free_state(State *state)
{
if (state != 0)
{
for (int i = 0; i < NPREF; i++)
free(state->pref[i]);
free_suffix(state->suf);
if (state->next != 0)
{
free_state(state->next);
free(state->next);
}
}
}
static void free_suffix(Suffix *suffix)
{
if (suffix != 0)
{
free(suffix->word);
free_suffix(suffix->next);
free(suffix);
}
}
Do you see how I've designed the free_xxxx() code based on the design of the xxxx structure?
Caveat Lector: uncompiled code, much less tested code.
I dug up the code from the TPOP site, and tried to apply it. I made some fixes to the freeing code above (syntax error fixed, the null checks in free_state() and free_suffix()), but the code as a whole was not designed to allow the data to be freed.
There are a couple of problems. First, a few of the prefixes are not allocated (NONWORD). It might be possible to avoid releasing those by testing whether a prefix is NONWORD, but that's nasty. It might be possible to allocate those prefixes too (replace NONWORD by estrdup(NONWORD)). I think there's another place, somewhere, that a non-allocated pointer is being stashed in a prefix in the state table; I'm getting crashes in malloc() complaining of 'freeing non-allocated memory' (which is distinct from 'double freeing allocated memory', I believe), but I've not managed to resolve that.
However, that then changes to another problem; the prefixes are reused. That is, almost every prefix in the system is used as the the second word of one prefix, then as the first word of the next prefix. Thus, you can't readily free the prefixes.
If you were to design this so that the memory could be released, then you'd probably design it so that there was a system of 'atoms' (immutable strings) such that each word was allocated once and reused as often as necessary (see C Interfaces and Implementations: Techniques for Creating Reusable Code by D Hanson for the source of the term). The code freeing the state table would then concentrate only on the non-word data. There'd be code to release the complete set of atoms as well.
I ran the Markov program under valgrind without the cleanup; there are no memory access problems and no leaked data; it is all still accessible at program exit. I was using a data file of about 15,000 words (and about 2900 distinct words), and the statistics were:
==9610== HEAP SUMMARY:
==9610== in use at exit: 695,269 bytes in 39,567 blocks
==9610== total heap usage: 39,567 allocs, 0 frees, 695,269 bytes allocated
==9610==
==9610== LEAK SUMMARY:
==9610== definitely lost: 0 bytes in 0 blocks
==9610== indirectly lost: 0 bytes in 0 blocks
==9610== possibly lost: 0 bytes in 0 blocks
==9610== still reachable: 695,269 bytes in 39,567 blocks
So, you set yourself an interesting exercise. However, I think it is not achievable without reworking some of the memory allocation mechanism so that the data can be freed cleanly.
(On BSD, and hence on Mac OS X too, there are a pair of functions in <stdlib.h> called setprogname() and getprogname(). On BSD, setprogname() is called automatically before the main() gets going (with argv[0], I believe). The declaration in eprintf.h conflicts with the declaration in <stdlib.h>, which may be why the code in the question uses setProgName() instead of the original setprogname(). I chose to fix setprogname() in eprintf.h so that it took a const char * argument and therefore matched the declaration in <stdlib.h>.)
TPOP was previously at
http://plan9.bell-labs.com/cm/cs/tpop and
http://cm.bell-labs.com/cm/cs/tpop but both are now (2015-08-10) broken.
See also Wikipedia on TPOP.
Edited to include short description of what is expected from the code.
#include <sys/file.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#define MAX_PAGE 0xFF+1
/* page table entry you may need to add your own fields to it*/
typedef struct
{
unsigned short frame;/*location*/
unsigned int valid:1;
unsigned int in_mem:1;
unsigned int dirty:1;
unsigned int last_frame;
} pt_entry;
/* list entry for physical frames*/
struct list_item
{
unsigned short frame;
struct list_item *next;
struct list_item *prev;
int page_num;
};
typedef struct list_item *list;
void start_simulation(FILE *);
void resolve(int);
unsigned short find_frame(void);
unsigned short find_victim(void);
void display_stats(void);
void to_resident_set(list);
void free_mem(list);
void invalidate(unsigned short);
/*============================ header ends here ============================== *
/*#include "lru.h"*/
pt_entry pte[MAX_PAGE]; /* page table */
int mem_size; /* physical memory size in page frames */
list free_list_head; /* free list */
list res_set_head; /* resident set */
int total_fault = 0; /* total number of page faults */
int total_ref = 0; /* total number of memory references */
/* main program:
** read in paramters, and open the input file start the simulation */
int main(int argc, char *argv[])
{
FILE *stream;
if (argc != 3)
{
printf("The format is: pager file_name memory_size.\n");
exit(1);
}
printf("File used %s, resident set size %d\n", argv[1], atoi(argv[2]));
if ((stream = fopen(argv[1], "r")) == NULL)
{
perror("File open failed");
exit(1);
}
mem_size = atoi(argv[2]);
start_simulation(stream);
fclose(stream);
}
/*initialise the page table
** initialise the resident set, and the free list
** in the simulation loop
**16-bit memory addresses representing the program trace are read from the input
**file one by one the virtual address is resolved ie. physical frame for the
**virtual page identified
**the loop exits when it encounters the end of file
** free memory allocated for lists
** display statistics
*/
void start_simulation(FILE * stream)
{
char *addr_buf;
int address;
int i, n;
list new_entry, current;
/* initialise the page table */
for(i=0; i<MAX_PAGE;i++)
{
pte[i].frame = -1;
pte[i].valid = 0;
pte[i].dirty = 0;
pte[i].in_mem = 0;
}
/* initialise the resident set - empty*/
res_set_head = (list)malloc(sizeof(struct list_item));
res_set_head->next = res_set_head;
res_set_head->prev = res_set_head;
/* initialise free list - all physical pages*/
free_list_head = (list)malloc(sizeof(struct list_item));
free_list_head->next = free_list_head;
free_list_head->prev = free_list_head;
current = free_list_head;
for(i=0; i<mem_size;i++)
{
new_entry = (list)malloc(sizeof(struct list_item));
current->next = new_entry;
new_entry->prev = current;
new_entry->next = free_list_head;
new_entry->frame = i;
current = new_entry;
free_list_head->prev = current;
}
/* main simulation loop */
while( (n = fscanf(stream, "%x", &address)) != -1)
{
resolve(address);
total_ref++;
}
free_mem(free_list_head);
free_mem(res_set_head);
display_stats();
return;
}
/* resolve address reference
** if page table entry valid - do nothing
** if page table entry invalid - find a physical frame for this page
**and update pte for the page
*/
void resolve(int address)
{
unsigned short frame_alloc;
int virt_page;
static int disp_counter = 0;
virt_page = address >> 8;
if (pte[virt_page].valid == 1)
{
/*Was trying to implement */
//pte[virt_page].frame = pte[0];
}
else
{
frame_alloc = find_frame();
pte[virt_page].valid = 1;
pte[virt_page].frame = frame_alloc;
total_fault++;
}
}
/* find_frame:
** if free list is empty find a victim frame
** else detach the last frame of the free list and attach it
** to the resident set
** return frame number
*/
unsigned short find_frame()
{
unsigned short frame;
list current, new_tail;
if (free_list_head == free_list_head->prev) /* free list empty */
frame = find_victim();
else
{
new_tail = free_list_head->prev->prev;
new_tail->next = free_list_head;
current = free_list_head->prev;
free_list_head->prev = new_tail;
to_resident_set(current);
frame = current->frame;
}
return frame;
}
/* to_resident_set:
** attach a list entry at the end of resident set
*/
void to_resident_set(list current)
{
list tail;
tail = res_set_head->prev;
tail->next = current;
current->next = res_set_head;
current->prev = tail;
res_set_head->prev = current;
}
/* find_victim:
** As you can see I simply take the first page frame from the resident set list.
** This implements the FIFO replacement strategy. Your task is to replace it with
** a more efficient strategy.
*/
unsigned short find_victim()
{
int i;
unsigned short frame=0;
list current;
for(i=0;i<MAX_PAGE;i++)
{
if (pte[i].frame == frame && pte[i].valid == 1)
{
frame = res_set_head->next->frame;
invalidate(frame);
current = res_set_head->next;
res_set_head->next = current->next;
res_set_head->next->prev = res_set_head;
to_resident_set(current);
break;
}
}
return frame;
}
/* invalidate:
** invalidate the page table entry for the victim page */
void invalidate(unsigned short frame)
{
int i;
for(i=0;i<MAX_PAGE;i++)
{
if (pte[i].frame == frame && pte[i].valid == 1)
{
pte[i].valid = 0;
pte[i].frame = -1;
break;
}
}
}
/* display_stats:
** This is very basic, you may want to make it more sophisticated,
** for example save the data from multiple runs into a file for
** comparison etc
*/
void display_stats()
{
printf("\nProcess issued %d memory references\n", total_ref);
printf("Process triggered %d page faults\n", total_fault);
printf("Pafe fault rate is %d percent\n",((total_fault*100)/total_ref));
}
/* free memory allocated to the list */
void free_mem(list head)
{
list current,tail;
tail = head->prev;
current = head;
while (current->prev != tail)
{
current = current->next;
free(current->prev);
}
}
The most obvious problem lies in the input to your algorithm.
The restpage array is a global array and will thus be initialised to contain only the value 0. You then use these array elements as the page-numbers you are requesting, which means that your algorithm processes only requests for page 0 if mem_size < 100.
And if mem_size >= 100, you are overrunning the array bounds and land squarely in the land of undefined behaviour.
There are two fixes you need to make:
Just as you are checking for a valid file in the command-line arguments, you must also check that mem_size is not too large
Write an additional loop to give each element in restpage a random value, to ensure not all page requests are for the same page.
You have dimensioned restpage to [100] but mem_size seems freely configurable, is this the intent?
mem_size = atoi(argv[2]);
fclose(stream);
..
for(i=0;i<mem_size;i++)
{
totalabsence+=find_victim(&pt,restpage[i]);
}
EDIT:
I see one bug in your new code, in your find_victim you don't initialize the local variable 'frame'
EDITx2:
When you read from the file you may just want to put one hex address on each line
and use instead fgets() to read the file line by line (or load the whole file and
go through it line by line).