简体   繁体   中英

C: Pointer to struct element inside the struct

For ease of printing, I want to create one array of pointers to the elements of my struct. How can I do that? My attempt is on the last line of the struct. Just as a note, this struct has other values than int, that are now showed here. I want this sort of indexing only for the ints.

struct status {
    int lastinputVac;
    int inputVac;

    int outputVac;

    /* The unit is % */
    int outputpower;

    int outputHz;

    /* The unit is % */
    int batterylevel;

    int temperatureC;

    int *ordered_values[] = { &lastinputVac, &inputVac, &outputVac, &outputpower, &outputHz, &batterylevel, &temperatureC };
}

Just bite the bullet and write the extra code to print it with struct member access. It's not worth compromising your design to save a few lines of code.

Imagine you have to expose this struct in a library some day. Do you really want to load the user down with this weird pointer array that serves no real purpose?

So the question was "I want to create one array of pointers to the elements of my struct." and mentions that the elements might be something else than int.

I wrote a generic solution that might also need a lot of improvement in terms of safety or language conformance and design but it shows one solution to the question below. The code below calculates the size of the struct assuming that the elements of the struct are all the same size, then constructs an array of pointers to the elements and the print_array function iterates through those elements by printing them out. I could have overloaded the ostream<< operator too but I wanted to keep this simple as much as possible.

  #include "stdafx.h"

  struct status {
      int lastinputVac;
      int inputVac;
      int outputVac;

      /* The unit is % */
      int outputpower;

      int outputHz;

      /* The unit is % */
      int batterylevel;

      int temperatureC;
};

//template<typename T>
#include <vector>
#include <algorithm>
#include <iostream>
#include <iterator>

// hide this into a library 
template<typename T>
size_t build_array(const status& val, T*** vecptr)
{
const size_t _size = sizeof(status) / sizeof(int);
*vecptr = (T**)malloc( sizeof(T*) *_size );

for (size_t i = 0; i < _size; ++i) {
    (*vecptr)[i] = ((T *)((&val)+i));
}
return _size;
}

template<typename T>
void free_array(T** vecptr)
{
free(vecptr);
}

template<typename T>
void print_array(T **vecptr, size_t size) 
{
for (size_t i = 0; i < size; i++)
    std::cout << *(*vecptr + i) << std::endl;
}

//T1 is the type of the struct and T2 is the type of the elements
template<typename T1, typename T2>
class PrintStruct
{
private:
    T2 **m_vecptr;
    size_t m_size;

public:
    PrintStruct(T1 t) {
        m_size = build_array<T2>(t, &m_vecptr);
        print_array(m_vecptr, m_size);
    }

    ~PrintStruct() {
        free_array(m_vecptr);
    }
 };

 int _tmain(int argc, _TCHAR* argv[])
 {
 status _status;
 _status.batterylevel = 1;
 _status.inputVac = 2;
 _status.lastinputVac = 3;
 _status.outputHz = 4;
 _status.outputpower = 5;
 _status.outputVac = 6;
 _status.temperatureC = 7;

 PrintStruct<status, int> _ps(_status);
 return 0;
 }

This way it is impossible.

In C++ there is a concept of member pointers ("relative" pointers), that is what you really need.

In your case and C I would propose always to use an array of ints and just to name the indices

struct status {
   int values[7];
}
..
const int lastinputVac = 0;  // or upper case when you like, or use #define
..
status a;
a.values[lastinputVac] = 27;
int z = a.values[lastinputVac];

You get the idea??

By the way, you could do both approach with union, offering a compiled access with name AND a way via the index of an array

You can't get the address of a struct member independently of a struct variable instance; IOW, you'd have to do something like

struct status foo;
int *ordered_values[] = {&foo.lastInputVac, &foo.inputVac, ...};

However, given that all your struct members are int s, you could do some type punning a la

int *ordered_values = (int *) &foo;
for (i = 0; i < 7; i++)
  printf("%d\n", ordered_values[i]);

This should work; the address of a struct instance is the same as the address of its first member, and since all the members are int s, there shouldn't be any alignment or padding issues. I don't recommend the practice, but for your purposes it should work well enough.

From reading your comments, it looks like you have a lot of elements to the struct, but you only want to print some of them. Here are my thoughts:

1) Don't put this array in the struct! It's static -- it won't ever change for any struct status so it's wasteful and un-intuitive to have it in the struct.

2) Have you thought about separating these values out into their own struct? You can then use the following paradigm:

enum status {
    LASTINPUT_VAC,
    INPUT_VAC,
    OUTPUT_VAC,
    OUTPUT_PWR,
    OUTPUT_HZ,
    BATT_LVL,
    TEMPERATURE,
    STATUS_MAX
};

struct system {
    char name[MAXNAME];
    long other_data;
    float coords[2];
    char *whatever_you_have_here;
    int status[STATUS_MAX];
};

Then to print/use one you reference it by:

struct system sys;
int temp = sys.status[TEMPERATURE];
for (int i = 0; i < STATUS_MAX; i++)
    printf("%d\n", sys.status[i]);

That may not fit your exact needs, but I just wanted to put that out there as another way you can do this sort of thing in C. Cheers!

There is an other approach coming in my mind. My observation is, the members are in the right order, but you probably want an easy access by indices's.

But warning: you have to care about padding. padding may misalign ints. However, in the case of int32 I would expect no problem.

Some would consider this as too hacky, however, it is an possibility

struct status {
  int lastinputVac;
  int inputVac;

  int outputVac;

/* The unit is % */
  int outputpower;

  int outputHz;

  /* The unit is % */
  int batterylevel;

  int temperatureC;
} stat;

int* a = &stat;

int maxIndex = (&stat.temperatureC - &stat.lastinputVac) / sizeof (stat); // should be 6
 // you could build named constants for that
a[0]; // lastinputVac
a[1]; // inputVac
a[2]; // outputVac
a[3]; // outputpower
a[4]; // outputHz
a[5]; // batterylevel
a[6]; // temperatureC

Union the struct with an array. Consult your compiler documentation regarding alignment.

union {

    struct {
       int someInt0;
       int someInt1;    
    }; //anonymous

    int arry[2];

} thing;

~

val0 = thing.someInt0; //structure access
val1 = thing.arry[0]; //gcc, C18, XC8, and XC32 handle this with the top value indexed to 0

if (val0 == val1) { 
    puts("this works");
}

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