如何解决使用 alignof 打包两个对象
使用第二个对象的对齐方式打包两个对象得到最终大小是否符合标准?
我将这种方法用于 doubly linked list,但提取了相关部分:
#include <stdio.h>
#include <stdlib.h>
#include <stdalign.h>
struct node
{
struct node *prev;
struct node *next;
};
#define get_node(data,szof) ((struct node *)(((char *)data) + szof))
int main(void)
{
size_t align = alignof(struct node);
double *data;
// Round size up to nearest multiple of alignof(struct node)
size_t szof = (sizeof(*data) + (align - 1)) / align * align;
// Pack `data` + `struct node`
data = malloc(szof + sizeof(struct node));
// Get node using a generic pointer to calculate the offset
struct node *node = get_node(data,szof);
*data = 3.14;
node->prev = NULL;
node->next = NULL;
printf("%f\n",*data);
free(data);
return 0;
}
其中 data
可以是指向任何原始类型或复合类型的指针。
解决方法
使用第二个对象的对齐方式打包两个对象得到最终大小是否符合标准?
当然,提供的代码是有效的。
这里没有什么可写的,因为它更难证明,而不是反驳某些东西。指针值针对引用类型正确对齐,没有未初始化的内存访问。如果您自己记得对齐,那么您可以编写整个程序而无需使用 struct
。
在实际代码中,我建议创建一个结构并让编译器计算出来[1]。我们有offsetof
。
struct double_and_node {
double data;
struct node node;
};
void *pnt = malloc(sizeof(double_and_node));
double *data = (struct node*)((char*)pnt + offsetof(struct double_and_node,data));
struct node *node = (struct node*)((char*)pnt + offsetof(struct double_and_node,data));
[1] 但实际上,如果是这样,无论如何都只需使用该结构......:
struct double_and_node *pnt = malloc(sizeof(double_and_node));
double *data = &pnt->data;
struct node *node = &pnt->node;
,
作为对先前答案的扩展想法,可以使用 typeof() 和 offsetof() 定义/使用动态定义的结构使用宏来完成一些通用的操作将数据类型与节点结构连接起来:
#include <stdio.h>
#include <stddef.h>
struct node
{
struct node *prev;
struct node *next;
};
#define LINKED_TYPE_SIZE(data) \
sizeof(struct { typeof(data) f; struct node node; })
#define LINKED_TYPE_NODE(datap) \
(struct node *)((char *)(datap) + offsetof(struct { typeof(*(datap)) f; struct node node; },node))
int main(void)
{
double v1;
printf("size of linked double = %zu\n",LINKED_TYPE_SIZE(v1));
printf("%p,%p\n",&v1,LINKED_TYPE_NODE(&v1));
int v2;
printf("size of linked int = %zu\n",LINKED_TYPE_SIZE(v2));
printf("%p,&v2,LINKED_TYPE_NODE(&v2));
short int v3;
printf("size of linked short int = %zu\n",LINKED_TYPE_SIZE(v3));
printf("%p,&v3,LINKED_TYPE_NODE(&v3));
struct foo {
int f1;
char f2;
int f3;
} foo_struct;
printf("size of linked foo = %zu\n",LINKED_TYPE_SIZE(foo_struct));
printf("%p,&foo_struct,LINKED_TYPE_NODE(&foo_struct));
return 0;
}
上述在 x86_64 Linux 桌面上的执行结果如下:
$ gcc try.c -o try
$ ./try
size of linked double = 24
0x7ffdfbdf50f8,0x7ffdfbdf5100
size of linked int = 24
0x7ffdfbdf50f4,0x7ffdfbdf50fc
size of linked short int = 24
0x7ffdfbdf50f2,0x7ffdfbdf50fa
size of linked foo = 32
0x7ffdfbdf5100,0x7ffdfbdf5110
注意: 由于 typeof() 是一个非标准函数,也可以通过将数据类型作为参数显式传递给宏来摆脱它:
#define LINKED_TYPE_SIZE(type) \
sizeof(struct { type f; struct node node; })
#define LINKED_TYPE_NODE(type,datap) \
(struct node *)((char *)(datap) + offsetof(struct { type f; struct node node; },node))
,
嗯,这很复杂。 ((char *)data) + szof
行可以说是根据 alignof(struct node)
与 sizeof(double)
调用未定义的行为,但这不是很明显。
首先,让我们假设 double* data
实际上指向一个 double
。然后我们将被允许通过字符类型指针检查这个对象,根据 6.3.2.3/7:
当指向对象的指针转换为指向字符类型的指针时, 结果指向对象的最低寻址字节。的连续增量 结果,直到对象的大小,产生指向对象剩余字节的指针。
所以我们可以在坚持实际 ((char *)data) + szof
的同时执行 double
。否则,如果我们超出该 double
的边界,则上述引用的特殊规则不适用。
相反,我们应该遵循由加法运算符指定的指针算术规则。尽管这些规则希望您使用指向类型 double*
而不是 char*
。这些规则并没有真正指定当您通过 double
检查 char*
并超出 sizeof(double)
字节时会发生什么。
因此,超出 ((char *)data) + szof
的 sizeof(double)
是有问题的 - 我认为无论您怎么说,它都是未定义的行为。
那么这里还有另一个方面……如果 char 指针指向没有类型的东西怎么办? C 标准没有指定那时会发生什么。这实际上就是代码所做的。
因为碰巧,data = malloc(szof + sizeof(struct node));
分配了一个没有声明也没有“有效类型”的原始段。 6.5 规则然后指出
如果一个值被存储到一个没有声明类型的对象中 左值的类型不是字符类型,则左值的类型变为 该访问和不修改的后续访问的对象的有效类型 存储值
并且在 *data = 3.14;
之前您不会左值访问实际内存,在这种情况下,内存获得有效类型 double
。这发生在指针算法之后。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。