Прежде всего, как многие другие указали в комментариях, это не идеальный способ делать то, чего вы пытаетесь достичь. Самый простой и переносимый способ - использовать что-то вроде ((lambda_object*)p)->expression
.
Что касается того, почему ваш код ведет себя именно так, возможно, я могу дать объяснение.
Перед этим вот ваша программа 'исправлена', чтобы распечатать сохраненную строку именно так, как вы хотели.
#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
}
Исходя из причины этого, предполагая 64-битную машину, ваша структура lambda_object
будет выглядеть так это в памяти:
| Bytes 0 to 3 | Bytes 4 to 7 | Bytes 8 to 16 |
--------------------------------------------------------------
| type | padding | expression |
--------------------------------------------------------------
Здесь следует отметить, что expression
- это указатель на строку, а не на саму строку. Таким образом, хотя type
имеет длину всего 4 байта, expression
начинается только с p + 8
, а не p + 4
, как можно было бы ожидать. Байты с 4 по 7 будут просто оставлены пустыми в качестве заполнения. Это потому, что 64-битный указатель должен начинаться с 64-битного выровненного адреса.
Но тогда ((char *)p + 8)
должен работать правильно? К сожалению нет! Мы начали с p
как указателя на lambda_object
. Мы присвоили тип p
указателю char, чтобы достичь правильного смещения в этой структуре, но это означает, что вы сообщаете компилятору, что в местоположении p + 8
есть символ, тогда как на самом деле это указатель на символ. Если вы передадите это в printf()
, он попытается распечатать этот указатель как строку, в результате получится gibberi sh.
Теперь вам нужно отменить ссылку на указатель p + 8
, чтобы получить указатель expression
, указав компилятору рассматривать p + 8
как указатель на указатель. Это достигается с помощью преобразования типа в (char**)
. Теперь вы можете разорвать ссылку на него один раз, чтобы получить указатель на символ и, наконец, передать его на printf()
.