简体   繁体   中英

Iterate array of struct using double pointer and access elements using double pointer

I have a nested struct car

struct employee{
    int id;
    unsigned int department_id;
    char initials;
};

struct car{
    int number;
    unsigned int tagged;
    struct employee emp;
};

Then I have an object of the struct car

struct car carObj[5] = {
    {1, 1, {11, 111,'c'}},
    {2, 0, {22, 222, 'd'}},
    {3, 1, {33, 333, 'e'}},
    {4, 1, {44, 444, 'f'}},
    {5, 0, {55, 555, 'g'}}
};

Next I have a pointer to the struct car

struct car *carPtr;

This "carPtr" pointer I have to fill with the various elements from the object carObj. I will allocate a new memory for each object of the car and copy the array element in it.

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

struct employee{
    int id;
    unsigned int department_id;
    char initials;
};

struct car{
    int number;
    unsigned int tagged;
    struct employee emp;
};

struct car carObj[5] = {
    {1, 1, {11, 111,'c'}},
    {2, 0, {22, 222, 'd'}},
    {3, 1, {33, 333, 'e'}},
    {4, 1, {44, 444, 'f'}},
    {5, 0, {55, 555, 'g'}}
};

struct car *carPtr;

void fillData(struct car **t)
{
    int cnt = 0;

    for(int i = 0; i < 5; i++)
    {
     cnt++;
     if(*t == NULL)
     {
        *t = calloc(cnt, sizeof(struct car));
     }
     else
     {
        *t = realloc(*t, (cnt*sizeof(struct car)));
     }

     memcpy(& ______, &carObj[i], sizeof(struct car));

        printf("%s(): number = %d, tagged = %d, id = %d, department = %d, initials = %c\n", __func__, ____.number, _____.tagged, ____.emp.id,  ____.emp.department_id, ______.emp.initials);
    }    
}

int main()
{   
    sendData(&carPtr);
    for(int i = 0; i < 5; i++)
    {
    printf("%s(): number = %d, tagged = %d, id = %d, department = %d, initials = %c\n", __func__, carPtr[i].number, carPtr[i].tagged, carPtr[i].emp.id,  carPtr[i].emp.department_id, carPtr[i].emp.initials);
    }

    return 0;
}

Question how do I access the access the individual elements using the double pointer? In memcpy above I have marked a blank "____ ". What statement I need to write there to access the recently allocated pointer? In the printf what statement I should write to access the elements of the pointer.

You are close, but there are a few improvements to make:

  1. Avoid the use of global variables,
  2. Choose a meaningful return type for fillData() that will indicate success/failure back in the caller,
  3. You know the number of elements before you call fillData() so simply allocate once and copy, and
  4. Pass the necessary information as parameters to your function.

Declare the variables in the scope where they are needed. For carObj[] that would be:

int main (void)
{   
  struct car carObj[ ] = {{1, 1, {11, 111,'c'}},    /* avoid global variables */
                          {2, 0, {22, 222, 'd'}},
                          {3, 1, {33, 333, 'e'}},
                          {4, 1, {44, 444, 'f'}},
                          {5, 0, {55, 555, 'g'}}},
    *carPtr = NULL;                                 /* must initialize pointer NULL */
  size_t nelem = sizeof carObj/sizeof *carObj;      /* number of elements */
  ...

Since you allocate in fillData() , you must have a way to indicate to the calling function whether the allocation succeeded or failed so that can be validated in the caller BEFORE any use is made of the allocated block of memory, eg

/* choose meaningful return type that indicates success/failure */
struct car *fillData (struct car **t, struct car *obj, size_t nelem)
{
  *t = calloc (nelem, sizeof **t);    /* number of elements known, just allocate */
  
  if (t == NULL) {                    /* valdiate every allocation */
    perror ("calloc-t");
    return NULL;
  }
  
  return memcpy (*t, obj, nelem * sizeof **t);  /* return pointer to allocated block */
}

( note: by using the dereferenced pointer to set the type-size for the allocation, you will never get it wrong)

As shown above, you know the number of elements nelem , so there is no need to loop in fillData() , just allocate, validate and copy.

By passing the necessary parameters to fillData() , you avoid the need for any globals and by choosing a meaningful return, you have a way to validate whether your block was allocated and filled back in main() . Your call in main() would be:

  if (!fillData (&carPtr, carObj, nelem))           /* validate function return */
    return 1;

Putting it altogether, you would have:

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

struct employee {
    int id;
    unsigned int department_id;
    char initials;
};

struct car {
    int number;
    unsigned int tagged;
    struct employee emp;
};

/* choose meaningful return type that indicates success/failure */
struct car *fillData (struct car **t, struct car *obj, size_t nelem)
{
  *t = calloc (nelem, sizeof **t);    /* number of elements known, just allocate */
  
  if (t == NULL) {                    /* valdiate every allocation */
    perror ("calloc-t");
    return NULL;
  }
  
  return memcpy (*t, obj, nelem * sizeof **t);  /* return pointer to allocated block */
}

int main (void)
{   
  struct car carObj[ ] = {{1, 1, {11, 111,'c'}},    /* avoid global variables */
                          {2, 0, {22, 222, 'd'}},
                          {3, 1, {33, 333, 'e'}},
                          {4, 1, {44, 444, 'f'}},
                          {5, 0, {55, 555, 'g'}}},
    *carPtr = NULL;                                 /* must initialize pointer NULL */
  size_t nelem = sizeof carObj/sizeof *carObj;      /* number of elements */
  
  if (!fillData (&carPtr, carObj, nelem))           /* validate function return */
    return 1;
  
  // sendData (&carPtr);
  
  for (size_t i = 0; i < nelem; i++)                /* output results */
  {
    printf ("%s(): number = %d, tagged = %u, id = %d, department = %d, initials = %c\n",
            __func__, carPtr[i].number, carPtr[i].tagged, carPtr[i].emp.id,
            carPtr[i].emp.department_id, carPtr[i].emp.initials);
  }
  
  free (carPtr);        /* free allocated memory */
}

Example Use/Output

$ ./bin/car_employee
main(): number = 1, tagged = 1, id = 11, department = 111, initials = c
main(): number = 2, tagged = 0, id = 22, department = 222, initials = d
main(): number = 3, tagged = 1, id = 33, department = 333, initials = e
main(): number = 4, tagged = 1, id = 44, department = 444, initials = f
main(): number = 5, tagged = 0, id = 55, department = 555, initials = g

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.

$ valgrind ./bin/car_employee
==14725== Memcheck, a memory error detector
==14725== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==14725== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==14725== Command: ./bin/car_employee
==14725==
main(): number = 1, tagged = 1, id = 11, department = 111, initials = c
main(): number = 2, tagged = 0, id = 22, department = 222, initials = d
main(): number = 3, tagged = 1, id = 33, department = 333, initials = e
main(): number = 4, tagged = 1, id = 44, department = 444, initials = f
main(): number = 5, tagged = 0, id = 55, department = 555, initials = g
==14725==
==14725== HEAP SUMMARY:
==14725==     in use at exit: 0 bytes in 0 blocks
==14725==   total heap usage: 2 allocs, 2 frees, 1,124 bytes allocated
==14725==
==14725== All heap blocks were freed -- no leaks are possible
==14725==
==14725== For counts of detected and suppressed errors, rerun with: -v
==14725== 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM