0

I'm trying to malloc a struct with a value read from 3 archives passed by command. I have 3 types of coins whose prices and dates I've turned into arrays, but not dynamically. How do I malloc these dates and prices from the struct?

Command

a.exe BTC.csv NEO.csv IOT.csv

BTC.csv

253   
02/20/18,11403.7   
02/19/18,11225.3   
02/18/18,10551.8   
02/17/18,11112.7   
02/16/18,10233.9  
 ...

NEO.csv

253    
02/20/18,128.36    
02/19/18,137.47    
02/18/18,127.38    
02/17/18,136.75    
02/16/18,128.85   
...

IOT.csv

253    
2/20/18,1.91    
2/19/18,2.09    
2/18/18,1.98   
2/17/18,2.2   
2/16/18,2.1   
...

Code:

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

typedef struct
{
    int month;
    int day;
    int year;
    double value;
}array;

typedef struct
    {   //I want allocate these arrays dynamically
    int month[253];
    int day[253];
    int year[253];
    double newvalue[253];
}coin;

int main(int argc, char *argv[]){
    FILE *csv;
    char string[9];
    long int n_lines=0;
    int n = argc-1;

    coin *m = (coin*)malloc(n*sizeof(coin));

    for(int z=1; z<argc; z++)
       {
        sprintf(string, "%s", argv[z]);

        if((csv=fopen(string, "r")) == NULL)
        {
            printf("%s not found\n", string);
            exit(1);
        }

        fscanf(csv, "%li", &n_lines); //n_lines=253

        array *v;
        v=(array*)malloc(n_lines*sizeof(array));

        char line[256];
        int i=0;

        while (fgets(line, 256, csv) != NULL && i<n_lines)
        {
                int count = fscanf(csv, "%d/%d/%d,%lf", &v[i].month, &v[i].day, &v[i].year, &v[i].value);

                m[z-1].month[i] = v[i].month;
                m[z-1].day[i] = v[i].day;
                m[z-1].year[i] = v[i].year;
                m[z-1].newvalue[i] = (v[i].value)/2;
                i++;
        }

        free(v);

        fclose(csv); 

    }

    for(int z=1;i<argc;z++)
    {
        for(int i=0;i<n_lines;i++)
        {
           printf("%0.2d/%0.2d/%0.2d  %lf\n", m[z-1].month[i], m[z-1].day[i], m[z-1].year[i], m[z-1].newvalue[i]);
        }
    }

    return 0;
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Andre
  • 115
  • 12
  • If you want `month`, `day` etc to be dynamically allocated, then don't declare them as array, declare them as pointers. Also see [don't cast `malloc`](https://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc) – Pablo Mar 29 '18 at 00:37
  • Also in the `while(fgets...` line you forgot to increment `i` by one. – Pablo Mar 29 '18 at 00:42
  • Ok but that didn't help me, I still don't know how to malloc these pointers – Andre Mar 29 '18 at 00:43

2 Answers2

3

Instead of setting up your struct as a bunch of arrays:

typedef struct
{   //I want allocate these arrays dynamically
    int month[253];
    int day[253];
    int year[253];
    double newvalue[253];

}coin;

Just use pointers:

typedef struct
{
    int *month;
    int *day;
    int *year;
    double *newvalue;
} coin;

And dynamically allocate memory for them after v=(array*)malloc(n_lines*sizeof(array));:

coin c;
c.month = malloc(n_lines * sizeof(int));
c.day = malloc(n_lines * sizeof(int));
c.year = malloc(n_lines * sizeof(int));
c.newvalue = malloc(n_lines * sizeof(double));

Don't forget to free() them once you're done using them.

frslm
  • 2,969
  • 3
  • 13
  • 26
2

I think you should be allocating n arrays of your array structure. That would be better called coin, or perhaps coin_value (or some sort of Camel_Case name, such as Coin_Value), but it is the basic entry type. It's marginally complex because you need an array of 3 arrays of (253) structures, but it is much cleaner than hiding separate arrays inside a structure, not least because it dramatically simplifies the memory allocation (one per file). Indeed, it would be possible to allocate all the memory at once (and therefore free it at once), but I've not done that. The code also doesn't check that the data files all have the same number of lines — it should. There's also no checking that the date values in corresponding entries match; again, there probably should be such checking. When it spots an error, it reports it on standard error and exits.

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

typedef struct Coin_Value
{
    int month;
    int day;
    int year;
    double value;
} Coin_Value;

int main(int argc, char *argv[])
{
    FILE *csv;
    long n_lines = 0;
    int n = argc - 1;

    Coin_Value **m = (Coin_Value **)malloc(n * sizeof(*m));
    if (m == 0)
    {
        fprintf(stderr, "Memory allocation failure (%zu bytes)\n", n * sizeof(*m));
        exit(1);
    }

    /* Should check that number of lines in each file is consistent */
    for (int z = 1; z < argc; z++)
    {
        if ((csv = fopen(argv[z], "r")) == NULL)
        {
            fprintf(stderr, "%s not found\n", argv[z]);
            exit(1);
        }

        if (fscanf(csv, "%li", &n_lines) != 1)
        {
            fprintf(stderr, "failed to read a number from %s\n", argv[z]);
            exit(1);
        }
        if (n_lines <= 0 || n_lines > 1000)
        {
            fprintf(stderr, "number of lines in %s out of control (got %ld)\n", argv[z], n_lines);
            exit(1);
        }

        /* Gobble any trailing data and newline */
        int c;
        while ((c = getc(csv)) != EOF && c != '\n')
            ;

        Coin_Value *v = (Coin_Value *)malloc(n_lines * sizeof(*v));
        if (v == 0)
        {
            fprintf(stderr, "Memory allocation failure (%zu bytes)\n", n_lines * sizeof(*v));
            exit(1);
        }

        char line[256];
        for (int i = 0; fgets(line, sizeof(line), csv) != NULL && i < n_lines; i++)
        {
            if (sscanf(line, "%d/%d/%d,%lf", &v[i].month, &v[i].day, &v[i].year, &v[i].value) != 4)
            {
                fprintf(stderr, "Format error processing line: %s", line);
                exit(1);
            }
        }

        m[z-1] = v;

        fclose(csv);
    }

    /* Multi-column output */
    putchar('\n');
    for (int z = 1; z < argc; z++)
        printf("%s%19.3s", (z == 1) ? "" : "   ", argv[z]);
    putchar('\n');

    for (long i = 0; i < n_lines; i++)
    {
        for (int z = 1; z < argc; z++)
        {
            printf("%s%.2d/%.2d/%.2d  %9.2f", (z == 1) ? "" : "   ",
                   m[z - 1][i].month, m[z - 1][i].day, m[z - 1][i].year, m[z - 1][i].value);
        }
        putchar('\n');
    }
    putchar('\n');

    for (int z = 1; z < argc; z++)
    {
        printf("%.3s:\n", argv[z]);
        for (long i = 0; i < n_lines; i++)
        {
            printf("%.2d/%.2d/%.2d  %9.2f\n",
                   m[z - 1][i].month, m[z - 1][i].day, m[z - 1][i].year, m[z - 1][i].value);
        }
        putchar('\n');
    }

    for (int z = 1; z < argc; z++)
        free(m[z-1]);
    free(m);

    return 0;
}

This code prints the data two different ways:

  1. In N columns across the page.
  2. In N groups of entries down the page.

On your sample inputs (5 lines, instead of 253 lines):

                BTC                   NEO                   IOT
02/20/18   11403.70   02/20/18     128.36   02/20/18       1.91
02/19/18   11225.30   02/19/18     137.47   02/19/18       2.09
02/18/18   10551.80   02/18/18     127.38   02/18/18       1.98
02/17/18   11112.70   02/17/18     136.75   02/17/18       2.20
02/16/18   10233.90   02/16/18     128.85   02/16/18       2.10

BTC:
02/20/18   11403.70
02/19/18   11225.30
02/18/18   10551.80
02/17/18   11112.70
02/16/18   10233.90

NEO:
02/20/18     128.36
02/19/18     137.47
02/18/18     127.38
02/17/18     136.75
02/16/18     128.85

IOT:
02/20/18       1.91
02/19/18       2.09
02/18/18       1.98
02/17/18       2.20
02/16/18       2.10
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278