4

Let have a struct like this:

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

One function takes this struct by pointer and outputs the value of struct members. I want to print those values like this:

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);

How can I replace those multiple if conditions with a loop?

Lundin
  • 195,001
  • 40
  • 254
  • 396
Biswapriyo
  • 3,491
  • 3
  • 22
  • 42
  • 1
    You may be able to, if you have a union of a struct with a 3-int array. But why would you want to do that? You might as well just use an array to start with. – user12205 Aug 21 '18 at 06:35
  • 2
    Didi you consider changing the struct to an array type? Please provide more context to demonstrate why that is not an option. – Yunnosch Aug 21 '18 at 06:38
  • I think you should be able to loop through memory addresses starting from where `example` is located and then going with a step of `sizeof(int)` 3 times. You would have to cast result to integer. I think this solution would be rather hacky and not readible. There's nothing with this code in terms of readibility. Other option would be using `union`. – gonczor Aug 21 '18 at 06:40
  • there are only a few fields in the struct, why would you want to do that? Even if you can loop, you'll have to use generics since you can't print various types with the same format specifier – phuclv Aug 21 '18 at 06:49
  • @phuclv There's plenty of reason why you'd want to do something like this. Data serialization before sending data over some protocol, for example. – Lundin Aug 21 '18 at 06:50
  • @Lundin if you want to serialize then you can just read the whole struct as bytes instead of looping through elements and copy – phuclv Aug 21 '18 at 07:07
  • Please don't radically change the question once there are posted answers. If you have a follow-up question, then post a new question. I will rollback to the original post. – Lundin Aug 21 '18 at 07:29
  • duplicates: [iterating through a struct in c](https://stackoverflow.com/q/47396744/995714), [Iterate through structure elements of the same type](https://stackoverflow.com/q/42624543/995714), [C method for iterating through a struct's members like an array?](https://stackoverflow.com/q/14418595/995714). And for iterating fields with different types there's also a duplicate: [Is there any way to loop through a struct with elements of different types in C?](https://stackoverflow.com/q/1784782/995714) – phuclv Aug 21 '18 at 10:23
  • Possible duplicate of [Iterating over same type struct members in C](https://stackoverflow.com/q/1869776/995714) – phuclv Aug 21 '18 at 10:23
  • @Biswapriyo `offsetof` has already be mentioned in the first question in my comment. And I've only voted for one duplicate since I'm not a moderator, but it's really a duplicate so I'm not going to retract the vote – phuclv Aug 22 '18 at 14:58

3 Answers3

10

The best way is probably type punning over union. It allows you to use the same memory area with different variable representations, for example by giving each struct member individual names, while at the same time remaining able to loop through them.

#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]);
  }
}

If you can't change the struct definition, then the second best is some pointer arithmetic through a character type. This takes much more care though, so that you don't end up writing code with poorly-defined behavior - you need to be aware of things like alignment and strict aliasing. Character types are preferred to work with in case the struct turns more complex with different types of members, because character types are safe from aliasing issues.

Assuming uint8_t is a character type, then:

#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);
  }
}
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • but the OP has edited and now the struct contains both `int` and `long` – phuclv Aug 21 '18 at 07:07
  • @phuclv And that edit is not ok, so I did a rollback. You should be able to do so, too. – Lundin Aug 21 '18 at 07:30
  • @Biswapriyo It is probably better to ask a new question and link to this one if needed. And if you have an answer to your new question yourself, you can also post an answer to your own question - doing so is perfectly fine. – Lundin Aug 22 '18 at 15:15
1

The short answer is, you cannot do that easily. Members in structs are designed to be accessed by name, as opposed to elements in arrays which are designed to be indexed.

Computing addresses from offsets is a possibility but you still need to know the struct layout anyway — and on top, that layout can change with a compiler option or pragma.

Languages which carry type information into a more elaborate runtime environment, like C# and Java, allow you to enumerate struct members at run time, but C just creates an un-annotated data blob which you must know at compile time anyway, so you can as well enumerate the members explicitly. Of course you can use the usual C tricks; there is probably a serialization solution out there which generates code for accessing struct elements from some struct definition wrapped in a macro (ah, yes, the first google hit: https://gist.github.com/Rhomboid/8e48620badbb3d9b4c30).

The fact that you can index elements of arrays at run time is owed to the fact that the internal layout of an array is completely known (for example, it is defined to not contain padding).

Of course you can define a struct which holds an array of unions large enough to accommodate your largest element which carries its own type and "member" name information and then functions to interpret that information, computing the proper memory location, using the appropriate type cast and printf conversion string at run time. In effect you'd emulate some of the features of more elaborate run time environments.

Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62
0

If you are treating the struct variable as a separate then this is not possible

but if you treat them as an array then it is possible

For Example

struct Example {int member[3];};

int main()
{
     struct Example example;
     int i;
     for (i = 0; i < 3; ++i)
         example.member[i] = i;
}
Mr. Roshan
  • 1,777
  • 13
  • 33
  • Can you loop on that array? I mean it contains 3 elements but you're going till 19th element? – Kamaldeep singh Bhatia Aug 21 '18 at 06:47
  • I posted an example how it is possible to loop over the members without changing the struct definition. Though of course you still need to know the nature of the struct. – Lundin Aug 21 '18 at 06:48
  • 1
    Using `_Example` as your structure's name is discouraged. Names starting with an underscore followed by an uppercase letter are reserved words in standard C: e.g. [`_Bool`](https://en.cppreference.com/w/c/keyword/_Bool), [`_Generic`](https://en.cppreference.com/w/c/keyword/_Generic), [`_Atomic`](https://en.cppreference.com/w/c/keyword/_Atomic), etc. – joH1 Aug 21 '18 at 06:52