1

I have a complex structure which looks like this.

struct a
{
  struct b
  {
    int b_inner_int;
    char b_inner_char;
  }x;

  struct c
  {
    int c_inner_int;
    char c_inner_char;
  }y;

}z;

I use a function, that takes address of "struct c" as an argument. Now I want this function to copy the values of "struct c" to "struct b". The function call that I make in the main function may look like this.

copy_val(&z.y);

Now, how do I define copy_val? Any suggestions? If i define a pointer of type struct c, like below it isn't working.

void copy_val(struct c *addr)
{
    struct c *tmp=addr;
    int tmp_int=tmp->c_inner_int;
    int tmp_char=tmp->c_inner_char;

    tmp=tmp-1; /** assuming that b and c are of same type and decrementing pointer by 1 takes to beginning of b **/

    tmp->b_inner_int=tmp_int;
    tmp->b_inner_char=tmp_char;
}
dragosht
  • 3,237
  • 2
  • 23
  • 32
Sparrow
  • 195
  • 2
  • 5
  • 15
  • 1
    Try something, share it, and show what you have tried, and then if you had problems, we can discuss it. SO isn't the place to have us write your code. I will say that a function that is copying from one to the other will need both inner structure addresses passed to it, if you insist on passing the inner structures.. – JohnH Aug 13 '14 at 13:50
  • 1
    Don't you mean that you want to copy from the `c` structure to the `b` structure? – Some programmer dude Aug 13 '14 at 13:53
  • yes that was a typo... I mean copying from c to b... @JoachimPileborg – Sparrow Aug 13 '14 at 13:57
  • @JohnH Was editing the post... I have posted what I have tried now – Sparrow Aug 13 '14 at 13:58
  • And why not simply have a `copy_val` function that takes the whole `a` structure, and then do e.g. `z.x = z.y;` – Some programmer dude Aug 13 '14 at 13:59
  • 2
    if struct b and struct c are the same then just define a single struct and use it for two different variables. Then it would be a matter of just assigning one variable to the other. – Richard Chambers Aug 13 '14 at 13:59
  • 3
    and I would not count on the decrement of the tmp pointer to get you to struct b either. no telling what the compiler may have done to the struct a layout. – Richard Chambers Aug 13 '14 at 14:00
  • Yes, use `offsetof` (or just pass the address of `struct a` in the first place) to get the address of `struct b` (I think, that's still not strictly conforming, though). – mafso Aug 13 '14 at 14:03

5 Answers5

4
#include <stdio.h>
#include <stddef.h>

struct a
{
  struct b
  {
    int b_inner_int;
    char b_inner_char;
  }x;

  struct c
  {
    int c_inner_int;
    char c_inner_char;
  }y;

}z;

void copy_val(struct c *addr){
    size_t offset_c = offsetof(struct a, y);
    size_t offset_b = offsetof(struct a, x);
    struct b *bp = (struct b*)((char*)addr - offset_c + offset_b);
    bp->b_inner_int = addr->c_inner_int;
    bp->b_inner_char = addr->c_inner_char;
}

int main(void){
    z.y.c_inner_int = 1;
    z.y.c_inner_char = '1';
    copy_val(&z.y);
    printf("%d, %c\n", z.x.b_inner_int, z.x.b_inner_char);
    return 0;
}
BLUEPIXY
  • 39,699
  • 7
  • 33
  • 70
2

You say that you want to take the address of a structure member, and reference other members based on that.

That's a bit iffy; the natural thing to do would be to take the address of the top-level structure whose members you want to work with, i.e. a struct a.

Also, it can be simplified:

struct a
{
  struct b
  {
    int b_inner_int;
    char b_inner_char;
  } x, y;
} z;

With that done, you can do just:

z.x = z.y;

to copy the entire value of type struct b.

unwind
  • 391,730
  • 64
  • 469
  • 606
1

If you pass like this copy_val(&z.y); you can't catch it in proper way because you have structure inside structure. so try to pass whole structure and copy it-

copy_val(&z);

Try the following code-

void copy_val(struct z *addr)
{
addr->x.b_inner_int= addr->y.c_inner_int;
addr->x.b_inner_char= addr->y.c_inner_char;
}
Sathish
  • 3,740
  • 1
  • 17
  • 28
1

Your assumption that b and c are the same type is invalid, according to the C standard section 6.2.7 p.1 (emphasis mine):

...if one member of the pair is declared with a name, the other is declared with the same name.

Because the structs use different member names, they are not considered compatible, thus your assumption is invalid.

Further, even if they are the exact same type, the tmp=tmp-1; statement is not valid: while sizeof(struct b) may report something like 8, this does not mean there's zero padding between the two structures.

Instead, you should use the offsetof macro to get the address of z, given the address of z.y:

struct a *z = (struct a *)((char *)addr - offsetof(struct a, y));

Note, however, that this may break aliasing assumptions, as part of the object pointing to z is also pointed to by y, but the pointers are not compatible.

Drew McGowen
  • 11,471
  • 1
  • 31
  • 57
  • This doesn't break aliasing assumptions, what `z` points to, _is_ a `struct a` object (so in `*z` declared and effective types match). But I think, it's out-of-bounds access: `(char *)addr` is a pointer to a `sizeof *addr `-sized array, pointing to its first byte. An implementation with bounds-checking (which is allowed) may throw an exception. – mafso Aug 13 '14 at 14:12
  • @mafso actually, since `addr` is assumed to be the address of a `struct c` inside a `struct a`, `addr` is a pointer to the *middle* of a `sizeof(struct c)`-sized array, it's not OOB. Consider `char y[100] = ...; char *z = &y[20];`, then `z[-15]` is not OOB (because it refers to `y[5]`). – Drew McGowen Aug 13 '14 at 14:15
  • Your example is different IMO (and defined, just as you said). My concerns are a little harder to describe and I think your answer is OK as-is (besides there are certainly no aliasing-problems here). Are you interested in a discussion on that, maybe in chat? – mafso Aug 13 '14 at 14:52
  • @mafso sure, but I'm busy for the next few hours – Drew McGowen Aug 13 '14 at 15:12
  • @mafso alright, I've got a chat over at http://chat.stackoverflow.com/rooms/59294/pointers-aliasing-and-undefined-behavior – Drew McGowen Aug 13 '14 at 18:38
-1
include <stdio.h>

struct a
{
  struct b
  {
    int b_inner_int;
    char b_inner_char;
  }x;

  struct c
  {
    int c_inner_int;
    char c_inner_char;
  }y;

}z;

void copy_val(struct c *c_pointer)
{
    struct b *tmp = (struct b *) (c_pointer-1);

    tmp->b_inner_int = c_pointer->c_inner_int;
    tmp->b_inner_char = c_pointer->c_inner_char;
}

int main(int argc, char **argv)
{

    struct a test;

    test.y.c_inner_int = 32; 
    test.y.c_inner_char = 'A';

    copy_val(&test.y);

    printf("b inner int : %d\n", test.x.b_inner_int);
    printf("b inner char %c\n", test.x.b_inner_char);


    return 0;
}
DainDwarf
  • 1,651
  • 10
  • 19
  • 2
    This is effectively what OP described as trying, and it didn't work. The use of `c_pointer - 1` is not valid. – Drew McGowen Aug 13 '14 at 14:11
  • OP code didn't work because his tmp pointer was of type struct c *. I do agree that using offsetof is a much better answer, but I tested my code on my machine, it does work. I wouldn't use it, though. – DainDwarf Aug 13 '14 at 14:15
  • Just because it works on your computer doesn't mean it's not necessarily correct. After all, undefined behavior includes working as intended. – Drew McGowen Aug 13 '14 at 14:15