访问指向结构内部结构的指针数组

时间:2018-08-03 11:39:26

标签: c

我试图在结构中分配char数组值并检索数据,但检索时遇到一些问题。

struct first {
    int a;
    int b; 
};

struct second {
    int c;
    int d;
    struct first * f_t[2]; 
};

int main()
{
    struct second *p;
    char a[24] = {1, 0, 0, 0, 
                  2, 0, 0, 0, 
                  3, 0, 0, 0, 
                  4, 0, 0, 0,
                  5, 0, 0, 0,
                  6, 0, 0, 0};
    p = (struct second *)a;
    p->f_t[0] = (struct first *) (a + 8);
    p->f_t[1] = (struct first *) (a + 16);
    printf("%d %d %d %d %d %d", 
         p->c, p->d, p->f_t[0]->a, p->f_t[0]->b, p->f_t[1]->a, p->f_t[1]->b);
}

输出:

1 2 1245008 1245016 1245024 6

前两个值正确出现(12),但其余值似乎有些垃圾值。

请帮助识别一些错误的初始化方法或其他相同方法。

3 个答案:

答案 0 :(得分:0)

首先,永远不要假定结构中的填充。它的运行相当可靠,但是从技术上来说是未定义的行为。

更重要的是,您正在违反严格的别名规则。编译器假设您永远不会使用不同类型的指针(在这种情况下为struct first*char*)访问同一内存位置。您可以使用-fno-strict-aliasing来解决此问题,也可以使用联合来进行类型校正(通常被称为类型校正)。转到here了解更多信息。

此代码的更正确版本为-

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

struct first {
  int a;
  int b; 
};
struct second {
  int c;
  int d;
  struct first * f_t[2]; 
};
int main()
{
  struct second *p;
  char a[24] =        {1,0,0,0,2,0,0,0,3,0,0,0,4,0,0,0,5,0,0,0,6,0,0,0};
  p = alloca(sizeof(struct second));
  memset(p, 0, sizeof(struct second));
  memcpy(p, a, sizeof(int)*2);
  p->f_t[0]= alloca(sizeof(struct first));
  p->f_t[1]= alloca(sizeof(struct first));
  memset(p->f_t[1], 0, sizeof(struct first));
  memset(p->f_t[0], 0, sizeof(struct first));
  memcpy(p->f_t[0], a + 8, sizeof(struct first));
  memcpy(p->f_t[1], a + 16, sizeof(struct first));
  printf("%d %d %d %d %d %d",p->c,p->d,p->f_t[0]->a,p->f_t[0]->b,p->f_t[1]->a,p->f_t[1]->b);

}

注意:使用alloca()是错误的做法。除非您知道自己在做什么,否则不要使用它。在这里很好,因为它只是在堆栈上分配的一个小结构,但通常避开它,而使用malloc()或仅使用局部变量。

从技术上讲,这仍然属于未定义的行为,因为我们假设如何填充结构,这是特定于实现的,但是大多数平台使用自对齐数据类型,并且在使用正确对齐的结构时速度要快得多,因此很容易猜测。有关更多信息,请转到here我仍然不鼓励这样做,因为这是未定义的行为,很容易弄乱。

此外,下次,请确保提供一个最小,完整和可编译的示例。

答案 1 :(得分:0)

好吧,严格的别名规则,可能的填充,变量大小,对齐方式会带来很多(潜在)问题。

但是,假设您的编译器设置为no-strict-aliasing,并且所有大小,填充和对齐方式都匹配。

然后看看这些行:

p->f_t[0]=(Struct first *) (a+8);
p->f_t[1]=(Struct first *) (a+16);

他们将要做的是覆盖a[8]并使用指针的值(即地址)转发。因此,a不再等于初始化值。

尝试一下以了解自己的情况:

// Print all element in a
p->f_t[0]=(Struct first *) (a+8);
// Print all element in a
p->f_t[1]=(Struct first *) (a+16);
// Print all element in a

因此,您的问题是您尝试使用a[8] to a[23]进行存储

a)两个指针

b)首先两个结构

不可能的原因

答案 2 :(得分:0)

其他评论和帖子已经解决了将一种类型转换为另一种类型所伴随的 strict aliasing 问题,并且还详细介绍了其他问题。

从您现有的代码看来,您正在尝试使用struct引用pointer variables数组的每个成员。

此答案仅限于以简单的方式说明我认为您想做的事情:(请阅读有关移植物的嵌入式注释)

typedef struct  { //using struct instead of Struct
  int a;
  int b; 
}FIRST;           //create typedef tag (improved readability throughout code)
typedef struct  {
  int c;
  int d;
  FIRST f_t[2]; // changed from *[] to [] 
}SECOND;        // (the rest of your post indicates that is what you meant) 

int main()
{
  SECOND *p1; //for illustration, create two pointers to reference 2 elements of arr[2]
  SECOND *p2;

  //Create array of SECOND, populate with unique values in each location
  //to provide verification during printf that assignments were made correcly.                                                                                                                                             
  SECOND arr[2] = {{1,2,{3,4,5,6}},{7,8,{9,10,11,12}}}; 
                  //note values are arranged to follow the definitions of 2 elements of SECOND arr[2]. 

  //Create 2 pointers, 1 for each element of SECOND arr[2]
  p1 = &arr[0]; //assigns address of 1st element of SECOND arr[0], to pointer p1

  p2 = &arr[1];//assigns address of 1st element of SECOND arr[1], to pointer p2

  printf("Elements of p1[0]:\n%d, %d, %d, %d, %d, %d\n", p1[0].c, p1[0].d, p1[0].f_t[0].a,p1[0].f_t[0].b,p1[0].f_t[1].a,p1[0].f_t[1].b );
  printf("Elements of p2[0]:\n%d, %d, %d, %d, %d, %d\n", p2[0].c, p2[0].d, p2[0].f_t[0].a,p2[0].f_t[0].b,p2[0].f_t[1].a,p2[0].f_t[1].b );
  getchar();//stop execution to view result (needed in my environment)

}