这是在一个课程项目中出现的。这个问题可能有点太啰嗦了,但是因为我真的不知道问题到底在哪里,所以把它全部写出来可能会有用。
假设我们有一个这样的结构:
typedef struct
{
char *name;
char **friends;
int numFriends;
} User;
我们有一个结构体的容器:
typedef struct
{
User *users;
int db_size;
} DB;
我们还有两个功能来管理好友:
void makeFriends(char *name1, char *name2, DB *db)
{
User user1 = findByName(name1);
User user2 = findByName(name2);
user1.friends[numFriends] = name2;
user2.friends[numFriends] = name1;
user1.numFriends++;
user2.numFriends++;
update_db(user1, db);
update_db(user2, db);
}
void unmakeFriends(char *name1, char *name2, DB *db)
{
User user1 = getByName(name1);
User user2 = getByName(name2);
for (int i = 0; i < user1.numFriends; ++i)
{
if (strcmp(user1.friends[i]), name2)
{
int size = 0;
char *newFriendList = malloc(APPROPRIATE_AMOUNT);
for (int j = 0; j < count; ++i)
{
if (j != i)
{
newFriendList[size] = user1.friends[j];
size++;
}
}
user1.friends = newFriendList;
user1.numFriends = size;
// this breaks things for some reason
//free(newFriendList);
}
}
// and the same for user2
for (int i = 0; i < user2.numFriends; ++i)
{
if (strcmp(user2.friends[i]), name2)
{
int size = 0;
// this can lead to the corruption of user1's list
// char *newFriendList = malloc(APPROPRIATE_AMOUNT);
// but this works fine
char *newFriendList = calloc(someNum, APPROPRIATE_AMOUNT);
for (int j = 0; j < count; ++i)
{
if (j != i)
{
newFriendList[size] = user2.friends[j];
size++;
}
}
user2.friends = newFriendList;
user2.numFriends = size;
// this breaks things for some reason
//free(newFriendList);
}
}
update_db(user1, db);
update_db(user2, db);
}
我们创建三个用户,给他们一个名字并为他们的好友列表分配一些内存:
User vladimir = { .name = "Vladimir", .friends = malloc(APPROPRIATE_AMOUNT), 0 };
User estragon = { .name = "Estragon", .friends = malloc(APPROPRIATE_AMOUNT), 0 };
User pozzo = { .name = "Pozzo", .friends = malloc(APPROPRIATE_AMOUNT), 0};
现在,假设弗拉基米尔和爱斯特拉冈想成为朋友,然后爱斯特拉冈和波佐也成为了朋友。一段时间过去了,弗拉季米尔和爱斯特拉冈觉得他们并不喜欢对方,所以其中一个和另一个解除了好友关系。
如果我运行函数unmakeFriends
并使用malloc
两次,第一个用户的朋友列表在第二个循环期间被损坏(如出现两次的名称或其他未定义的行为)。如果我使用两次callloc,就会得到这个或者总线错误。如果我尝试释放内存,就会得到总线错误。如果我按照现在的方式使用代码,一个作为malloc
,另一个作为calloc
,它会按预期工作。
发生了什么,为什么?
calloc 0初始化动态分配的缓冲区。Malloc只是把它当作垃圾。