C从空指针访问结构内容

问题描述

我的一项任务使我失去宝贵的时间而没有成功。我必须从作为void * c传递给函数的结构中读取内容。我可以毫无问题地读取其内容,但该字段是指向另一个结构的指针。示例代码

import SwiftUI

struct TestCountdown: View {
    @State var first = 5
    @State var second = 5
    @State var totalDuration = 30
    var body: some View {
        vstack {
             Text("\(totalDuration)")
            if (first > 0) && (second > 0) {
                HStack {
                    TestCode(number: $first,title: "First")
                    Spacer()
                    TestCode(number: $second,title: "Second")
                }
            }
        }
        .onAppear {
            Timer.scheduledTimer(withTimeInterval: 1.0,repeats: true) { _ in
                if self.totalDuration > 0 {
                    self.totalDuration -= 1
                    
                    if self.first > 0 {
                        self.first -= 1
                    } else {
                        self.second -= 1
                    }
                }
            }
        }
    }
}

struct TestCode: View {
    @Binding var number: Int
    @State var title: String
    var body: some View {
        vstack {
            Text(title)
                .font(.system(size: 20,weight: .medium,design: .rounded))
            Text("\(number)")
                .font(.system(size: 30,design: .rounded))
        }
        .frame(minWidth: 50,maxWidth: .infinity)
    }
}

我无法阅读的部分是设施名称和设施城市。我知道我可以使用->轻松访问,但是作业要求精确地了解如何在内存中定义结构,并使用void *直接提取内容。谢谢。

解决方法

作业要求精确地了解如何在内存中定义结构

内存布局(假设没有填充)如下所示。

                -------------------------|
                |          ^             |
    c  ------>  |  year    | sizeof(int) |
                |          v             |
                |------------------------|
                |  type                  |
                |------------------------|
                |  free                  |
                |------------------------|           |--------|            |---|---|---
                |  facility              |  ------>  |  name  |  ------->  | a | b | ..
                |------------------------|           |--------|            |---|---|---
                                                     |  city  |  ---\      
                                                     |--------|     |      |---|---|---
                                                                    \--->  | x | y | ..
                                                                           |---|---|---

要访问c->facility->city,例如:

    void *facility = *(void **)( (char *)c + 3 * sizeof(int) );  // skip past year,type,free
    void *city = *(void **)((char *)facility + sizeof(char *));  // skip past name

[ EDIT ]。如果没有“ 不填充”的假设,则代码可以使用offsetof宏。

    void *facility = *(void **)( (char *)c + offsetof(struct Car,facility) );
,

在给定指向void *的结构时,访问该结构的常用方法是将其转换为正确的类型:

void test(void *p)
{
    Car *c = p;

    printf("Year %d\n",c->year);
    printf("Type %d\n",c->type);
    printf("Free %d\n",c->free);
    printf("Facility name %s\n",c->facility->name);
    printf("Facility city %s\n",c->facility->city);
}

请注意,由于您没有返回任何值,因此我将test的返回类型更改为void。在调用它之前,还应该使用void test(void *);对其进行声明。

如果不允许将指针转换为正确的类型,则可以使用int中定义的offsetof计算<stddef.h>成员的位置。如有必要,您也可以在通过其他方式发现偏移后填写偏移。

但是,要访问facility成员,我们遇到了有关C规则的问题,如下面的注释中所述。我认为严格遵守C语言并没有完全定义的方法。在这种情况下,这是一个错误的分配。

void test(void *p)
{
    char *c = p;

    printf("Year %d\n",* (int *) (c + offsetof(Car,year)));
    printf("Type %d\n",type)));
    printf("Free %d\n",free)));

    //  Set f to point to the location of facility within the Car structure.
    char *f = c + offsetof(Car,facility);

    /*  Unfortunately,although we know f points to a pointer to the structure
        containing the name and the city,that structure has no tag,so we
        cannot write a cast to it.  Instead,we use "(struct Car **)" to say f
        points to a pointer to a struct Car.  It does not,but the C standard
        requires that all pointers to structures have the same representation
        and alignment requirements.  This is dubious C code,but I see no
        alternative given the problem constraints.

        Then we dereference that pointer to a structure and convert it to a
        pointer to a char,so we can do address arithmetic.  Again,since we
        have no name for the facility structure,we cannot reference its
        members using offsetof.  Normal C implementations will not add padding
        between members of the same type,so we calculate an offset using the
        size of a "char *" and hope that works.
    */
    f = (char *) (* (struct Car **) f);
    printf("Name %s.\n",* (char **) (f + 0));
    printf("City %s.\n",* (char **) (f + sizeof(char *)));
}