0

I am building a small program that takes name and age as input (stored in a struct) and spits out the output. One of the problems that I am facing is I have to enter the number of people I am going to store, something that I am sure I can solve with realloc() it's just not working. Here is what I got so far.

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

struct info
{
    int age;
    char name[30];
};

int main()
{
    struct info *Ptr;
    int i, num;

    printf("Enter number of people");
    scanf("%d", &num);

    // Allocates the memory for num structures with pointer Ptr pointing to the base address.
    Ptr = (struct info*)malloc(num * sizeof(struct info));

    for(i = 0; i < num; ++i)
    {
        printf("Enter name and age:\n");
        scanf("%s %d", &(Ptr+i)->name, &(Ptr+i)->age);
    }


    for(i = 0; i < num ; ++i)
        printf("Name = %s, Age = %d\n", (Ptr+i)->name, (Ptr+i)->age);

    return 0;
}

I have tried to realloc inside the first for loop, but it wasn't working even if it makes sense to have it there. Have also tried to convert the loop to a while loop like this:

     while(input != "stop)
    {
      allocate more memory
}

How can I use realloc to in order to skip having to enter the persons number before entering them?

Ali Khaki
  • 1,184
  • 1
  • 13
  • 24
HannaM
  • 11
  • I know, but i can't do that before i can do realoc in the right way before i can move on to comparing the input of the struct and the input variable – HannaM Nov 01 '18 at 15:13
  • 2
    Could you explain clearly what you are you trying to achieve? you don't want to take no.of people, and then adjust if no.of people increase, is this is what you are trying to achieve? – nilsocket Nov 01 '18 at 15:15
  • "*it wasn't working*" what actually did not work? – alk Nov 01 '18 at 16:32
  • While realloc allows you to extend the allocation, it can be very inefficient. If the records do not need to be located in contiguous memory, a linked list may be appropriate. Adding records is fast and efficient, accessing records less-so. – Clifford Nov 01 '18 at 16:39

4 Answers4

2

realloc is the correct way. Just start with Ptr = NULL and num = 0 and on each input increase the number of elements by one.

Remember to limit the number of characters scanf can read, otherwise you may buffer overrun.

Also I find Ptr[i] way easier then (Ptr+i)->.

Also compare strings with strcmp not using !=. The != will compare pointers to strings, not strings themselves.

As I like reading the whole line, then scanning the line, I would do it like this:

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

struct info
{
    int age;
    char name[30];
};

int main()
{
    struct info *ptr = 0;
    size_t num = 0;

    for (;;) {
        printf("Enter name and age. If you want to stop, type only 'stop'.\n");

        char line[256];
        if (fgets(line, sizeof(line), stdin) == NULL) { 
             fprintf(stderr, "fgets error");
             exit(-1);
        }

        if (!strcmp("stop\n", line)) {
             break;
        }

        struct info tmp;
        if (sscanf(line, "%29s %d\n", tmp.name, &tmp.age) != 2) {
             fprintf(stderr, "error parsing line\n");
             exit(-1);
        }

        ptr = realloc(ptr, (num + 1) * sizeof(*ptr));
        if (ptr == NULL) { 
             fprintf(stderr, "error allocating memory!\n");
             exit(-1);
        }

        ptr[num] = tmp;
        ++num;
    }


    for (size_t i = 0; i < num ; ++i) {
        printf("Name = %s, Age = %d\n", ptr[i].name, ptr[i].age);
    }

    free(ptr);

    return 0;
}
Clifford
  • 88,407
  • 13
  • 85
  • 165
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • `if (fgets(line, sizeof(line), stdin) < 0) {...}` fgets() returns a `char*` .This could be NULL, but never `< 0` Your compiler should have warned you. – joop Nov 01 '18 at 15:22
  • @joop right, right, you are right. That standard library, each function has different error handling. – KamilCuk Nov 01 '18 at 15:23
  • You can do pointer arithmetic if the pointers are pointing at the same array (or one is an array and the other points to one past the last element). but I don't think it's ever undefined to compare two pointers for equality. – Tim Randall Nov 01 '18 at 15:27
  • thanks for your reply, but i noticed you're not using malloc, how's that possible? – HannaM Nov 01 '18 at 16:16
  • 1
    @HannaM: "*not using malloc, how's that possible?*" You might like to read this answer: https://stackoverflow.com/a/12134328/694576 – alk Nov 01 '18 at 16:34
  • @HannaM `realloc(NULL, ...)` is equal to `malloc(...)`. That's why it's important for `ptr = NULL` the first time realloc is called. – KamilCuk Nov 01 '18 at 21:39
  • At least by convention this `exit(-1);` ought to be `exit(EXIT_FAILURE);` (`EXIT_FAILURE` comes with `stdlib.h`). – alk Nov 02 '18 at 05:36
0

If you are not sure of the no.of.elements you want to allocate and do it based on the users choice, then you can follow the below approach.

It starts with one element and the memory is reallocated as when the user wants to add new element.

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

struct info
{
  int age;
  char name[30];
};

int main()
{
    struct info *Ptr=NULL;
    int i=0, num;

    char c='Y';
    while(c=='Y'||c=='y') {

            Ptr=realloc(Ptr,(i+1)*sizeof(struct info));
            if(Ptr==NULL)
                 break;
            printf("Enter name and age:\n");
            scanf("%s %d",&Ptr[i].name,&Ptr[i].age);
            printf("Do you want to cont?\n");
            scanf(" %c",&c);
            i++;
    }
    num=i;

    for(i = 0; i < num ; ++i)
            printf("Name = %s, Age = %d\n", (Ptr+i)->name, (Ptr+i)->age);
    free(Ptr);
    return 0;
}
Karthick
  • 1,010
  • 1
  • 8
  • 24
0

You should use a data struct like vector.

  1. vector_init init vector.
  2. vector_push push val to vector, if necessary, will realloc memory.
  3. vector_output output the vector.

The following code could work:

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

#define INIT_SIZE 1

struct info
{
    int age;
    char name[30];
};

struct vector {
    struct info* p;
    int n;
    int index;
};

void vector_init(struct vector* ve) {
    ve->n     = INIT_SIZE;
    ve->index = 0;
    ve->p     = malloc(sizeof(struct info) * ve->n);
}

void vector_push(struct vector* ve, struct info* tmp) {
    if (ve->n == ve->index) {
        ve->n *= 2;
        ve->p = realloc(ve->p, sizeof(struct info) * ve->n);
    }
    ve->p[ve->index++] = *tmp;
}

void vector_output(const struct vector* ve) {
    for (int i = 0; i < ve->index; ++i)
        printf("Name = %s, Age = %d\n", ve->p[i].name, ve->p[i].age);
}

int main()
{
    struct vector ve;
    vector_init(&ve);

    for (;;) {
        struct info tmp;
        printf("Enter name and age:\n");
        scanf("%29s", tmp.name);
        if (strcmp(tmp.name, "stop") == 0)
            break;
        scanf("%d", &tmp.age);
        vector_push(&ve, &tmp);
    }
    vector_output(&ve);

    return 0;
}
Yunbin Liu
  • 1,484
  • 2
  • 11
  • 20
0

To answer exactly, you can first read the input to temp variables and check if you need to stop: Break the loop if so. Or continue and reallocate the 'storage' array by increasing it's size by one and copying the values you just read to the 'storage' array.

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

struct          info
{
  int           age;
  char          name[30];
};

int             main()
{
  struct info * infos = 0;
  int           num = 0;
  char          input_name[30];
  int           input_age;

  while (1)
    {
      printf("Enter name and age:\n");
      int r = scanf("%29s", input_name);
      if (r == EOF || strcmp(input_name, "stop") == 0)
        break;
      scanf(" %d", &input_age);

      infos = realloc(infos, sizeof(struct info) * (num + 1));
      infos[num].age = input_age;
      memcpy(infos[num].name, input_name, sizeof(char) * 30);
      num++;
    }

  for(int i = 0; i < num ; ++i)
    printf("Name = %s, Age = %d\n", infos[i].name, infos[i].age);

  return 0;
}
Tezirg
  • 1,629
  • 1
  • 10
  • 20
  • This `"%s"` should be `"%29s"` to avoid overflowing `input_name`. – alk Nov 01 '18 at 16:23
  • Also error checking for the 2nd call to `scanf()` is missing as well as for `realloc()`. – alk Nov 01 '18 at 16:24
  • And this test `(strcmp(input_name, "stop") == 0 || r == EOF)` has to be the other way round: `(r == EOF || strcmp(input_name, "stop"))` as you do not want to call `strcmp()` in case of `EOF`. – alk Nov 01 '18 at 16:25