我正在试验C11和VLA,试图在堆栈中仅使用不完整的声明来声明结构变量。目的是提供一种机制来创建某种结构类型的变量,而无需显示内部信息(例如PIMPL惯用语),而无需在堆上创建变量并返回指向它的指针。另外,如果结构布局发生变化,我也不想重新编译使用该结构的每个文件。

我设法编程以下内容:

private.h:

#ifndef PRIVATE_H_
#define PRIVATE_H_

typedef struct A{
    int value;
}A;

#endif /* PRIVATE_H_ */

public.h:
#ifndef PUBLIC_H_
#define PUBLIC_H_

typedef struct A A;

size_t A_getSizeOf(void);

void A_setValue(A * a, int value);

void A_printValue(A * a);

#endif /* PUBLIC_H_ */

实现.c:
#include "private.h"
#include "stdio.h"

size_t A_getSizeOf(void)
{
    return sizeof(A);
}

void A_setValue(A * a, int value)
{
    a->value = value;
}

void A_printValue(A * a)
{
    printf("%d\n", a->value);
}

main.c:
#include <stdalign.h>
#include <stddef.h>

#include "public.h"

#define createOnStack(type, variable) \
    alignas(max_align_t) char variable ## _stack[type ## _getSizeOf()]; \
    type * variable = (type *)&variable ## _stack

int main(int argc, char *argv[]) {
    createOnStack(A, var);

    A_setValue(var, 5335);
    A_printValue(var);
}

我已经测试了此代码,它似乎可以工作。但是,我不确定我是否忽略了可能是危险的,不可移植的或可能损害性能的某些事情(例如混叠,对齐或类似的东西)。我也想知道在C语言中是否有更好的(便携式)解决方案。

最佳答案

这当然违反了有效的输入规则(也就是严格的别名),因为C语言不允许通过不具有该类型(或兼容类型)的指针访问tyt char []的对象。

您可以通过诸如-fno-strict-aliasing之类的编译器标志或诸如以下之类的属性来禁用严格的别名分析

#ifdef __GNUC__
#define MAY_ALIAS __attribute__((__may_alias__))
#else
#define MAY_ALIAS
#endif

(感谢R ..指出后者),但实际上,只要您仅使用变量的专有名称来初始化类型化的指针,一切都应该可以正常工作。

就个人而言,我会将您的声明简化为以下内容:
#define stackbuffer(NAME, SIZE) \
    _Alignas (max_align_t) char NAME[SIZE]

typedef struct Foo Foo;
extern const size_t SIZEOF_FOO;

stackbuffer(buffer, SIZEOF_FOO);
Foo *foo = (void *)buffer;

另一种选择是使用非标准的alloca(),但是该“功能”带有其自身的一系列问题。

08-28 10:21