C 从空指针访问结构内容
C Access structure contents from a void pointer
我有一项任务让我失去了宝贵的时间而没有成功。我必须从作为 void *c 传递给函数的结构中读取内容。除了指向另一个结构的字段外,我可以毫无问题地读取它的内容。示例代码:
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct Car Car;
struct Car {
int year;
int type;
int free;
struct {
char *name;
char *city;
} *facility;
};
int main()
{
Car *c = malloc(sizeof(Car)*2);
c->year = 2020;
c->type = 2;
c->free = 1;
c->facility = malloc(200);
c->facility->name = malloc(10);
c->facility->city = malloc(10);
snprintf(c->facility->name, 5, "Test");
snprintf(c->facility->city, 5, "Test");
test(c);
}
int test(void *c)
{
int year = *(void **) c;
int type = *(void **) (c+4);
int free = *(void **) (c+4+4);
printf("Year %d\n",year);
printf("Type %d\n",type);
printf("Free %d\n",free);
void *facility;
facility = *(void **) (c+4+4+4);
printf("Facility name %s", *(void **) facility);
printf("Facility city %s", *(void **) facility+8);
}
我看不懂的部分是设施名称和设施城市。我知道我可以使用 -> 轻松访问,但作业要求准确理解结构在内存中的定义方式,并使用 void* 直接提取其内容。谢谢
the assignment asks precisely understand how structure is defined in memory
内存布局(假设没有填充)如下所示。
-------------------------|
| ^ |
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
的 return 类型更改为 void
,因为您没有 returning 任何值。您还应该在调用它之前用 void test(void *);
声明它。
如果不允许将指针转换为正确的类型,则可以使用 <stddef.h>
中定义的 offsetof
计算 int
成员的位置。如有需要,您也可以通过其他方式发现偏移量后再进行填写。
但是,要访问 facility
成员,我们 运行 遇到了有关 C 规则的问题,如下面的评论所述。我不相信在严格符合 C 的情况下有完全定义的方法来做到这一点。在那种情况下,这是一个错误的分配。
void test(void *p)
{
char *c = p;
printf("Year %d\n", * (int *) (c + offsetof(Car, year)));
printf("Type %d\n", * (int *) (c + offsetof(Car, type)));
printf("Free %d\n", * (int *) (c + offsetof(Car, 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 *)));
}
我有一项任务让我失去了宝贵的时间而没有成功。我必须从作为 void *c 传递给函数的结构中读取内容。除了指向另一个结构的字段外,我可以毫无问题地读取它的内容。示例代码:
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct Car Car;
struct Car {
int year;
int type;
int free;
struct {
char *name;
char *city;
} *facility;
};
int main()
{
Car *c = malloc(sizeof(Car)*2);
c->year = 2020;
c->type = 2;
c->free = 1;
c->facility = malloc(200);
c->facility->name = malloc(10);
c->facility->city = malloc(10);
snprintf(c->facility->name, 5, "Test");
snprintf(c->facility->city, 5, "Test");
test(c);
}
int test(void *c)
{
int year = *(void **) c;
int type = *(void **) (c+4);
int free = *(void **) (c+4+4);
printf("Year %d\n",year);
printf("Type %d\n",type);
printf("Free %d\n",free);
void *facility;
facility = *(void **) (c+4+4+4);
printf("Facility name %s", *(void **) facility);
printf("Facility city %s", *(void **) facility+8);
}
我看不懂的部分是设施名称和设施城市。我知道我可以使用 -> 轻松访问,但作业要求准确理解结构在内存中的定义方式,并使用 void* 直接提取其内容。谢谢
the assignment asks precisely understand how structure is defined in memory
内存布局(假设没有填充)如下所示。
-------------------------|
| ^ |
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
的 return 类型更改为 void
,因为您没有 returning 任何值。您还应该在调用它之前用 void test(void *);
声明它。
如果不允许将指针转换为正确的类型,则可以使用 <stddef.h>
中定义的 offsetof
计算 int
成员的位置。如有需要,您也可以通过其他方式发现偏移量后再进行填写。
但是,要访问 facility
成员,我们 运行 遇到了有关 C 规则的问题,如下面的评论所述。我不相信在严格符合 C 的情况下有完全定义的方法来做到这一点。在那种情况下,这是一个错误的分配。
void test(void *p)
{
char *c = p;
printf("Year %d\n", * (int *) (c + offsetof(Car, year)));
printf("Type %d\n", * (int *) (c + offsetof(Car, type)));
printf("Free %d\n", * (int *) (c + offsetof(Car, 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 *)));
}