简体   繁体   中英

can I use struct member from another struct?

From these question: Casting one struct pointer to another - C , I would like to know, if it is possible to use a member of a "general" struct typed to a "specific" struct:

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

enum type_e { CONS, ATOM, FUNC, LAMBDA };

typedef struct {
    enum type_e type;
} object;

typedef struct {
    enum type_e type;
    char *expression;
} lambda_object;

typedef struct {
    enum type_e type;
    object *car, *bus;
    int value;
} cons_object;


object *traverse(object *o){
    if (o->type == CONS){
        cons_object *cons = (cons_object*)o;
        traverse(cons->car);
        traverse(cons->bus);
        return (object*)cons;
    } else if (o->type == LAMBDA) {
        lambda_object *lam = (lambda_object*)o;
        return (object*)lam;    
    }
    return 0;
}

int main(){
    lambda_object l = {LAMBDA, "value to print\n"};
    object *p = traverse((object*)&l);
    printf("sizeof(object):%lu\nsizeof(lambda_object):%lu\n",sizeof(object), sizeof(lambda_object));
    printf("%s\n",*(p+4));

}

Which emits no error, just command terminated so I have no idea what gone wrong, but suspect I tried to deference wrong address *(p+4) , but I know, there is a pointer to my string. From definition of lambda_object , after enum (which is 4 bytes long, just as int ), there is my pointer. So I should not be dereferencing wrong address, but still I cannot. Why?

output :

a.c: In function ‘main’:
a.c:46:11: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘object’ {aka ‘struct <anonymous>’} [-Wformat=]
  printf("%s\n",*(p+4));
          ~^    ~~~~~~

Press ENTER or type command to continue
sizeof(object):4
sizeof(lambda_object):16

Command terminated

EDIT: I have tried (char*)p[4] , still termination

First of all, like many others pointed out in the comments, this is not the ideal way to do whatever it is that you are trying to achieve. The easiest and most portable way would be to use something like ((lambda_object*)p)->expression .

As for why your code behaves as it does, perhaps I can provide an explanation.

Before that, here is your program, 'fixed' to print the stored string exactly the way you wanted it to.

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

enum type_e { CONS, ATOM, FUNC, LAMBDA };

typedef struct {
    enum type_e type;
} object;

typedef struct {
    enum type_e type;
    char *expression;
} lambda_object;

typedef struct {
    enum type_e type;
    object *car, *bus;
    int value;
} cons_object;


object *traverse(object *o){
    if (o->type == CONS){
        cons_object *cons = (cons_object*)o;
        traverse(cons->car);
        traverse(cons->bus);
        return (object*)cons;
    } else if (o->type == LAMBDA) {
        lambda_object *lam = (lambda_object*)o;
        return (object*)lam;    
    }
    return 0;
}

int main(){
    lambda_object l = {LAMBDA, "value to print\n"};
    object *p = traverse((object*)&l);
    printf("sizeof(object):%lu\nsizeof(lambda_object):%lu\n",sizeof(object), sizeof(lambda_object));

    printf("%s\n",*((char**)((char*)p+8))); // Note the weird typecasts and p + 8 instead of 4
}

Coming to the reason for this, assuming a 64-bit machine, your lambda_object struct would look like this in memory:

| Bytes 0 to 3 | Bytes 4 to 7 | Bytes 8 to 16                |
--------------------------------------------------------------
| type         | padding      | expression                   |
--------------------------------------------------------------

What you should note here is that expression is a pointer to the string and not the string itself. So even though type is only 4-bytes long, expression starts only at p + 8 and not p + 4 as one might expect. The bytes from 4 to 7 will simply be left empty as padding. This is because a 64-bit pointer has to start at a 64-bit aligned address.

But then ((char *)p + 8) should work right? Unfortunately not! We started with p as a pointer to a lambda_object . We have typecasted p to a char pointer to reach the right offset within this struct but this means that you are telling the compiler that there is a character at the location p + 8 , when in fact what is there is a pointer to a character. If you pass this to printf() it tries to print this pointer as a string, resulting in gibberish.

What you should do now is de-reference the pointer p + 8 to fetch the pointer expression by telling the compiler to treat p + 8 as a pointer to a pointer. This is achieved using a typecast to (char**) . Now you can de-reference this once to get a char pointer and finally pass this on to printf() .

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