【问题标题】:How to loop through structure members?如何循环访问结构成员?
【发布时间】:2018-08-21 06:32:56
【问题描述】:

让我们有一个这样的结构:

struct _Example {
    int a;
    int b;
    int c;
} Example;

一个函数通过指针获取这个结构并输出结构成员的值。我想像这样打印这些值:

Example example;
func(&example);

if(example.a)
    printf("%d", example.a);

if(example.b)
    printf("%d", example.b);

if(example.c)
    printf("%d", example.c);

如何用循环替换那些多个if 条件?

【问题讨论】:

  • 如果你有一个结构体与一个 3-int 数组的联合,你也许可以做到。但是你为什么要这样做呢?您不妨只使用一个数组开始。
  • 你考虑过将结构体改成数组类型吗?请提供更多上下文来说明为什么这不是一个选项。
  • 我认为您应该能够从example所在的位置开始循环内存地址,然后以sizeof(int)的步长循环3次。您必须将结果转换为整数。我认为这个解决方案会相当笨拙且不可读。就可读性而言,这段代码没有任何意义。其他选项是使用union
  • 结构中只有几个字段,为什么要这样做?即使你可以循环,你也必须使用泛型,因为你不能用相同的格式说明符打印各种类型
  • @phuclv 你有很多理由想要做这样的事情。例如,通过某种协议发送数据之前的数据序列化。

标签: c


【解决方案1】:

最好的方法可能是在union 上键入双关语。它允许您使用具有不同变量表示的相同内存区域,例如通过给每个结构成员单独的名称,同时保持能够循环通过它们。

#include <stdio.h>

typedef union {
  struct  // anonymous struct, requires standard C compiler
  {
    int a;
    int b;
    int c;
  };
  int array[3];
} Example;


int main (void)
{
  Example ex = { .a=1, .b=2, .c=3 };

  for(size_t i=0; i<3; i++)
  {
    printf("%d\n", ex.array[i]);
  }
}

如果你不能改变结构定义,那么第二好的是通过字符类型进行一些指针运算。不过,这需要更加小心,这样您就不会最终编写出行为定义不明确的代码——您需要注意对齐和严格别名之类的事情。如果结构因不同类型的成员而变得更加复杂,则首选使用字符类型,因为字符类型不会出现别名问题。

假设uint8_t是一个字符类型,那么:

#include <stdio.h>
#include <stdint.h>

typedef union {
  struct
  {
    int a;
    int b;
    int c;
  };
} Example;


int main (void)
{
  Example ex = { .a=1, .b=2, .c=3 };
  uint8_t* begin = (uint8_t*)&ex;
  uint8_t* end   = begin + sizeof ex;


  for(uint8_t* i=begin; i!=end; i+=sizeof(int))
  {
    printf("%d\n", *(int*)i);
  }
}

【讨论】:

  • 但 OP 已编辑,现在结构包含 intlong
  • @phuclv 那个编辑不行,所以我做了一个回滚。你也应该可以这样做。
  • @Biswapriyo 如果需要,最好提出一个新问题并链接到这个问题。如果您自己有新问题的答案,您也可以发布自己问题的答案 - 这样做非常好。
【解决方案2】:

简短的回答是,你不能轻易做到这一点。结构中的成员被设计为按名称访问,而不是数组中的元素被设计为被索引。

从偏移量计算地址是可能的,但无论如何您仍然需要知道结构布局 - 最重要的是,该布局可以通过编译器选项或pragma 进行更改。

将类型信息带入更复杂的运行时环境的语言,如 C# 和 Java,允许您在运行时枚举结构成员,但 C 只是创建一个未注释的数据 blob,您必须在编译时了解它 无论如何,因此您也可以显式枚举成员。当然,您可以使用通常的 C 技巧;可能有一个序列化解决方案,它生成代码,用于从包含在宏中的某些结构定义访问结构元素(啊,是的,第一个谷歌点击:https://gist.github.com/Rhomboid/8e48620badbb3d9b4c30)。

您可以在运行时索引数组元素的事实归功于数组的内部布局是完全已知的(例如,它被定义为不包含填充)。

当然,您可以定义一个结构,该结构包含一个足够大的联合数组,以容纳最大的元素,该元素带有自己的类型和“成员”名称信息,然后使用函数来解释该信息,计算正确的内存位置,使用在运行时进行适当的类型转换和printf 转换字符串。实际上,您可以模拟更复杂的运行时环境的一些特性。

【讨论】:

    【解决方案3】:

    如果您将struct variable 视为单独的,那么这是不可能的

    但如果您将它们视为array 则有可能

    举例

    struct Example {int member[3];};
    
    int main()
    {
         struct Example example;
         int i;
         for (i = 0; i < 3; ++i)
             example.member[i] = i;
    }
    

    【讨论】:

    • 你能在那个数组上循环吗?我的意思是它包含 3 个元素,但你要到第 19 个元素?
    • 我发布了一个示例,如何在不更改结构定义的情况下循环成员。当然,您仍然需要了解结构的性质。
    • 不鼓励使用_Example 作为结构的名称。以下划线后跟大写字母开头的名称是标准 C 中的保留字:例如_Bool_Generic_Atomic
    猜你喜欢
    • 1970-01-01
    • 2012-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-30
    • 1970-01-01
    • 2022-01-19
    • 1970-01-01
    相关资源
    最近更新 更多