Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/71.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C中的SDL1.2:变量在不应该改变的时候改变_C - Fatal编程技术网

C中的SDL1.2:变量在不应该改变的时候改变

C中的SDL1.2:变量在不应该改变的时候改变,c,C,嗯,据我所知不应该是这样。我将首先提供上下文。 我想将控件定义为基本的屏幕小部件: typedef struct { struct Control** children; SDL_Surface* surface; struct Control* parent; char* type; int width; int height; int x; int y; } Control; 主屏幕称为窗口,我为其制作了一个特殊的init函数

嗯,据我所知不应该是这样。我将首先提供上下文。 我想将控件定义为基本的屏幕小部件:

typedef struct {
    struct Control** children;
    SDL_Surface* surface;
    struct Control* parent;
    char* type;
    int width;
    int height;
    int x;
    int y;
} Control;
主屏幕称为窗口,我为其制作了一个特殊的init函数:

Control* createWindow() {
    Control window = {
        .type = "window",
        .surface = SDL_SetVideoMode(WIN_W, WIN_H, 24, SDL_SWSURFACE),
        .height = WIN_H,
        .width = WIN_W,
        .x = 0,
        .y = 0
    };
    return &window;
}
它有一个称为面板的子级,具有自己的初始值设定项:

Control* createPanel(const char* filepath, Control* parent, int x, int y, int numberOfChildren) {
    Control panel = {
        .parent = parent,
        .children = (Control**)malloc(numberOfChildren * sizeof(Control*)),
        .type = "panel",
        .x = x,
        .y = y,
        .surface = SDL_LoadBMP(filepath)
    };
    return &panel;
}
以及主要功能的开始:

int main(int argc, char* args[]) {

    Control* window = NULL;
    Control* panel = NULL;

    SDL_Init(SDL_INIT_EVERYTHING);

    window = createWindow();
    panel = createPanel("BACKGROUND.bmp", window, 0, 0, 3);
现在,当到达createWindow函数时,一切都很好,窗口定义良好。在下一行之后,面板初始化窗口被损坏。我就是不明白为什么

我想这可能是因为我发送了一个窗口,让它被指定为面板的父窗口,所以我尝试不传递它,也不删除该指定。不行,在createPanel返回后,它仍然会弄乱主作用域中窗口的字段

我已经调试这个问题很长时间了,我只是没有线索了。我对C非常陌生,指针异常可能会发生,我不知道,所以我真的希望这是一件非常琐碎的事情


谢谢您的时间。

让我们看看您的CreateWindow函数

Control* createWindow() {
    Control window = {
        .type = "window",
        .surface = SDL_SetVideoMode(WIN_W, WIN_H, 24, SDL_SWSURFACE),
        .height = WIN_H,
        .width = WIN_W,
        .x = 0,
        .y = 0
    };
    return &window;
}
您正在堆栈上创建一个新的控件结构,然后返回地址。由于结构不在堆上,因此可能会修改该位置的数据。您希望将窗口保存在堆上


看看这个答案:

您正在返回一个指向局部变量的指针。您必须记住,非静态局部变量超出范围,并且在返回中定义它们的函数时不再存在

返回并使用此指针将导致,这就是您所看到的

我对如何解决这个问题的建议?不要返回指针,返回一个副本

如果您迫切需要返回一个指针,而实际上不需要在该函数中执行该操作,则必须使用从堆中分配它。记住,你必须这样做。

为什么不简单地:

void createWindow(Control * c) {
    *c = {
        .type = "window",
        .surface = SDL_SetVideoMode(WIN_W, WIN_H, 24, SDL_SWSURFACE),
        .height = WIN_H,
        .width = WIN_W,
        .x = 0,
        .y = 0
    };
}
然后:

Control c;
createWindow(&c); 
通过这种方式,您可以在调用位置为对象分配空间,并将其地址传递给函数以初始化该地址,这实际上是RVO的手动实现,编译器很可能足够聪明,能够判断指针是否指向堆栈对象,并在不调用函数的情况下直接初始化数据

Control* createWindow() {
    Control window = {
        .type = "window",
        .surface = SDL_SetVideoMode(WIN_W, WIN_H, 24, SDL_SWSURFACE),
        .height = WIN_H,
        .width = WIN_W,
        .x = 0,
        .y = 0
    };
    return &window;
}
引用堆栈上的内存以使用其函数已返回的对象是一个非常糟糕的主意。该数据可能会在那里保留一段时间,但当堆栈再次达到该深度时,数据将被覆盖,下次您将获取和/或生成垃圾。您还可以动态分配内存并返回指向该内存的指针,同时记住在调用位置处理该数据或依赖于其他一些管理方案,但在您的情况下,这将是一个小开销,可以避免过度使用

对于你的问题,一个全球性的解决方案是相当笨拙的,而且完全没有意义。如果您决定拥有多个窗口,是否要在每次需要时编辑源并重新编译以添加另一个全局窗口?这似乎不是个好主意


编辑:我在发布代码之前没有测试该代码,假设它可以工作,但指定的初始值设定项似乎不能与取消引用的指针一起工作。将初始值设定项强制转换为您在注释中提到的类型可以正常工作,编译器生成的代码应该与初始化过程相同,即c->type=window等等,这是我个人会做的。

您到底觉得奇怪什么?我今天才发现这一点。显然,这是一种初始化结构的合法方法,而且它是有效的:我明白了。如何复制结构?我认为一个好的解决方案是将窗口和面板移动到全球范围。你同意吗?@AdarHefer不返回指针,只返回一个控件结构。编译器将以最有效的方式为您处理复制。非常感谢您的建议。这似乎更有意义。奇怪。编译器在赋值发生的那一行说*c={…错误:需要一个表达式。为什么会发生这种情况?回答我自己的问题:在赋值之前必须强制转换以控制。有意义:非常感谢你的帮助!顺便说一句,正如Joachim在回答的评论中所指出的,你不妨返回结构本身。编译器可能会使用RVO而不是实际复制st因此,它将有效地隐式执行此答案中的代码显式执行的操作,例如,使用调用位置中分配的存储并直接将数据存储在那里,而不是在堆栈上创建另一个实例并在上面复制值。