2019-10-09
How to dynamically allocate an struct + pointer
stackoverflow
Question

I've been trying to find out how to store information from the user in an array of struct, but so far... Not working.

I created the struct, and inside the main I created the pointer to that struct, then I dynamically allocated that struct. But I don't really know how I'm going to get the info from the user, I mean. I know it but it's not working as expected. If I was using only an array of struct, it would be something like this...

&p[i].id //Normal

I tried to use this method but not working, idk why... The code is not completed yet...

//
//  7.c
//  IFTM Exercises
//
//  Created by Lelre Ferreira on 8/29/19.
//  Copyright © 2019 Lelre Ferreira. All rights reserved.
//

#define size 5
#include <stdio.h>
#include <stdlib.h>

struct produtos {

    int id;
    int quant;
    float vlrs;
    char nm[50];

};

void cadastroProdutos (struct produtos *p, int tamanho);
void maiorValorProdutos (struct produtos *p, int tamanho);
void maiorEstoqueProdutos (struct produtos *p, int tamanho);

int main (int argc, const char * argv[]){

    struct produtos *p;
    int tamanho    = 0;

    printf("Insira quantidade de produtos/itens a serem cadastrados: ");
    scanf("%d", &tamanho);
    p = (struct produtos *) malloc(tamanho   * sizeof(struct produtos));

    cadastroProdutos(p, tamanho);
    maiorValorProdutos(p, tamanho);
    maiorEstoqueProdutos(p, tamanho);

    return 0;
}

void cadastroProdutos(struct produtos *p, int tamanho){

    int i = 0;
    for (i = 0; i < tamanho; i++) {

        printf("Insira o ID: ");
        scanf("%d", &p[i] -> id);
        printf("Insira o nome: ");
        scanf("%s",  p[i] -> nm);
        printf("Insira o valor: ");
        scanf("%f", &p[i] -> vlrs);
        printf("Insira a quantidade: ");
        scanf("%d", &p[i] -> quant);

    }
}

void maiorValorProdutos(struct produtos *p, int tamanho){



}

void maiorEstoqueProdutos(struct produtos *p, int tamanho){



}

The IDE is giving this error: Cannot take the address of an rvalue of type 'int'...

Answer
1

You missed understanding that the [..] operator applied to your pointer p (e.g. p[0]) acts as a dereference making the type struct produtos and the '.' operator proper (instead of struct produtos* as noted in @MilesBudnek's answer.

Aside from your '.' or '->' operator problem, you seem to be missing the point on dynamically allocating. While it is fine to start with #define size 5 allocated structs, the point of dynamically allocating memory is being able to handle your 6th struct seamlessly by reallocating additional memory on as as-needed basis to handle all your input (or until your run out of available memory).

The way you currently have your function cadastroProdutos written, doesn't really make a clean dynamic allocation possible (in addition to the fact it returns void and there is no validation of any input within it). Rather than looping size times within your function, you simply want to get 1-struct worth of data each time cadastroProdutos is called. That way you can call it as many times as needed until all your data is entered. There is no need to loop within your function, and your use of scanf is horribly fragile as you have no way to handle characters left in stdin due to a matching failure or simply stray characters accidentally typed, (e.g. 'a' typed for id, or a name longer than 49 characters, etc.)

To resolve a majority of the problems, don't use scanf for User-Input, instead read all User-Input into a temporary buffer of reasonable size (e.g. 1024 characters or so) and then parse the needed information from the buffer using sscanf. This has the double-benefit of allowing separate validation of both the read and parse; and by using a reasonable sized buffer you eliminate the extraneous character problem (such as a cat stepping on the keyboard) by consuming a complete line of input every time.

Your cadastroProdutos which takes input, provides no meaningful return to indicate success/failure of the input leaving your function susceptible to Undefined Behavior if any one input fails. So validate EVERY user input by checking the return of the function(s) used. Of any input failure, returning 0 for failure, and non-zero on success allows you to handle any input failure back in the calling function.

(note: counting types should use size_t instead of int)

Putting those changes together for your cadastroProdutos, you could do something similar to:

size_t cadastroProdutos (produtos *p, size_t *tamanho)
{
    produtos tmp;       /* temporary struct */
    char buf[MAXC];     /* temporary buffer */

    /* prompt, read-line, validate parse */
    fputs ("\nInsira o ID: ", stdout);
    if (!fgets (buf, MAXC, stdin) || sscanf (buf, "%d", &tmp.id) != 1)
        return 0;   /* (failure) */

    fputs ("Insira o nome: ", stdout);
    if (!fgets (buf, MAXC, stdin) || sscanf (buf, "%49[^\n]", tmp.nm) != 1)
        return 0;

    fputs ("Insira o valor: ", stdout);
    if (!fgets (buf, MAXC, stdin) || sscanf (buf, "%f", &tmp.vlrs) != 1)
        return 0;

    fputs ("Insira a quantidade: ", stdout);
    if (!fgets (buf, MAXC, stdin) || sscanf (buf, "%d", &tmp.quant) != 1)
        return 0;

    p[(*tamanho)++] = tmp;  /* assign tmp to p[*tamanho], increment */

    return *tamanho;    /* return tamanho (success) */
}

(note: tamanho is passed as a pointer so its number can be updated within the function, and 0 is returned on failure, while the updated tamanho is returned on success allowing you a meaningful return of type size_t)

Avoid using magic-numbers in your code. You did a good job defining a constant for size, now just fining defining the constants you need so there are no more magic-numbers like char nm[50]; with it. Doing that you could use a couple more #define, e.g.

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

#define PROD 2      /* initial no. of struct to allocate */
#define NMSZ 50     /* max name size */
#define MAXC 1024   /* reasonable no. of chars for temporary buffer */

typedef struct {    /* a typedef used for convenience */
    int id;
    int quant;
    float vlrs;
    char nm[NMSZ];
} produtos;

Now all you need to do in main() is to declare a temporary buffer to hold the line, and size_t values, the total number of struct currently allocated (allocated) and the number filled (tomanho) which leads to a simple check at the start of each loop if (tomanho == allocated), you know you need to realloc additional struct before attempting to fill any more. Your main() function can essentially be:

int main (void) {

    size_t  allocated = PROD,   /* initial number of struct to allocate */
            tomanho = 0;        /* number of allocated structs used */
    produtos *p = malloc (allocated * sizeof *p);   /* allocate */

    if (!p) {   /* validate EVERY allocation */
        perror ("malloc-p");
        return 1;
    }

    while (cadastroProdutos (p, &tomanho)) {    /* loop validating return */
        char buf[MAXC];                         /* buffer for input (y/n) */
        if (tomanho == allocated) { /* is a realloc needed to add struct? */
            /* always realloc with a temporary pointer */
            void *tmp = realloc (p, 2 * allocated * sizeof *p);
            if (!tmp) {     /* validate the reallocation */
                perror ("realloc-p");
                break;      /* realloc failed, original p still good, break */
            }
            p = tmp;        /* assign new block of mem to p */
            allocated *= 2; /* update no. of structs allocated */
        }

        fputs ("\n  add another (y/n)? ", stdout);  /* add more produtos? */
        if (!fgets (buf, MAXC, stdin) || !(*buf == 'y' || *buf == 'Y')) {
            putchar ('\n');
            break;
        }
    }

At this point all your struct are filled and stored in p and all you need to do is whatever you need to do with your data (simply output below) and then free() the memory your have allocated, e.g.

    for (size_t i = 0; i < tomanho; i++)    /* loop showing stored data */
        printf ("p[%2zu] %4d  %-20s  %6.2f  %4d\n",
                i, p[i].id, p[i].nm, p[i].vlrs, p[i].quant);

    free (p);   /* don't forget to free memory you allocate */
}

Putting it altogether in a short example, you could do:

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

#define PROD 2      /* initial no. of struct to allocate */
#define NMSZ 50     /* max name size */
#define MAXC 1024   /* reasonable no. of chars for temporary buffer */

typedef struct {    /* a typedef used for convenience */
    int id;
    int quant;
    float vlrs;
    char nm[NMSZ];
} produtos;

size_t cadastroProdutos (produtos *p, size_t *tamanho)
{
    produtos tmp;       /* temporary struct */
    char buf[MAXC];     /* temporary buffer */

    /* prompt, read-line, validate parse */
    fputs ("\nInsira o ID: ", stdout);
    if (!fgets (buf, MAXC, stdin) || sscanf (buf, "%d", &tmp.id) != 1)
        return 0;   /* (failure) */

    fputs ("Insira o nome: ", stdout);
    if (!fgets (buf, MAXC, stdin) || sscanf (buf, "%49[^\n]", tmp.nm) != 1)
        return 0;

    fputs ("Insira o valor: ", stdout);
    if (!fgets (buf, MAXC, stdin) || sscanf (buf, "%f", &tmp.vlrs) != 1)
        return 0;

    fputs ("Insira a quantidade: ", stdout);
    if (!fgets (buf, MAXC, stdin) || sscanf (buf, "%d", &tmp.quant) != 1)
        return 0;

    p[(*tamanho)++] = tmp;  /* assign tmp to p[*tamanho], increment */

    return *tamanho;    /* return tamanho (success) */
}

int main (void) {

    size_t  allocated = PROD,   /* initial number of struct to allocate */
            tomanho = 0;        /* number of allocated structs used */
    produtos *p = malloc (allocated * sizeof *p);   /* allocate */

    if (!p) {   /* validate EVERY allocation */
        perror ("malloc-p");
        return 1;
    }

    while (cadastroProdutos (p, &tomanho)) {    /* loop validating return */
        char buf[MAXC];                         /* buffer for input (y/n) */
        if (tomanho == allocated) { /* is a realloc needed to add struct? */
            /* always realloc with a temporary pointer */
            void *tmp = realloc (p, 2 * allocated * sizeof *p);
            if (!tmp) {     /* validate the reallocation */
                perror ("realloc-p");
                break;      /* realloc failed, original p still good, break */
            }
            p = tmp;        /* assign new block of mem to p */
            allocated *= 2; /* update no. of structs allocated */
        }

        fputs ("\n  add another (y/n)? ", stdout);  /* add more produtos? */
        if (!fgets (buf, MAXC, stdin) || !(*buf == 'y' || *buf == 'Y')) {
            putchar ('\n');
            break;
        }
    }

    for (size_t i = 0; i < tomanho; i++)    /* loop showing stored data */
        printf ("p[%2zu] %4d  %-20s  %6.2f  %4d\n",
                i, p[i].id, p[i].nm, p[i].vlrs, p[i].quant);

    free (p);   /* don't forget to free memory you allocate */
}

(note: a simple add another (y/n)? prompt is added to determine if the user wants to add more data)

Example Use/Output

Above we start with 2-allocated struct, and then proceed to enter 6 struct, forcing a reallocation to take place. An example could be:

$ ./bin/produtos

Insira o ID: 1
Insira o nome: John Brown
Insira o valor: 12.34
Insira a quantidade: 415

  add another (y/n)? y

Insira o ID: 2
Insira o nome: Mary Brown
Insira o valor: 23.45
Insira a quantidade: 416

  add another (y/n)? y

Insira o ID: 3
Insira o nome: Mickey Mouse
Insira o valor: 34.56
Insira a quantidade: 417

  add another (y/n)? y

Insira o ID: 4
Insira o nome: Minnie Mouse
Insira o valor: 45.67
Insira a quantidade: 418

  add another (y/n)? y

Insira o ID: 5
Insira o nome: Sam Clemens
Insira o valor: 56.78
Insira a quantidade: 419

  add another (y/n)? y

Insira o ID: 6
Insira o nome: Mark Twain
Insira o valor: 67.89
Insira a quantidade: 420

  add another (y/n)? n

p[ 0]    1  John Brown             12.34   415
p[ 1]    2  Mary Brown             23.45   416
p[ 2]    3  Mickey Mouse           34.56   417
p[ 3]    4  Minnie Mouse           45.67   418
p[ 4]    5  Sam Clemens            56.78   419
p[ 5]    6  Mark Twain             67.89   420

All data is properly stored, and you could have added 1000 more entries if you desired. You should also run any program that uses dynamic memory through a memory error checking program (like valgrind on Linux) that can tell if you have made improper use of a pointer of block of memory you don't own.

A short test redirecting the inputs from a file rather than retyping can confirm whether any memory problems exists, and using the memory checker is simple, just run your program though it, e.g.

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

Insira o ID: Insira o nome: Insira o valor: Insira a quantidade:
  add another (y/n)?
Insira o ID: Insira o nome: Insira o valor: Insira a quantidade:
  add another (y/n)?
Insira o ID: Insira o nome: Insira o valor: Insira a quantidade:
  add another (y/n)?
Insira o ID: Insira o nome: Insira o valor: Insira a quantidade:
  add another (y/n)?
Insira o ID: Insira o nome: Insira o valor: Insira a quantidade:
  add another (y/n)?
Insira o ID: Insira o nome: Insira o valor: Insira a quantidade:
  add another (y/n)? 
p[ 0]    1  John Brown             12.34   415
p[ 1]    2  Mary Brown             23.45   416
p[ 2]    3  Mickey Mouse           34.56   417
p[ 3]    4  Minnie Mouse           45.67   418
p[ 4]    5  Sam Clemens            56.78   419
p[ 5]    6  Mark Twain             67.89   420
==12885==
==12885== HEAP SUMMARY:
==12885==     in use at exit: 0 bytes in 0 blocks
==12885==   total heap usage: 3 allocs, 3 frees, 896 bytes allocated
==12885==
==12885== All heap blocks were freed -- no leaks are possible
==12885==
==12885== For counts of detected and suppressed errors, rerun with: -v
==12885== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Always confirm "All heap blocks were freed -- no leaks are possible" and that there are no errors.

Look things over and let me know if you have further questions.

How to dynamically allocate an struct + pointer
See more ...