я могу использовать член структуры из другой структуры? - PullRequest
0 голосов
/ 05 августа 2020

Из этих вопросов: Приведение одного указателя структуры к другому - C, я хотел бы знать, можно ли использовать член «общей» структуры с типом «специфицированный *» 1021 * "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));

}

Что не выдает ошибок, просто command terminated, поэтому я понятия не имею, что пошло не так, но подозреваю, что пытался указать неправильный адрес *(p+4), но я знаю, что есть указатель на мою строку. Из определения lambda_object после enum (длиной 4 байта, как и int) есть мой указатель. Поэтому я не должен разыменовать неправильный адрес, но все же я не могу. Почему?

вывод :

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

РЕДАКТИРОВАТЬ: Я пробовал (char*)p[4], но все еще завершается

1 Ответ

0 голосов
/ 05 августа 2020

Прежде всего, как многие другие указали в комментариях, это не идеальный способ делать то, чего вы пытаетесь достичь. Самый простой и переносимый способ - использовать что-то вроде ((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().

...