Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

struct with a pointer to a member of another struct? 1

Status
Not open for further replies.

fyrdman

Programmer
Apr 1, 2005
10
US
I have a project where I want to have the two general types of structures:

struct A {

unsigned char *a;

};

struct B {

unsigned char b;

};


The general idea is that I want a member of struct A to be a pointer a member of struct A (without knowledge of the structure of B, other than B has a member unsigned char b).

My question is how do I initialize *a to the address of b? (and then properly reference b through *a within a function?)


I wish to initialize an instance of struct A as follows:

const struct A constinrom = { …, <something that extracts the addr of b> , …};


I wish to use struct A as follows (with no knowledge in anyfunction of struct B other than what is provided by struct A) :

void anyfunction (const struct A *name) {

(set variable c to equal the value in b)

(modify the value of c)

(set value in b equal the value in value of c)

}


Thank you,

Fyrdman


 
So... I'll assume you're using the [tt]unsigned char *[/tt] type to try to get around the circular dependency.

Why not just:
Code:
struct b;

struct a
{
    struct b *bstruct;
    ...
};

struct b
{
    struct a *astruct;
    ...
};


If you have to use the [tt]unsigned char *[/tt] type for some other reason, you just need to cast it to the appropriate type of pointer before you try to use it.

In this case, the [tt]void *[/tt] type will more clearly indicate what you're trying to do...


Also, make sure the pointers are pointing to enough memory to hold objects of their respective types.
 
chipperMDW:

Background: I have been an assembly programmer of 5 years experience. My C is completely self-taught out of Kernighan and Ritche and only have a couple of recent embedded C projects under my belt (C51 and PIC16).

I have not used the code you suggested because I expect information about struct A to be hidden from struct B. Nor do I expect struct A to have any information about struct B other than initialization of a pointer to a specific member of struct B.

Essentially, I want component A to make use of component B (or C,D,E...) with A having to know only one thing about B (or C,D,E...), *and* with B knowing *nothing* about A. (yeah, no cyclical dependency) So, the code you suggest isn’t quite what I’m looking for, especially since there are many different “struct B”s in the application that I want different instances of strut A to link to.

Every time I do :
const struct A name = { …, < addr of b> , …};

b may a member of a different struct.

I may need to make a common interface struct associated with A, and A may only link to those other structs that support that interface, but I’d rather not.

(I used unsigned char * in struct A because that is the type this solution will always point to. A general solution would be more idea, but I think I’m weak on the area of casts and voids, and I need something that works. But that is a *good* suggestion, and something I will try to become comfortable with.)

Fyrdman
 
I don't think I understand fully, but this was my attempt at a start.
Code:
#include <stdio.h>

struct A
{
   unsigned char *a;
};

struct B
{
   unsigned char b;
};

struct B MyB = {'*'};

void anyfunction (const struct A *name)
{
   unsigned char c;
   c = MyB.b; /* set variable c to equal the value in b */
   ++c;       /* modify the value of c */
   MyB.b = c; /* set value in b equal the value in value of c */
}

const struct A MyA = {&MyB.b};

int main()
{
   printf("*A.a = %d\n", *MyA.a);
   anyfunction(&MyA);
   printf("*A.a = %d\n", *MyA.a);
   return 0;
}

/* my output
*A.a = 42
*A.a = 43
*/
 
Ah, I misunderstood your post. Sorry.


How about just writing a function that takes a [tt]struct B *[/tt] and returns the address of its [tt]b[/tt] member?

Code:
void *addr_of_b (const B *bstruct)
{
    return bstruct + offsetof(struct B, b);
}

All you need in the struct A module is:
Code:
struct B;  /* Just a forward declaration */

...

struct B *bstruct;  /* Just a pointer */

...

const struct A constinrom =  { …, addr_of_b(bstruct), …};
 
Er,
Code:
void *addr_of_b (const B *bstruct)
{
    return ([highlight](unsigned char *)[/highlight]bstruct) + offsetof(struct B, b);
}
 
My attempt, #2.
Code:
#include <stdio.h>

struct A
{
   unsigned char *a;
};

void anyfunction(const struct A *name)
{
   unsigned char c;
   c = *name->a; /* set variable c to equal the value in b */
   ++c;          /* modify the value of c */
   *name->a = c; /* set value in b equal the value in value of c */
}

void foo(const struct A *name)
{
   ++*name->a;   /* do the above without a temporary */
}

struct B
{
   unsigned char  b;
} B = {'*'};

struct A A = {&B.b};

int main()
{
   printf("*A.a = %d, B.b = %d\n", *A.a, B.b);
   anyfunction(&A);
   printf("*A.a = %d, B.b = %d\n", *A.a, B.b);
   foo(&A);
   printf("*A.a = %d, B.b = %d\n", *A.a, B.b);
   return 0;
}

/* my output
*A.a = 42, B.b = 42
*A.a = 43, B.b = 43
*A.a = 44, B.b = 44
*/
 

ChipperMDW,

You are giving me very educational suggestions, but we're not there yet.

>How about just writing a function that takes a struct B *
>and returns the address of its b member?

That is a very interesting idea. But I neglected to enphasize that the initialization must be in ROM. To conserve RAM, each of my objects has some RAM space, but I put everything I can in ROM. The declaration would actually be
Code:
const ROM struct A constinrom =  { …, addr_of_b(bstruct), …};
The rom qualifier is a microchip C18 extension that compiles constinrom in to the Harvard machine’s program space. So addr_of_b *must* be determined at build time.

Maybe it could be done by adding another layer of abstraction and have the ROMed struct point to a RAM value initialized at runtime, but things are complex enough, and that gets back to eating up my RAM.

(In about an hour I’m heading out, but I try to check in tomorrow.)

Fyrdman
 
Then how about finding [tt]offsetof(struct B, b)[/tt] for your machine type, then defining it as a constant accessible to the code that initializes the [tt]struct A[/tt]?

That's non-portable, but it sounds like you're already making something fairly machine-specific.


If you need it to be portable, or if you might be changing the layout of [tt]struct B[/tt], you can just write a short program to generate the header for you.

e.g.
Code:
...

int main ()
{
    FILE *header = fopen ("boffset.h", "w");

    ...

    fprintf (header,
             "#define BOFFSET %d",
             offsetof(struct B, b));

    ...
}
 
OK, DaveSinkula,

What you have is pretty much what I tried, and should work. But let’s take it you the next notch to what I really need. I deal with the fact that I can’t have RAM and ROM in the same struct, I and another layer of struct that points to my RAM struct and ROM struct for each object.

The struct I really want to point to is a struct with a pointer to the RAM I want to reference. The pointer doing that pointing is within a struct pointed to by another struct. I may have the concept of what Dave is doing, but my competency fails me as I struggle to make the multiple indirect reference.

Below is a far as I got before I decided to ask for help. (Forgive any errors in transposition as I am rapidly trying to write the posts on short time.)

Code:
static struct timer_space S_TIMER_space = {0};
unsigned char timer_class S_TIMER_class = {&S_Time,/pointer to index here/};

    // S_Time is a array of struct timer_setting 
                                                        
unsigned char t S_T = {&S_TIMER_space,&S_TIMER_class};

void Reset_timer(rom struct t *name){
    (*(*name).space).timer_field.time = (*(*name).class).setting[(*(*(*name).class).index)].value - 1 ;

    return;
}


//T Structure --------------------------------

union timer_field {
         unsigned  char t8bit; 
         unsigned  int  time;
         struct    tflags {
                       unsigned :8;
                       unsigned :tbits-8;
                       unsigned Timeout:1;  
                       } tflags;
         };

struct timer_space {
         union      timer_field    timer_field;
         };

struct timer_setting {
                    tsize      value   ;
                    bit       *units   ; //tick
         };

struct timer_class {
         rom struct timer_setting *setting ;
         static unsigned char *index   ;
				// index may be RAM in any other struct
         };

struct t {
         struct     timer_space   *space   ;
         rom struct timer_class   *class   ;
         };


//Sample “stuct B”

//F Structure --------------------------------

struct Act {
           void (*fp)(void);
    };

struct T {
          unsigned  char    N;
          struct bit_field  O;
    };

struct F_space {
          unsigned  char    index;
          struct bit_field  field;
    };

struct F_class {
    rom struct T       *table;
    rom struct Act     *act;
    };

struct F {
    struct     F_space *space;
    rom struct F_class *class;
    };
 
Maybe you should look at implementing all of this
data and the data delvery methodology in a shared
memory segment where the data can be dealt with in
a way that you design.
 
I did'n actually understood, where is actually the problem by initializing?

Code:
#include <stdio.h>
#include <stdlib.h>

struct
{
   int ii;
   unsigned char b;
   int jj;
} B = {123, 'A', 234};

struct
{
   int kk;
   unsigned char *a;
   int ll;
} A = {345, &B.b, 456};

               
int main(void)
{
   printf("%c\n", *A.a);

return(0);
}

The output result is 'A'.
 
I beg patience as I try this again,

I use a set up where each static object consists of a struct of pointers to various struct

struct X {
struct X1;
struct X2;
struct X3;
….
};

struct Y {
struct Y1;
struct Y2;
struct Y3;
….
};

The Problem: This has worked very well for me, but now I have run into trouble initializing at *build*time* a pointer within a *nested* struct that points to a member in any other nested struct. That is, how do I initialize at build time some member (a pointer) within, say, struct X1 to hold the address of some member in an instance of Y3.

Background: It would be most convenient for me to pass all information with respect to an object with a single pointer to a struct. However, due to the different functional properties of different memory types as well as resource limitation of different memory types, I need the struct for each object to include different memory types. But a single struct may only have contiguous memory. This is why I’m nesting structs. I want one struct to directly reference another struct in cases of closely coupled objects, but no more coupling than that. I want Y to “know” nothing about X and I only want X to know only one thing about Y.

My attempt at a solution follows. My problem seems to be at figuring out the initialization of struct A_rom A_rom = {…. as commented near the bottome of the code below. (It could be I don’t know what I’m doing, or it could be I’m running up against a compiler limitation)

Code:
// These qualifiers are C extensions specific to PIC C18 compiler.
//   rom = qualifier to place item in ROM space
//   ram = qualifier to place item in RAM space (default)
//
// Other compilers will have some means of directing allocation 
// to specific memory areas, so these symbols abstract those means:
#define RAM ram  
#define ROM rom 
//#define RAM static // for example for Hi-Tech PICC or Cygnal
//#define ROM const  // 

                       // a common enough convention in 8-bit micro programming?
#define Byte unsigned char


//A Structure --------------------------------

struct setting {
     Byte value ;
         };

struct A_ram {
     RAM Byte count;
     };

struct A_rom {
     ROM struct setting *table;
     RAM        Byte    *index;
             // index may be RAM in any other struct
     };

struct A {
     RAM struct A_ram *RAMspace;
     ROM struct A_rom *ROMspace;
     };


//Sample “struct B”

struct B_ram {
     RAM Byte  index;
     };

struct B_rom {              
     ROM Byte  *table;
     };

struct B {
     RAM struct B_ram *RAMspace;
     ROM struct B_rom *ROMspace;
     };



Byte S[5] = {0,1,2,3,4};
Byte T[5] = {10,11,12,13,14};

//non-dynamic build time memory allocation to comply with customer requirements
RAM struct B_ram B_ram = {42};
ROM struct B_rom B_rom = {&T};
ROM struct B     B     = {&B_ram,&B_rom};
    
RAM struct A_ram A_ram = {5};
//ROM struct A_rom A_rom = {&S , &B.RAMspace->index}; // Compiler Runtime Crash
  ROM struct A_rom A_rom = {&S , &B.RAMspace.index};  // Compiler says to use -> and crashes
ROM struct A     A     = {&A_ram,&A_rom};

                                                        
//haven't tested this, but it compiles
void playwithA(rom struct A *name){
    (*(*name).RAMspace).count = (*(*name).ROMspace).table[(*(*(*name).ROMspace).index)].value - 1 ;

    return;
}
 
Err,

struct X {
struct X1 *#;
struct X2 *#;
struct X3 *#;
….
};

struct Y {
struct Y1 *#;
struct Y2 *#;
struct Y3 *#;
….
};


 
Might this be getting closer?
Code:
[red]struct setting[/red] S[5] = {[red]{[/red]0[red]}[/red],[red]{[/red]1[red]}[/red],[red]{[/red]2[red]}[/red],[red]{[/red]3[red]}[/red],[red]{[/red]4[red]}[/red]};
Byte T[5] = {10,11,12,13,14};

RAM struct B_ram B_ram = {42};
ROM struct B_rom B_rom = {[red]T[/red]};
ROM struct B     B     = {&B_ram,&B_rom};

RAM struct A_ram A_ram = {5};
ROM struct A_rom A_rom = {[red]S[/red], &[red]B_ram.index[/red]};
ROM struct A     A     = {&A_ram,&A_rom};
 

That got it working! (I left the & in)

Dang, I was so fixed on going through the top struct, that I didn't consider going through the lowest struct. Blind! Since I have to know the whole "path" to the lowest struct via the highest, there is actually *less" dependency just referencing the lowest struct!

Thank you, even if you were stating rather the obvious, what I needed was the obvious stated!

Code:
struct setting S[5] = {0,1,2,3,4};
Byte T[5] = {10,11,12,13,14};

RAM struct B_ram B_ram = {42};
ROM struct B_rom B_rom = {&T};
ROM struct B     B     = {&B_ram,&B_rom};

RAM struct A_ram A_ram = {5};
ROM struct A_rom A_rom = {&S, &B_ram.index};
ROM struct A     A     = {&A_ram,&A_rom};
 
That got it working! (I left the & in)
I'm glad it's working. But I removed the [tt]&[/tt] because although the resulting value may be the same, the type is not correct. Or, as lint puts it...
Code:
[Warning 545] Suspicious use of &
[Error 64] Type mismatch (initialization) (unsigned char * = unsigned char (*)[5])
[Warning 545] Suspicious use of &
[Error 64] Type mismatch (initialization) (struct setting * = struct setting (*)[5])
 

DaveSinkula :
I'm glad it's working. But I removed the & because although the resulting value may be the same, the type is not correct.

More precisely it works on project code my C18 compiler. Maybe my there is a subtle difference between my project and sample code. Maybe there is a tiger trap waiting for me in my code.

So, if a is an array, then pa = &a[0] is the same as pa = a, but what about pa = &a?

C18 has ‘lint’ built in? (judging from the compiler messages)?

So, now maybe you could direct me towards an education in the pros and cons (especially the cons) of the void * type?

Thanks

Fyrdman
 
So, if a is an array, then pa = &a[0] is the same as pa = a, but what about pa = &a?
Try this for starters.
So, now maybe you could direct me towards an education in the pros and cons (especially the cons) of the void * type?
Perhaps that should be a new thread? Do you have some sort of specific example(s)/situation(s) as a starting point?
 
Try this [link] for starters.

*That* link is very helpful, but not yet, as I will have to read it 3-4 times I think. [dazed] Thanks!

OK, for array a
pa = &a[0] is the same as pa = a and pa = (a+0)
but
pa = &a is not *exactly* the same as pa = a.

I’ll have to check over my code in a new light.


Perhaps that [void * question] should be a new thread? Do you have some sort of specific example(s)/situation(s) as a starting point?

I have no example, as I don’t think I understand what it is for. Perhaps there is a FAQ “What is void * good for and what should I watch out for?” but yes, really a different thread.

 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top