C 使用双指针迭代结构数组并使用双指针访问元素

C 使用双指针迭代结构数组并使用双指针访问元素,c,pointers,struct,C,Pointers,Struct,我有一辆嵌套结构车 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, '

我有一辆嵌套结构车

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;
然后我有一个结构车的对象

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;
接下来,我有一个指向struct car的指针

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;
我必须用对象carObj中的各种元素填充这个“carPtr”指针。 我将为汽车的每个对象分配一个新内存,并复制其中的数组元素

#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;
}
#包括
#包括
#包括
结构雇员{
int-id;
未签名的int部门id;
字符首字母;
};
结构车{
整数;
无符号整数标记;
结构员工emp;
};
结构车carObj[5]={
{1,1,{11,111,'c'},
{2,0,{22,222,'d'},
{3,1,{33,333,'e'},
{4,1,{44444,'f'},
{5,0,{55555,'g'}
};
结构车*carPtr;
无效填充数据(结构车**t)
{
int-cnt=0;
对于(int i=0;i<5;i++)
{
cnt++;
如果(*t==NULL)
{
*t=calloc(cnt,sizeof(struct-car));
}
其他的
{
*t=realloc(*t,(cnt*sizeof(结构车));
}
memcpy(&uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,&carObj[i],sizeof(struct car));
printf(“%s():编号=%d,标记=%d,id=%d,部门=%d,缩写=%c\n“,\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;
}    
}
int main()
{   
sendData&carPtr;
对于(int i=0;i<5;i++)
{
printf(“%s():编号=%d,标记=%d,id=%d,部门=%d,缩写=%c\n“,\uu func\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;
}
返回0;
}
问题:如何使用双指针访问单个元素? 在上面的memcpy中,我标记了一个空白“\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu”。我需要在那里写什么语句来访问最近分配的指针?
在printf中,我应该写什么语句来访问指针的元素。

您很接近了,但是需要做一些改进:

  • 避免使用全局变量
  • fillData()
    选择一个有意义的返回类型,它将在调用者中指示成功/失败
  • 在调用
    fillData()
    之前,您知道元素的数量,因此只需分配一次并复制,然后
  • 将必要的信息作为参数传递给函数
  • 在需要变量的范围内声明变量。对于
    carObj[]
    ,这将是:

    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;
    
    由于在
    fillData()
    中进行分配,因此必须有一种方法向调用函数指示分配是成功还是失败,以便在使用分配的内存块之前在调用方中进行验证,例如

    /* 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 */
    }
    
    注意:通过使用取消引用的指针设置分配的类型大小,您永远不会出错)

    如上所示,您知道元素的数量
    nelem
    ,因此无需循环
    fillData()
    ,只需分配、验证和复制即可

    通过将必要的参数传递给
    fillData()
    ,可以避免使用任何全局变量,并且通过选择有意义的返回,可以验证块是否已在
    main()
    中分配和填充。您在
    main()
    中的调用将是:

    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;
    
    总而言之,你应该:

    #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 */
    }
    
    内存使用/错误检查

    在您编写的任何动态分配内存的代码中,对于所分配的任何内存块,您有两个责任:(1)始终保留指向内存块起始地址的指针,以便(2)在不再需要它时可以释放它

    必须使用内存错误检查程序,以确保您不会试图访问内存或写入超出/超出分配的块的边界,尝试在未初始化的值上读取或基于条件跳转,最后确认释放所有已分配的内存

    对于Linux,
    valgrind
    是正常的选择。每个平台都有类似的内存检查器。它们都很容易使用,只需运行程序即可

    $ 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)
    
    始终确认已释放所有已分配的内存,并且没有内存错误


    仔细检查一下,如果您还有其他问题,请告诉我。

    memcpy(*t,&carObj[i],sizeof(struct-car))
    (*t)->number
    ,等等。更简单的是,结构是可赋值的左值,:
    **t=carObj[i]
    (但重新分配并始终分配给第一个元素是没有意义的)
    (*t)[i]=carObj[i]。此外,如果您知道项目的数量,为什么不一次性为所有项目分配足够的空间,而不是在循环中重复重新分配?谢谢各位的建议。Jonathan我认为在memcpy中,如果我们使用*t,那么在重新分配内存后,它将始终覆盖第一个元素地址。这是一个示例代码,而在实际代码中,我必须分配内存并在代码中填充数据。Dmitri我不知道实时环境中的项目数量,而且我也不会拥有carObj结构,在这种情况下,我将不得不自己填充数据。我想你需要
    struct employee*emp[]因为员工可以使用多辆车,一辆车可以被多个员工使用。需要动态分配和取消分配非常感谢David为您所做的一切努力。仅供参考,在实际项目环境中,我不会提前知道元素的确切数量。在实时环境中,我将接收一个struct car对象,该对象将存储在动态分配的指针中。因此,我每次都必须为每个汽车对象分配内存,并将接收到的汽车信息存储在其中。假设上面的代码正在服务器上运行,它将在服务器上保存汽车详细信息。客户端每次都会向服务器发送一个汽车信息,服务器会保存它。好的,在这种情况下,你必须有办法找到最后一个元素(比如sentinel NULL或bla)