0

I want to read different values which are stored in a file in C using one-dimensional arrays. there are different types of data in the text and many records. each record is separated by an empty space and a record holds a name, a palate Number, a boolean type 1 or 0, cost and date. the structure of the file looks like this:

Hanif Hefaz
BA123HB
0
100.50
20180101

Jacki Shroff
UP673MK
1
3000.99
20170512

. . .

The problem is that I want to read each record from the file and print the names and palate Numbers of only those whose date is more than one year from now. if so I want to check the type this time. if the type is 1 then I want to print a new value, let's say amount along with the name and palate Number for that record in a way, that the cost is multiplied by 1.5 and if the type is 0 the cost should be multiplied by 2.5.

to describe this at short form:

read file
check if the date in the record is more than 1 year from now
if yes
check type
if type = 1
amount = cost * 1.5
else
if type = 0
amount = cost * 2.5
print name, palate Number, amount

Currently, I am able to print all the records in the file. but how to implement those conditions?

This is my Code:

#include <stdio.h>
#include <stdlib.h>

void FileRead(FILE *pr)
{
    fseek(pr, 0,0);
    while(1)
    {
    char name[100];
    if(fgets(name,100,pr) == NULL)
    {
        break;
    }
    printf("Name: : %s", name);
    char palateNumber [20];
    fgets(palateNumber,20,pr);
    printf("spz: %s", palateNumber);
    char type [2];
    fgets(type,20,pr);
    printf("type: %s", type);
    char cost [10];
    fgets(cost,10,pr);
    printf("cena: %s", cost);
    char date [15];
    fgets(date,15,pr);
    printf("date : %s", datum);
    fgets(name, 50, pr);
    printf("%s", name);
    }

}


int main()
{

   char x;
   FILE *pr;
   pr=fopen("file.txt","r");
   if(pr==NULL)
   {
       printf("File can not be opened.");
       return 0;
   }

  while(scanf("%c", &x))
    {
        switch(x)
        {

        case 'v' :
            FileRead(pr);
        break;

        }
    }
fclose(pr);
    return 0;
}

I also know to find the difference between the current date and the date in the file using this function, which I recently got from someone recently from here

time_t fill_broken_down_time (int y, int m, int d)
{                   /* initialize struct members */
    struct tm bdt = { .tm_sec=0, .tm_min=0, .tm_hour=0, .tm_mday=d,
                    .tm_mon=m>0?m-1:0, .tm_year=y-1900, .tm_isdst=-1 };

    return mktime(&bdt);    /* return mktime conversion to time_t */
}
        while (fgets (buf, MAXC, pr)) {     // read each line.

        if (sscanf (buf, "%4d%2d%2d", &y, &m, &d) != 3) {
            continue;
        }
        time_t  now = time(NULL),
        then = fill_broken_down_time (y, m, d);
        double secs = difftime (now, then);
        printf ("%g\n",secs / 86400.0);
        }
  • 1
    Found it -- give me a minute to digest it `:)` Have you talked about creating a structure in your class? Like `struct mydata { char name[20]; char label[20]; int multflag; double value; char date[9] };` ? That is a great way to coordinate different types of data as one unit. Let me know. – David C. Rankin Mar 15 '19 at 22:52
  • Well, I prefer to do it in one-dimensional arrays, without structs. I am not good at structs as well. can we do that using one-dimensional arrays? –  Mar 15 '19 at 22:59
  • 1
    `char type [2]; fgets(type,20,pr);` will break. 3 characters need to be placed in the array, not 2: the single digit, a newline, and a `NUL` terminator. The size `20` will let this happen, but there is only room for 2 characters. – Weather Vane Mar 15 '19 at 23:08
  • That is right, I even don't want 2 characters there, it should be one! 1 or 0 right? –  Mar 15 '19 at 23:20
  • 1
    `while(scanf("%c", &x))` should be `while(scanf("%c", &x) == 1)` or you won't detect the `EOF`. The `scanf` family's return value must always be compared with a *specific* value: the number of items that should be scanned. – Weather Vane Mar 15 '19 at 23:26

1 Answers1

2

Well, I got your comment, but let's try an array of struct to make your data handling manageable. This is no trivial addition to your last question. Trying to coordinate 5 separate arrays (3 of which would be 2D character arrays) poses more problems than it is worth. A struct is the proper way to coordinate the different types of information as one unit. Also, unless you have an undying need to use bool simply use int instead. The compiler can handle the native type int just as efficiently.

Let's start with your struct. A struct is nothing more than a convenient wrapper allowing you to collect different types and handle them as one object. In your case your character strings name, plate, date, your int type and your double value (your amount) can be the members of a struct. You can use a typedef to a struct to make its use more convenient, so instead of writing struct nameofstruct everywhere, you can simply use the typedeffed nameofstruct as you would an ordinary type, like int. For for example:

#define MAXS   16u  /* max number of structs */
#define MAXNM  32u  /* max characters in name and other arrays in struct */
#define MAXC 1024u  /* max characters in read buffer */
#define SECPY 31536000u /* seconds per-year */

typedef struct mydata {
    char name[MAXNM],
        plate[MAXNM],
        date[MAXNM];
    int type;
    double value;
} mydata_t;

Create a struct mydata with the typedef of mydata_t that is a reference (an alias) to struct mydata (you can actually omit the mydata and just typedef the anonymous struct).

Now you can create an array of struct mydata or simply of mydata_t and each element of the array is a struct that can hold each of those values.

Now let's start on your problem. As mentioned above, just declare an array of mydata_t instead of 5 separate arrays, e.g.

int main (int argc, char **argv) {
    ...
    int n = 0, ndx = 0; /* NOTE when dealing with array, start at ZERO */
    ...
    mydata_t data[MAXS] = {{ .name = "" }};

Now you have an array of 16 struct to work with (adjust your constant sizing the array as needed) and you have 2 counters n to keep track of which member in your struct your are currently reading, and ndx the index of which struct in the array you are filling.

Using them you can read each line like in your last question and use n to determine what to do with the line (you can use isspace() to check if the first character is whitespae (which includes a '\n') to skip empty-lines. You then just pass n to switch(n) to take the appropriate action for that line (or use a bunch of if ... else if ... else if ...) For example:

   while (fgets (buf, MAXC, fp)) {     /* read each line in file */
        if (isspace(*buf))  /* skip blank lines (or start with space) */
            continue;
        buf[strcspn (buf, "\r\n")] = 0; /* trim '\n' from end of buf */
        /* if line isn't a date line, just output line as non-date line */
        switch (n) {
            case 0: /* fill the name in struct */
            case 1: /* fill the plate in struct */
            case 2: /* set the type in struct */
            ...

(note: the buf[strcspn (buf, "\r\n")] = 0; is simply a handy way to trim the '\n' (or \r\n) from the end of buf before you copy it to name, plate, date, etc..)

In each of your cases, you simply VALIDATE that the string read with fgets will fit in the string member of your struct before you copy it or you convert the string to the numeric value you need, example:

            case 0: 
                if (strlen (buf) < MAXNM)
                    strcpy (data[ndx].name, buf);
                else {
                    fputs ("error: name exceeds storage.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;   /* advance n counter to act on next member */
                break;

or

            case 2:
                if (sscanf (buf, "%d", &data[ndx].type) != 1) {
                    fputs ("error: type not an integer.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;
                break;

(You use the '.' (dot) operator to access each member of a struct when dealing with the struct itself, or you use the '->' (arrow) operator if what you have is a pointer to struct. When you have an array of stuct the array index ([..]) acts as a dereference. Exactly the same as an array of anything else)

Since you don't store year, month, day, you simply need a function that checks the time from now so you can test whether it is more than a year. That way you simply pass your date to the function and get a time in seconds back as a double, e.g.

 double check_time_from_now (const char *str)
{
    int y, m, d;

    if (sscanf (str, "%4d%2d%2d", &y, &m, &d) != 3) {
        fprintf (stderr, "error non-date string: '%s'.\n", str);
        exit (EXIT_FAILURE);
    }

    time_t  now = time(NULL),
            then = fill_broken_down_time (y, m, d);
    double secs = difftime (now, then); /* get seconds between dates */

    return secs;
}

Another simply function allows you to print you array of struct, e.g.

void prn_data_t_array (mydata_t *data, int n)
{
    for (int i = 0; i < n; i++)
        printf ("%-12s %-8s  %d  %9.2f %s\n", data[i].name, data[i].plate,
                data[i].type, data[i].value, data[i].date);
}

With that we can look at the remainder of your switch(n) that shows how to handle each case:

    while (fgets (buf, MAXC, fp)) {     /* read each line in file */
        if (isspace(*buf))  /* skip blank lines (or start with space) */
            continue;
        buf[strcspn (buf, "\r\n")] = 0; /* trim '\n' from end of buf */
        /* if line isn't a date line, just output line as non-date line */
        switch (n) {
            case 0: 
                if (strlen (buf) < MAXNM)
                    strcpy (data[ndx].name, buf);
                else {
                    fputs ("error: name exceeds storage.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;
                break;
            case 1:
                if (strlen (buf) < MAXNM)
                    strcpy (data[ndx].plate, buf);
                else {
                    fputs ("error: plate exceeds storage.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;
                break;
            case 2:
                if (sscanf (buf, "%d", &data[ndx].type) != 1) {
                    fputs ("error: type not an integer.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;
                break;
            case 3:
                if (sscanf (buf, "%lf", &data[ndx].value) != 1) {
                    fputs ("error: value not a double.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;
                break;
            case 4: 
                if (strlen (buf) < MAXNM)
                    strcpy (data[ndx].date, buf);
                else {
                    fputs ("error: date exceeds storage.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                if (check_time_from_now (data[ndx].date) > SECPY) {
                    if (data[ndx].type)
                        data[ndx].value *= 1.5;
                    else
                        data[ndx].value *= 2.5;
                }
                n = 0;
                ndx++;
                if (ndx == MAXS)
                    goto arrayfull;
                break;
            default:
                fputs ("error: you shouldn't get here!\n", stderr);
                break;
        }
    }

(note: look closely at case 4: and how the line counter n is reset to zero and how your array index ndx is increment so you then fill the next struct in your array. Also note you protect your array bounds by checking if (ndx == MAXS) to make sure you don't write more structs than you have in the array.)

Putting it altogether you would have:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>

#define MAXS   16u  /* max number of structs */
#define MAXNM  32u  /* max characters in name and other arrays in struct */
#define MAXC 1024u  /* max characters in read buffer */
#define SECPY 31536000u /* seconds per-year */

typedef struct {
    char name[MAXNM],
        plate[MAXNM],
        date[MAXNM];
    int type;
    double value;
} mydata_t;

time_t fill_broken_down_time (int y, int m, int d)
{                   /* initialize struct members */
    struct tm bdt = { .tm_sec=0, .tm_min=0, .tm_hour=0, .tm_mday=d, 
                    .tm_mon=m>0?m-1:0, .tm_year=y-1900, .tm_isdst=-1 };

    return mktime(&bdt);    /* return mktime conversion to time_t */
}

double check_time_from_now (const char *str)
{
    int y, m, d;

    if (sscanf (str, "%4d%2d%2d", &y, &m, &d) != 3) {
        fprintf (stderr, "error non-date string: '%s'.\n", str);
        exit (EXIT_FAILURE);
    }

    time_t  now = time(NULL),
            then = fill_broken_down_time (y, m, d);
    double secs = difftime (now, then); /* get seconds between dates */

    return secs;
}

void prn_data_t_array (mydata_t *data, int n)
{
    for (int i = 0; i < n; i++)
        printf ("%-12s %-8s  %d  %9.2f %s\n", data[i].name, data[i].plate,
                data[i].type, data[i].value, data[i].date);
}

int main (int argc, char **argv) {
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
    int n = 0, ndx = 0; /* NOTE when dealing with array, start at ZERO */
    char buf[MAXC];     /* buffer to hold each line read from file */
    mydata_t data[MAXS] = {{ .name = "" }};

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    while (fgets (buf, MAXC, fp)) {     /* read each line in file */
        if (isspace(*buf))  /* skip blank lines (or start with space) */
            continue;
        buf[strcspn (buf, "\r\n")] = 0; /* trim '\n' from end of buf */
        /* if line isn't a date line, just output line as non-date line */
        switch (n) {
            case 0: 
                if (strlen (buf) < MAXNM)
                    strcpy (data[ndx].name, buf);
                else {
                    fputs ("error: name exceeds storage.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;
                break;
            case 1:
                if (strlen (buf) < MAXNM)
                    strcpy (data[ndx].plate, buf);
                else {
                    fputs ("error: plate exceeds storage.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;
                break;
            case 2:
                if (sscanf (buf, "%d", &data[ndx].type) != 1) {
                    fputs ("error: type not an integer.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;
                break;
            case 3:
                if (sscanf (buf, "%lf", &data[ndx].value) != 1) {
                    fputs ("error: value not a double.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;
                break;
            case 4: 
                if (strlen (buf) < MAXNM)
                    strcpy (data[ndx].date, buf);
                else {
                    fputs ("error: date exceeds storage.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                if (check_time_from_now (data[ndx].date) > SECPY) {
                    if (data[ndx].type)
                        data[ndx].value *= 1.5;
                    else
                        data[ndx].value *= 2.5;
                }
                n = 0;
                ndx++;
                if (ndx == MAXS)
                    goto arrayfull;
                break;
            default:
                fputs ("error: you shouldn't get here!\n", stderr);
                break;
        }
    }
    arrayfull:;
    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    puts ("\ncomputed information\n");
    prn_data_t_array (data, ndx);   /* print the computed values */

    return 0;
}

Now using your data file:

Example Input File

$ cat dat/namelbltypevaldate.txt
Hanif Hefaz
BA123HB
0
100.50
20180101

Jacki Shroff
UP673MK
1
3000.99
20170512

Example Use/Output

The code would produce:

$ ./bin/time_from_now3 dat/namelbltypevaldate.txt

computed information

Hanif Hefaz  BA123HB   0     251.25 20180101
Jacki Shroff UP673MK   1    4501.48 20170512

If you check, the appropriate multiplier has been applied to each.

I really wouldn't want to do this with 5 separate arrays. A struct is the proper tool for the job. You can use 5 arrays, in the exact same way I use the array of struct above -- you just have a much longer variable list and a lot more names to keep track of in your code. Look this over and let me know if you have more questions.

Dynamically Allocating Array of Struct

Functionally, the program doesn't care where the storage for your data is. However, allocating with automatic storage does have the drawback of not being able to dynamically grow as you continue to add records. The alternative to the dynamically allocate your data using malloc, calloc, realloc. This adds a slight amount of additional complexity as it is now up to you to track the allocated storage available, the storage used, and to reallocate when your storage used equals your storage available.

You do not want to realloc every line -- that is inefficient. Instead you will allocate some reasonable starting number of structs, fill them until you reach the limit and then reallocate. How much you reallocate is up to you, but common reallocation schemes are to double the currently allocated size (or you can add some other multiple like 3/2, etc... depending on how quickly you want your allocation to grow). The example uses the good old double method.

First the changes (to only print lines greater than 1 years from now) simply involves moving your output to within:

                if (check_time_from_now (data[ndx].date) > SECPY) {

Adding a function to output record ndx is something you can do to keep the body of your code tidy, e.g.

void prn_data_t_rec (mydata_t *data, int n)
{
    printf ("%-12s %-8s  %d  %9.2f %s\n", data[n].name, data[n].plate,
            data[n].type, data[n].value, data[n].date);
}

Which makes calling from the body of your code:

                if (check_time_from_now (data[ndx].date) > SECPY) {
                    if (data[ndx].type)
                        data[ndx].value *= 1.5;
                    else
                        data[ndx].value *= 2.5;
                    prn_data_t_rec (data, ndx);  /* output > 1 year */
                }

Now to the dynamic allocation. Instead of declaring:

mydata_t data[MAXS] = {{ .name = "" }};

You simply change the declaration and initial allocation to:

#define MAXS   16u  /* initial number of structs */
...
    int maxs = MAXS;    /* variable to track allocated number of struct */
    mydata_t *data = calloc (maxs, sizeof *data); /* allocate storage */

(note: calloc was chosen over malloc because I want to initialize all memory zero and avoid having to write an explicit initializer to set every type or value member to zero otherwise. The overhead in allowing calloc to do it is negligible)

Now *Validate EVERY Allocation

    if (!data) {    /* validate every allocation */
        perror ("calloc-data");
        return 1;
    }

The same applies to every reallocation with a caveat. You Always realloc with a Temporary Pointer. If you reallocate with the pointer itself, e..g ptr = realloc (ptr, newsize); and realloc fails returning NULL - you overwrite your original address with NULL creating a memory leak and lose all access to your existing data!

Everything else in the code is identical until you get to the realloc in case 4: of the switch(). There when (ndx == maxs) you must realloc additional storage, e.g.

            case 4: 
                if (strlen (buf) < MAXNM)
                    strcpy (data[ndx].date, buf);
                else {
                    fputs ("error: date excceeds storage.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                if (check_time_from_now (data[ndx].date) > SECPY) {
                    if (data[ndx].type)
                        data[ndx].value *= 1.5;
                    else
                        data[ndx].value *= 2.5;
                    prn_data_t_rec (data, ndx);
                }
                n = 0;
                ndx++;
                if (ndx == maxs) {  /* check if realloc required */
                    /* realloc w/temp pointer to 2X current size */
                    void *tmp = realloc (data, 2 * maxs * sizeof *data);
                    if (!tmp) { /* validate every allocation */
                        perror ("realloc-data");
                        goto outofmem;  /* original data still good */
                    }
                    data = tmp; /* set data to newly reallocated block */
                    /* zero the new memory allocated (optional) */
                    memset (data + maxs, 0, maxs * sizeof *data);
                    maxs *= 2;  /* update the currently allocated max */
                }
                break;

(note: the continued use of goto -- it is imperative here to preserve your original data if realloc fails. In the even of failure ALL of your original data is still good - and since you used a temporary pointer to test the reallocation, you haven't lost access to it. So simply jump out of the switch and while loop and you can continue to make use of it and free it when no longer needed.

Putting the entire example together you would have:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>

#define MAXS   16u  /* initial number of structs */
#define MAXNM  32u  /* max characters in name and other arrays in struct */
#define MAXC 1024u  /* max characters in read buffer */
#define SECPY 31536000u /* seconds per-year */

typedef struct {
    char name[MAXNM],
        plate[MAXNM],
        date[MAXNM];
    int type;
    double value;
} mydata_t;

time_t fill_broken_down_time (int y, int m, int d)
{                   /* initialize struct members */
    struct tm bdt = { .tm_sec=0, .tm_min=0, .tm_hour=0, .tm_mday=d, 
                    .tm_mon=m>0?m-1:0, .tm_year=y-1900, .tm_isdst=-1 };

    return mktime(&bdt);    /* return mktime conversion to time_t */
}

double check_time_from_now (const char *str)
{
    int y, m, d;

    if (sscanf (str, "%4d%2d%2d", &y, &m, &d) != 3) {
        fprintf (stderr, "error non-date string: '%s'.\n", str);
        exit (EXIT_FAILURE);
    }

    time_t  now = time(NULL),
            then = fill_broken_down_time (y, m, d);
    double secs = difftime (now, then); /* get seconds between dates */

    return secs;
}

void prn_data_t_array (mydata_t *data, int n)
{
    for (int i = 0; i < n; i++)
        printf ("%-12s %-8s  %d  %9.2f %s\n", data[i].name, data[i].plate,
                data[i].type, data[i].value, data[i].date);
}

void prn_data_t_rec (mydata_t *data, int n)
{
    printf ("%-12s %-8s  %d  %9.2f %s\n", data[n].name, data[n].plate,
            data[n].type, data[n].value, data[n].date);
}

int main (int argc, char **argv) {
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
    int n = 0, ndx = 0, /* NOTE when dealing with array, start at ZERO */
        maxs = MAXS;    /* variable to track allocated number of struct */
    char buf[MAXC];     /* buffer to hold each line read from file */
    mydata_t *data = calloc (maxs, sizeof *data); /* allocate storage */

    if (!data) {    /* validate every allocation */
        perror ("calloc-data");
        return 1;
    }

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    puts ("\ncomputed information for records > 1 year from now\n");

    while (fgets (buf, MAXC, fp)) {     /* read each line in file */
        if (isspace(*buf))  /* skip blank lines (or start with space) */
            continue;
        buf[strcspn (buf, "\r\n")] = 0; /* trim '\n' from end of buf */
        /* if line isn't a date line, just output line as non-date line */
        switch (n) {
            case 0: 
                if (strlen (buf) < MAXNM)
                    strcpy (data[ndx].name, buf);
                else {
                    fputs ("error: name excceeds storage.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;
                break;
            case 1:
                if (strlen (buf) < MAXNM)
                    strcpy (data[ndx].plate, buf);
                else {
                    fputs ("error: plate excceeds storage.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;
                break;
            case 2:
                if (sscanf (buf, "%d", &data[ndx].type) != 1) {
                    fputs ("error: type not an integer.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;
                break;
            case 3:
                if (sscanf (buf, "%lf", &data[ndx].value) != 1) {
                    fputs ("error: value not a double.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;
                break;
            case 4: 
                if (strlen (buf) < MAXNM)
                    strcpy (data[ndx].date, buf);
                else {
                    fputs ("error: date excceeds storage.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                if (check_time_from_now (data[ndx].date) > SECPY) {
                    if (data[ndx].type)
                        data[ndx].value *= 1.5;
                    else
                        data[ndx].value *= 2.5;
                    prn_data_t_rec (data, ndx);
                }
                n = 0;
                ndx++;
                if (ndx == maxs) {  /* check if realloc required */
                    /* realloc w/temp pointer to 2X current size */
                    void *tmp = realloc (data, 2 * maxs * sizeof *data);
                    if (!tmp) { /* validate every allocation */
                        perror ("realloc-data");
                        goto outofmem;  /* original data still good */
                    }
                    data = tmp; /* set data to newly reallocated block */
                    /* zero the new memory allocated (optional) */
                    memset (data + maxs, 0, maxs * sizeof *data);
                    maxs *= 2;  /* update the currently allocated max */
                }
                break;
            default:
                fputs ("error: you shouldn't get here!\n", stderr);
                break;
        }
    }
    outofmem:;
    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    puts ("\nall stored information\n");
    prn_data_t_array (data, ndx);   /* print the computed values */

    free (data);    /* don't forget to free what you allocate */

    return 0;
}

(note: it print > 1 year during the loop and output the total records at the end -- adjust as needed)

Example Use/Output

Using your same data file:

$ ./bin/time_from_now4 dat/namelbltypevaldate.txt

computed information for records > 1 year from now

Hanif Hefaz  BA123HB   0     251.25 20180101
Jacki Shroff UP673MK   1    4501.48 20170512

all stored information

Hanif Hefaz  BA123HB   0     251.25 20180101
Jacki Shroff UP673MK   1    4501.48 20170512

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.

$ ./bin/time_from_now4 dat/namelbltypevaldate.txt
==4331== Memcheck, a memory error detector
==4331== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==4331== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==4331== Command: ./bin/time_from_now4 dat/namelbltypevaldate.txt
==4331==

computed information for records > 1 year from now

Hanif Hefaz  BA123HB   0     251.25 20180101
Jacki Shroff UP673MK   1    4501.48 20170512

all stored information

Hanif Hefaz  BA123HB   0     251.25 20180101
Jacki Shroff UP673MK   1    4501.48 20170512
==4331==
==4331== HEAP SUMMARY:
==4331==     in use at exit: 0 bytes in 0 blocks
==4331==   total heap usage: 12 allocs, 12 frees, 5,333 bytes allocated
==4331==
==4331== All heap blocks were freed -- no leaks are possible
==4331==
==4331== For counts of detected and suppressed errors, rerun with: -v
==4331== 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.

Let me know if you have further questions (which will probably warrant a new question at this point)

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • I appreciate your good work and thank you so much. It almost works, the only thing there I do not want is it prints each record even if the date difference is less than one year from now. Why can't we do that using malloc? please show me a way to do the same thing as it does now but using malloc, because I need to do it using malloc. –  Mar 16 '19 at 13:29
  • 1
    Sure, I'll drop an update. Using `malloc` and `realloc` will add a bit of complexity due to the required validations and the use of a temporary pointer on `realloc`. The code will be identical, aside from allocating for each record. You can move the print to inside the `if (check_time_from_now (data[ndx].date) > SECPY)` conditional to only print if greater than a year ago. I'll have time this afternoon to update. – David C. Rankin Mar 16 '19 at 20:41
  • Well, I think we can also solve the date's problem by entering that command. I mean when I enter the command to print the results we can also enter current date as `20190316` as input and then find the difference between the entered date and the one in the file for each record. –  Mar 16 '19 at 20:50
  • I'm just tidying up the additions, give me 5 minutes `:)` – David C. Rankin Mar 16 '19 at 21:08
  • Thank You so much for your time and effort. that is very very enough. I got what I wanted. –  Mar 16 '19 at 22:03
  • 1
    Glad to help. Good luck with your coding -- and always validate every read and every allocation `:)` – David C. Rankin Mar 16 '19 at 22:04