代码优先:
struct yo
{
char *name;
int several[10];
int num;
};
int main(void)
{
struct yo *test_yo = malloc(sizeof(struct yo));
printf("%p\t%p\t%p\n", (void *)&test_yo->name, (void *)&test_yo->num, (void *)&test_yo->several);
return 0;
}
我的问题是:如果malloc只分配一些内存空间并返回指向它的指针,test_yo如何找到其名称的地址,num和几个 - 因为test_yo只是指向一堆垃圾的指针? malloc实际上是在做一些额外的工作,比如对齐struct成员并记录成员的内存地址吗?
答案 0 :(得分:1)
编译器可以解决这个问题。即sizeof
计算出所需的空间,编译器将如何安排该存储器中的各种项目
答案 1 :(得分:1)
malloc
函数本身并不对齐结构的成员,这是编译器在程序运行之前很久就完成的。例如,在具有8字节指针和4字节整数的系统上,可能是:
name
将位于结构的开头; several
将偏移8;和num
将位于偏移48处(允许several
具有10个四字节元素。)所有malloc
所做的就是给你一些至少足够大的内存来放置那个结构(或者如果找不到足够的内存就给你NULL)。
在结构中字段的定位是由访问所述结构的代码完成的。
答案 2 :(得分:0)
malloc仅分配内存。 C编译器在编译时知道结构'yo'包含三个字段,包括一个名为'name'的字段,它在结构偏移0处等等。
编译器因此获取malloc返回的指针,添加已知的结构字段偏移量,最后得到您描述的直接指向成员的地址。
答案 3 :(得分:0)
这与malloc无关。发生的事情非常简单 - 通过指针X引用结构成员只是将一个偏移量添加到存储在指针X中的存储器地址(其值)。因此,您只需将偏移量应用于malloc返回的指针。
请记住,编译后的二进制文件中不存在结构 - 它只是高级编程语言的编译时“概念”。您甚至可以使用offsetof
宏来获取结构中每个成员的偏移量。
因此,在获取指针及其值的位置并不重要,您可以通过应用添加其偏移量(编译器始终知道)来计算结构“字段”的地址。存储在指针中的值。
这是一个证明这一点的小例子:
#include <stddef.h>
#include <stdio.h>
struct my_struct {
int x;
int y;
};
int main()
{
struct my_struct *s;
/* Print relative offsets of the structure member fields: */
printf("Offset of 'x': %lu\n", offsetof(struct my_struct, x));
printf("Offset of 'y': %lu\n", offsetof(struct my_struct, y));
/*
* Assign some random memory address to a pointer and print addresses
* of the structure and its members. You can see that they are related,
* pointer to 'x' will always be the same as pointer to structure +
* `offsetof(struct my_struct, x)` and pointer to 'y' as pointer to
* structure + `offsetof(struct my_struct, y)`
*/
s = (void *)0xDEADBEEF;
printf("Pointer to structure: %p\n", s);
printf("Pointer to x: %p (x - base = %ld)\n",
&s->x, (ptrdiff_t)&s->x - (ptrdiff_t)s);
printf("Pointer to y: %p (y - base = %ld)\n",
&s->y, (ptrdiff_t)&s->y - (ptrdiff_t)s);
return 0;
}
不同平台上的结果可能会有所不同,这是我在Intel x86_64上的结果:
$ clang -Weverything -o test ./test.c && ./test
Offset of 'x': 0
Offset of 'y': 4
Pointer to structure: 0xdeadbeef
Pointer to x: 0xdeadbeef (x - base = 0)
Pointer to y: 0xdeadbef3 (y - base = 4)
请注意,您始终可以在运行时计算和字段偏移。与offsetof
不同的是,它可用于计算compile-time中的偏移量。
另外,在尝试自己计算场偏移时要小心。虽然编译器无法重新排列字段,但它可以轻松执行padding and alignment,如果需要,可以在字段成员之间有效地添加隐式“空”空格。
希望它有所帮助。祝你好运!
答案 4 :(得分:0)
malloc
只分配一定量的内存。它返回一个通用指针(即void*
)。
编译器会看到结构,并知道它的布局。
如果你有不同的类型,你会得到不同的值(注意下面的代码使用与堆分配的内存完全相同的指针)。
struct foo { int i, j, k };
struct bar { char i, j, k; };
void *ptr = malloc(100);
struct foo * fooptr = (struct foo *)ptr;
struct bar * barptr = (struct bar *)ptr;
printf("foo: %p, %p, %p\n", &fooptr->i, &fooptr->j, &fooptr->k);
printf("bar: %p, %p, %p\n", &barptr->i, &barptr->j, &barptr->k);
答案 5 :(得分:0)
C中的结构是一个非常......结构化的内存块。假设主机系统具有32位指针和32位整数,则name
(指针)将始终位于结构的偏移0处; several
的FIRST元素将始终位于结构的偏移量4处(偏移量0 + {指针大小});并且num
将始终位于结构的偏移44处(偏移4 + 10 * {sizeof an int})。从平台到平台和编译器到编译器的偏移量可能有所不同,具体取决于数据类型大小,内存对齐等,但对于给定的平台 - 编译器组合,总是相同。对于我上面给出的示例,类型struct yo
的每个项目将精确地为48个字节长,例如,num
始终位于结构中的偏移44处。