C 为什么这段代码的行为不可预测?

C 为什么这段代码的行为不可预测?,c,segmentation-fault,malloc,C,Segmentation Fault,Malloc,我正在开发一个控制台游戏,其中包含以下代码: typedef struct player{ char *name; /* ... */ char location; char traveltime; /* ... */ }pl; typedef struct planet{ char *name; /* ... */ }planet; pl *players; planet pla

我正在开发一个控制台游戏,其中包含以下代码:

typedef struct player{
        char *name;
        /* ... */
        char location;
        char traveltime;
        /* ... */
}pl;

typedef struct planet{
        char *name;
        /* ... */
}planet;

pl *players;
planet plan[22];
pl*玩家是马洛克的

players=malloc(NPLAYERS*sizeof(pl));
其中NPLAYERS是玩家数量。 计划[]是游戏中所有行星的数组

players[i].location
是作为计划[]的下标的玩家位置,如果玩家[i]。旅行时间==0。如果玩家[i].traveltime>0,则该玩家将前往玩家[i].location。 因此,当玩家旅行时,我想显示一个ncurses窗口,说明“正在前往(行星)”的途中

为此,我使用:

char *tmp, msg[]="PLAYER 1", i;
for(i=0; i!=NPLAYERS; ++i){
            infobox(msg);
            if( players[i].traveltime>0){
                    tmp=malloc( sizeof("en route to ")+sizeof(plan[ players[i].location ].name)+4)
                    strcpy(tmp, "en route to ");
                    strcat(tmp, plan[ players[i].location ].name);
                    strcat(tmp, "..\0");
                    infobox(tmp);
                    free(tmp);
            }
            ++msg[7];
  }
其中infobox(char msg[])打印一个ncurses窗口,其中包含发送给stdout的消息,NPLAYERS是玩家数量。这段代码在所有玩家之间循环,检查他们是否在旅行,如果是,则打印一条消息,说明他们的目的地。 这十次中有九次有效,但有时它会在空闲时出现分段错误(tmp),在malloc时出现分段错误或打印

    ***** glibc detected *** ./st: malloc(): memory corruption [a hex number] ***
在马洛克之后。 它为什么会这样做?我如何解决它

知道我在一台两岁的笔记本电脑上使用Arch Linux可能会有所帮助。

sizeof(plan[player[I].location].name)
中,您正在使用指针的大小。 你可能想要最大行星名称的大小

    tmp=malloc( sizeof("en route to ")+sizeof(plan[ players[i].location ].name)+4);
vs

弱点在于(过于复杂的)内存分配

由于您的
tmp
缓冲区是临时的,我宁愿使用基于堆栈的缓冲区,使用您的卷积sizeof()尝试分配的最大空间(作为奖励,这将比
malloc()/free()
更快)


只是不要声明它是静态的,这样多个线程就可以一起工作。

你没有可靠地为你的行星名称分配足够的空间,因为你选择的是指针的大小,而不是它所指向的字符串的长度

sizeof(plan[ players[i].location ].name)
这是4或8(取决于您使用的是32位还是64位系统)

您可能需要将其与
+1
一起使用:

strlen(plan[player[i].location].name)
你会得到准随机效应,因为你的大多数行星名称都足够短,当与分配汇总等结合时,你实际上有大约足够的空间。当你处理一个很长的行星名称时,你就会开始遇到麻烦


发生分配舍入是因为大多数分配器以最小大小(可能是8或16(甚至可能是32)字节)为单位分配空间。因此,当您请求(比如)17个字节时,实际上可能会给您一个指向24个甚至32个字节的指针,并且只有在您请求的17个字节溢出到超过舍入大小时,才会遇到严重问题。您永远不应该依赖于取整,因此您不应该访问您请求的范围之外的内存;然而,这种情况通常会发生。

几乎可以肯定的是,您在某个地方溢出了缓冲区,从而破坏了堆。可以使用内存调试器(如Valgrind)对此进行调试,也可以发布一个完整的自包含测试用例来显示问题。谢谢!成功了。我修改了malloc语句,这样它就保留了最长的行星名称(Theta Kaleidis 89)的大小,现在我已经运行了大约一百次这个程序。没有故障,没有坏记忆。(这条评论显示了这么多的编辑,因为我一直在按enter键输入新行)有一天你会添加“Lambda Eridanis 42”行星,你的程序会再次冻结:)你真的应该想出一个更好的方法来确定最大大小——见Johnathan的答案。tmp=malloc(sizeof(“在前往”)+strlen(计划[plant[players[I].location].姓名)+4);?那很好。我会在
4
中添加常量
sizeof(“中途”)==13
,并提供一个注释来解释这个神奇的数字。我认为这将暂时起作用,尽管作为数组下标的结构成员看起来不太好,但一旦我让游戏的其余部分工作起来,我会研究基于堆栈的缓冲区,这是乔纳森·莱夫勒和吉尔推荐的。如何将此线程标记为已解决?
strlen(plan[player[i].location].name)