不使用malloc,能否返回指向结构体的指针?

8
我是一名使用GBDK编写Gameboy ROM的开发者。GBDK自带一个不稳定版本的malloc函数,我无法使其正常工作。同时,我也无法在结构体中嵌套另一个结构体。这使我只能尝试返回一个指针,因此我想知道是否有一种方法可以避免在返回结构体指针时使用malloc函数?
基本上,我的目标是要能够像这样编写代码:
create_struct(struct_name, char member_x, char member_y);

这是我使用malloc编写的代码:
 struct point {
    char member_x;
    char member_y;
};


struct point *makepoint(char member_x, char member_y) {
    struct point *temp = malloc(sizeof(struct point));

    temp->member_x = member_x;
    temp->member_y = member_y;

    return temp;
};

你的问题是关于如何创建一个对象,而不是如何返回一个指针。 - user253751
7个回答

7
有多种有效的方法来返回指向结构体或任何类型对象的指针,但唯一可以返回指向在函数调用之前不存在的新对象的指针的方式是使用malloc、realloc、calloc、aligned_alloc(C11)或某些实现定义的分配函数(例如POSIX系统上的mmap等)。
其他可返回有效指针的方式包括:
1. 静态存储期对象的指针。这种对象只有一个实例,因此通常不是很好。
2. 作为参数传递给函数的指针,用于存储结果。这通常是一个很好的方法,因为您将获取存储的责任交给调用者。
3. 从某种全局池中获取的对象指针。对于低端游戏设备的嵌入式系统和游戏设计来说,这可能是一个非常好的方法。

我尝试过一些其他的答案,但似乎没有一个适用于GBDK。我对您关于全局池的第三个建议很感兴趣。您有什么资源可以给我指引吗? - MariusGames
如果你已经尝试过它们,但它们"无法工作",我不认为这意味着你应该尝试其他东西。这可能意味着你应该多读一些C语言,并学习你做错了什么(或者提出一些具体的问题)。每当你的代码不能达到预期效果时就尝试完全新的方法并不是编程的高效和可靠方式。 - R.. GitHub STOP HELPING ICE
谢谢,我知道。欢迎阅读建议 :) 我还在学习,但是由于GBDK使用的是旧版本的C语言,所以无法返回结构体。此外,malloc存在问题也是众所周知的。我会再次查看所有答案。 - MariusGames

3

你能否在不使用malloc的情况下返回一个指向结构体的指针呢?

I. 从技术上讲,是可以的。 你可以将你的结构体声明为static,这样它就可以跨越函数调用而存在:

struct foo *bar()
{
    static struct foo f = { 1, 2, 3 };
    return &f;
}

但我怀疑您实际上并不想这样做(因为这会产生有趣的副作用,请阅读关于static关键字含义的说明)。您有几种不同的可能性:

II. C标准库采取的方法是始终使调用者隐式负责提供结构体并管理内存。因此,函数不是返回指针,而是接受指向结构体的指针并将其填充:

void dostuff(struct foo *f)
{
    foo->quirk = 42;
}

三、或者直接返回结构体本身,这不会有什么问题(甚至可以进行移动优化):

struct foo bar()
{
    struct foo f = { 1, 2, 3 };
    return f;
}

因此,选择您的方式。

0
最简单的方法就是使用命名初始化器创建一个结构体,并在内联函数中返回它,这样就可以避免任何开销:
static inline struct point makepoint(char x, char y) {
  return (struct point) { .x = x, .y = y };
}

然后你可以这样调用它:

struct point foo = makepoint(10, 20);

再简单不过了!


0

只需像这样做:

void makepoint(struct point *dest, char member_x, char member_y) {
     dest->member_x = member_x; // you had these wrong in your code, by the way
     dest->member_y = member_y;
}

该结构体需要在其他地方进行“分配”(最好是在堆栈上进行)。


0
你可以将结构体作为参数传递,并让函数对其进行初始化:
struct point *makepoint(struct point *pt, char x, char y) {
    pt->x = x;
    pt->y = y;
    return pt;
}

然后像这样调用它:

struct point pt;
makepoint(&pt, 'a', 'b');

但是你也可以直接这样做:

struct point pt = { 'a', 'b' };

0
请注意,在这种情况下(struct point仅占用2个字节),您可以返回struct point而不是struct point *(对于大型结构体不应这样做)。
#include <stdio.h>

struct point {
    char member_x;
    char member_y;
};

struct point makepoint(char member_x, char member_y)
{
    struct point temp;

    temp.member_x = member_x;
    temp.member_y = member_y;
    return temp;
}

int main(void)
{
    struct point t = makepoint('a', 'b');

    printf("%c %c\n", t.member_x, t.member_y);
    return 0;
}

0
如果无法修复malloc(),那么您可能需要管理自己预分配的点,并限制可以“创建”的点的数量。 您需要稍微修改点以便更轻松地进行管理:
union free_point {
    union free_point *next;
    struct point data;
};

union free_point free_point_pool[MAX_POINTS];
union free_point *free_point_list;

struct point *makepoint(char member_x, char member_y) {
    static int i;
    union free_point *temp;

    temp = 0;
    if (i == MAX_POINTS) {
        if (free_point_list) {
            temp = free_point_list;
            free_point_list = temp->next;
        }
    } else {
        temp = free_point_pool + i++;
    }

    if (temp) {
        temp->data.x = x;
        temp->data.y = y;
    }

    return &temp->data;
};

然后,不要在调用makepoint()返回的结果上调用free(),而是应该创建一个新函数将其放置在free_point_list上。

void unmakepoint (struct point *p) {
    union free_point *fp = (union free_point *)p;
    if (fp) {
        fp->next = free_point_list;
        free_point_list = fp;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接