我可以使用其他结构的结构成员吗?

时间:2020-08-05 12:38:43

标签: c struct casting type-conversion

从这些问题:Casting one struct pointer to another - C,我想知道,是否有可能使用类型为“特定”结构的“通用”结构的成员:

#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),但是我知道有指向我字符串的指针。根据{{​​1}}的定义,在lambda_object之后(长4个字节,与enum一样),有我的指针。因此,我不应该取消引用错误的地址,但仍然不能。为什么?

输出

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 ,仍然终止了

1 个答案:

答案 0 :(得分:0)

首先,就像在评论中指出的其他许多人一样,这不是完成您要实现的目标的理想方法。最简单,最可移植的方法是使用类似((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(),它将尝试将此指针打印为字符串,从而导致乱码。

您现在应该做的是通过告诉编译器将p + 8视为指向指针的指针,取消对指针expression的引用以获取指针p + 8。这可以通过对(char**)进行类型转换来实现。现在,您可以取消引用一次以获得char指针,最后将其传递给printf()

相关问题