是否可以在堆栈上分配结构,并将其定义隐藏在源文件中?

问题描述 投票:1回答:3

我有以下头文件:

struct StackList_s;
typedef struct StackList_s StackList_t;
// From here I add in the method signatures

以下源文件:

struct StackList_s
{
    integer_t count;
    struct StackListNode_s *top; // Here begins the linked list
    // Some other members that store information about the stack
    integer_t version_id;
};
// From here I define StackListNode_s and implement the StackList_s functions
// Note that the user will never manipulate directly a StackListNode_s
// There are functions that will handle the free() of each node correctly

我将结构定义隐藏在源文件中,以便使用此堆栈的任何人都不能直接修改其成员,因为更改它们需要一些输入处理或检查某些无效状态。

目前,要获得新堆栈,您必须使用以下内容:

// malloc(sizeof(StackList_t)) and set members to default
StackList_t *stack = stl_new(/* Some info parameters */);

但我只能在堆中分配StackList_t。我想要做的是在堆栈上分配StackList_t然后它的节点可以在它们的数据和指向其他节点的堆中分配。这样我可以给用户一个选择,如果在本地使用结构,或者他将作为分配的资源绕过函数传递它。

StackList_t stack;
stl_init(&stack, /* Info parameters */); // No malloc, only setting members to 0

但当然我不能这样做,因为struct StackList_s的定义在源文件中。所以这是我的问题:

  • 是否有可能同时不允许访问结构的成员并在堆栈中分配相同的结构?
  • 有没有办法告诉编译器我的结构的大小?
c memory-management heap-memory stack-memory
3个回答
2
投票

您可以在Linux中使用VLA或alloca执行此操作:

图书馆标题:

struct StackList_s;
typedef struct StackList_s StackList_t;
extern const size_t StackList_size;

// If you're using VLAs
extern const size_t StackList_align;
StackList_t* stl_init_inline(char stack_source[], ...);

图书馆来源:

#include "header.h"

struct StackList_s {
    // ...
};

const size_t StackList_size = sizeof(StackList_t);

// If you're using VLAs
#include <stdalign.h>
#include <stdint.h>

const size_t StackList_align = alignof(StackList_t);
StackList_t* stl_init_inline(char stack_source[], ...) {
    // align the address to the nearest multiple of StackList_align
    uintptr_t address = (uintptr_t) ((void*) stack_source);
    if (address % StackList_align != 0) {
        address += StackList_align - address % StackList_align;
    }
    StackList_t* stack = (StackList_t*) ((void*) address);
    stl_init(stack, ...);
    return stack;
}

来源手

#include <header.h>

StackList_t* stack = alloca(Stacklist_size);
stl_init(stack, ...);

char stack_source[StackList_size + StackList_align - 1];  // Not compile time.
StackList_t* stack = stl_init_inline(stack_source, ...);

这会将它分配到堆栈上,你不需要释放它,但它比StackList_t stack_source;更慢,更冗长。 (并且alloca仅限Linux)

对于第二个问题,您需要一个结构的完整定义来获得它的大小。常见的陷阱包括sizeof(struct { int a; }) == sizeof(struct { int a; })可能是假的。它可能不会,所以你可以做#define StackList_size sizeof(struct { integer_t count; struct StackListNode_s *top; integer_t version_id; }),但这也导致了很多代码重复。

我个人只是将结构定义放在头文件中,只是在某个地方的注释中声明“不要弄乱成员或我的方法不起作用”(也许让名字以_开头给出一个暗示他们是私人的)


1
投票

你可以通过使用#define而不使用VLA来做类似于Artyer的回答

标题:

#define STACKLISTSIZE 32
typedef uint8_t stl_storage[STACKLISTSIZE];
typedef struct stacklist_s stacklist_t;

stacklist_t* stl_create_from_stack(stl_storage b);  //user provides memory
stacklist_t* stl_allocate(void);  //library allocates memory, user must free.

资源:

int myfunction()
{
  stl_storage x;
  stacklist_t* sp = stl_create_from_stack(x);
  //do something with sp.
}

确保在实现文件中有一个编译时声明sizeof(stack_s) == STACKSTRUCTSIZE


1
投票

某些实现保证编译单元之间的调用将以与平台的应用程序二进制接口(ABI)一致的方式进行处理,而不考虑被调用函数将对其接收的地址或调用者将执行的操作执行的操作。存储其提供的地址,或者一旦函数返回就会执行此类存储。在这样的实现上,给出如下内容:

// In header
typedef union FOO_PUBLIC_UNION { 
  uint64_t dat[4]; // Allocate space
  double dummy_align1; // Force alignment
  void *dummy_align2; // Force alignment
} FOO;
void act_on_foo(FOO_PUBLIC_UNION*);

// In code
FOO x = {0};
act_on_foo(&x);

在一个编译单元中,类似于:

struct FOO_PRIVATE {
  int this; float that; double whatever;
};

typedef union FOO_PUBLIC_UNION { uint64_t dat[4]; struct FOO_PRIVATE priv; } FOOPP;

void act_on_foo(FOO *p)
{
  FOOPP *pp = (FOOPP*)p;
  pp->priv.whatever = 1234.567;
}

如果FOOFOOPP的大小匹配,则从第一个编译单元调用外部函数的行为将被定义为分配sizeof(FOO)字节,将它们归零,并将它们的地址传递给act_on_foo,然后将其行为定义为对其执行操作。它接收地址的字节数,不考虑它们如何得到它们的值或者调用者稍后会用它们做什么。

不幸的是,即使几乎每个实现都应该能够产生与调用它一无所知的函数一致的行为,但是没有标准的方法向编译器指示特定的函数调用应该被视为“不透明”。旨在用于有用目的的实现可以并且通常使用“普通”函数调用来支持这样的语义,无论标准是否需要,并且这样的语义对于仅用于它们不会有用的目的的实现几乎没有价值。 。不幸的是,这导致了一个Catch 22:没有理由要求标准强制要求实现在有或没有授权的情况下可以自由执行,如果它们有用,但是一些编译器编写者认为标准缺乏任务是鼓励拒绝支持。

© www.soinside.com 2019 - 2024. All rights reserved.