realloc与不同的指针重叠
我试图学习C语言,发现了一个很好的练习,它给了我一些问题。我特别编写了以下代码:realloc与不同的指针重叠,c,pointers,memory,struct,C,Pointers,Memory,Struct,我试图学习C语言,发现了一个很好的练习,它给了我一些问题。我特别编写了以下代码: #include <stdlib.h> #include <string.h> #include <stdio.h> #include "trip.h" struct accounting{ char ** people; char ** descriptions; float * amountMoney; int * payers; i
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "trip.h"
struct accounting{
char ** people;
char ** descriptions;
float * amountMoney;
int * payers;
int participants;
int payments;
};
struct accounting *accountTable;
void trip_initialize(){
accountTable = malloc(sizeof(struct accounting));
accountTable->people = malloc(sizeof(char *));
accountTable->descriptions = malloc(sizeof(char *));
accountTable->amountMoney = malloc(sizeof(float *));
accountTable->amountMoney = malloc(sizeof(float *));
accountTable->payers = malloc(sizeof(int *));
accountTable->participants = 0;
accountTable->payments = 0;
};
void trip_shutdown(){
for (int i = 0; i < accountTable->participants; i++){
free(accountTable->people[i]);
}
for (int i = 0; i < accountTable->payments; i++){
free(accountTable->descriptions[i]);
}
free(accountTable->amountMoney);
free(accountTable->payers);
free(accountTable->people);
free(accountTable->descriptions);
free(accountTable);
};
int trip_add_person(const char * name){
accountTable->people = realloc(accountTable->people,sizeof(accountTable->people) + sizeof(char *));
if (!accountTable->people){
return -1;
}
accountTable->people[accountTable->participants] = malloc(sizeof(char *));
accountTable->people[accountTable->participants] = strdup(name);
return accountTable->participants++;
};
int trip_find_person(const char * name){
for (int i = 0; i < accountTable->participants; i++){
if (!strcmp(accountTable->people[i], name)){
return i;
}
}
return -1;
};
int trip_add_expense(const char * descr, float amount, int payer){
if (payer < 0 || payer > accountTable->participants){
return 0;
}
accountTable->descriptions = realloc(accountTable->descriptions, sizeof(accountTable->descriptions) + sizeof(char *));
if (!accountTable->descriptions){
return 0;
}
accountTable->amountMoney = realloc(accountTable->amountMoney, sizeof(accountTable->amountMoney) + sizeof(float *));
if (!accountTable->amountMoney){
return 0;
}
accountTable->payers = realloc(accountTable->payers, sizeof(accountTable->payers) + sizeof(int *));
if (!accountTable->payers){
return 0;
}
accountTable->descriptions[accountTable->payments] = strdup(descr);
accountTable->amountMoney[accountTable->payments] = amount;
accountTable->payers[accountTable->payments] = payer;
accountTable->payments++;
return 1;
};
如您所见,人员[2]指向与描述[0]相同的位置,如果我继续,人员[3]将指向描述[1],依此类推
我想我在某处引入了一些未定义的行为,但我真的不明白在哪里
注意:如果您尝试复制和粘贴此代码,trip\u shutdown()将无法工作,因为free(accountTable->descriptions[0])将尝试释放上一个循环中已释放的指针。转录的注释
引用自
任何行,如:oldptr=realloc(oldptr,newsize)代码>是等待发生的内存泄漏。如果重新分配失败,您只是用NULL
覆盖了oldptr
,因此无法释放仍然分配的内存。始终使用:
newptr = realloc(oldptr, newsize);
if (newptr != 0) { oldptr = newptr; oldsize = newsize; }
else { …handle error… }
(如果您正在跟踪大小-或oldcount=newcount;
如果您正在计算数组条目)
据我所知,oldptr
被NULL覆盖,释放NULL没有任何作用;这就是为什么我检查它是否为NULL,如果为NULL则返回0(在本例中,该练习明确声明返回0)。我认为当realloc
返回NULL时,旧指针会自动释放,但显然不是这样。我会记住的
我认为您在trip\u add\u expense()
中调用realloc()
时遇到了问题。您没有考虑已分配的项目数量-sizeof(accountTable->descriptions)
没有做您认为它做的事情,例如。您应该分别跟踪每个数组(或数组集)中指针的数量,并使用该数量和条目大小等调整大小
size_t new_size = accountTable->num_desc + 1;
char **new_desc = realloc(accountTable->descriptions, (new_size * sizeof(*accountTable->descriptions));
和错误检查,除了增长1也是次优
调用free(NULL)
没有坏处(但也没有好处),但是oldptr
指向的内存在失败时没有被realloc()
释放,但是您不能再将该指针传递到free()
,因为它已被覆盖。这就是内存泄漏。(这也不是代码中唯一的漏洞。)
因此,我使用accountTable->description+sizeof(char*)
来执行realloc
,因为我认为我需要一个额外的char*
,我以后用它来存储指向描述字符串开头的指针。这是错误的,我需要考虑我已经存储的描述的数量吗?另外,我知道按1增长是次优的,它的大小应该是Python对数组的两倍(如果我没记错的话),但因为这对我来说更像是指针的练习,所以我更喜欢这样做,以便更快地编写
问题之一是,您总是分配相同的空间;大小是固定的。除非您处理的是可变长度数组(不是),否则,sizeof(x)
是编译时常量
那么,告诉我我是否理解正确。无论我写了多少次accountTable->description=realloc(accountTable->description+sizeof(char*)
结果总是非常sizeof(accountTable->description)+sizeof(char*)
,因为sizeof
是一个编译时常量,对吗?这就解释了为什么它有两个以上的条目;第一个是有效的,因为我做了初始malloc,第二个是因为我将大小增加了sizeof(char*)
对,;这基本上是正确的。对于第三个条目,分配的空间与分配给两个条目的空间相同,对于4、5、6个条目,分配的空间与分配给2个条目的空间相同。如果你试图使用你认为分配了但实际上没有分配的额外条目,这不会带来快乐。您可以通过打印传递给分配函数的大小值进行演示。您如何知道有多少人、描述、金额和付款人(每个数组有多大)
代码
这大概就是你需要做的。请注意,在问题的评注中已经提到的错误之外,我还注释了一些额外的错误
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct accounting
{
char **people;
char **descriptions;
float *amountMoney;
int *payers;
int participants;
int payments;
};
struct accounting *accountTable;
extern int trip_add_person(const char *name);
extern int trip_find_person(const char *name);
extern void trip_shutdown(void);
extern void trip_initialize(void);
extern int trip_add_expense(const char *descr, float amount, int payer);
void trip_initialize(void)
{
accountTable = malloc(sizeof(struct accounting));
assert(accountTable != 0);
accountTable->people = malloc(sizeof(char *));
accountTable->descriptions = malloc(sizeof(char *));
/*accountTable->amountMoney = malloc(sizeof(float *)); // Repeated - leak! */
accountTable->amountMoney = malloc(sizeof(float *));
accountTable->payers = malloc(sizeof(int *));
accountTable->participants = 0;
accountTable->payments = 0;
assert(accountTable->people != 0 && accountTable->descriptions != 0 &&
accountTable->amountMoney != 0 && accountTable->payers != 0);
}
void trip_shutdown(void)
{
for (int i = 0; i < accountTable->participants; i++)
free(accountTable->people[i]);
for (int i = 0; i < accountTable->payments; i++)
free(accountTable->descriptions[i]);
free(accountTable->amountMoney);
free(accountTable->payers);
free(accountTable->people);
free(accountTable->descriptions);
free(accountTable);
}
int trip_add_person(const char *name)
{
size_t new_num = accountTable->participants + 1;
char **new_acct = realloc(accountTable->people, new_num * sizeof(*accountTable->people));
if (new_acct == 0)
{
fprintf(stderr, "Out of memory (%zu bytes requested)\n", new_num * sizeof(*accountTable->people));
return -1;
}
accountTable->people = new_acct;
/*accountTable->people[accountTable->participants] = malloc(sizeof(char *)); // Leak with strdup! */
accountTable->people[accountTable->participants] = strdup(name);
return accountTable->participants++;
}
int trip_find_person(const char *name)
{
for (int i = 0; i < accountTable->participants; i++)
{
if (!strcmp(accountTable->people[i], name))
{
return i;
}
}
return -1;
}
int trip_add_expense(const char *descr, float amount, int payer)
{
if (payer < 0 || payer > accountTable->participants)
{
return 0;
}
size_t new_num = accountTable->payments + 1;
char **new_desc = realloc(accountTable->descriptions, new_num * sizeof(*accountTable->descriptions));
if (new_desc == 0)
{
fprintf(stderr, "Out of memory (%zu bytes requested)\n", new_num * sizeof(*accountTable->descriptions));
return 0;
}
accountTable->descriptions = new_desc;
float *new_money = realloc(accountTable->amountMoney, new_num * sizeof(*accountTable->amountMoney));
if (new_money == 0)
{
fprintf(stderr, "Out of memory (%zu bytes requested)\n", new_num * sizeof(*accountTable->amountMoney));
return 0;
}
accountTable->amountMoney = new_money;
int *new_payers = realloc(accountTable->payers, new_num * sizeof(*accountTable->payers));
if (new_payers == 0)
{
fprintf(stderr, "Out of memory (%zu bytes requested)\n", new_num * sizeof(*accountTable->payers));
return 0;
}
accountTable->payers = new_payers;
accountTable->descriptions[accountTable->payments] = strdup(descr);
accountTable->amountMoney[accountTable->payments] = amount;
accountTable->payers[accountTable->payments] = payer;
accountTable->payments++;
return 1;
}
int main(void)
{
trip_initialize();
int who_1 = trip_add_person("Original Poster");
int who_2 = trip_add_person("Question Answerer");
int who_3 = trip_add_person("Antibody");
if (who_1 == -1 || who_2 == -1 || who_3 == -1)
fprintf(stderr, "Oops - Adding people!\n");
if (trip_add_expense("Deposit", 200.0, who_1) == 0 ||
trip_add_expense("Deposit", 200.0, who_2) == 0 ||
trip_add_expense("Deposit", 200.0, who_3) == 0)
fprintf(stderr, "Oops - adding expenses!\n");
trip_shutdown();
}
请注意,描述、金额、付款人的三重分配是一团混乱。你应该:
struct Payment
{
char *description;
float amount;
int payer;
};
您的结构应该包含指向这些元素的数组的指针。它将从根本上简化trip\u add\u expense()
功能
我认为您总是比阵列中使用的空间多分配一个空间。在第一次需要一些空间之前,最好不要分配指针。然后我会分配一些东西,比如size\t new\u num=(old\u num+2)*2编码>单位,并记录分配的单位数量和使用的单位数量。这分配了4个,然后是12个,然后是28个…条目供使用(2减去4的幂,正如它发生的那样,+2
处理old_num==0
;你也可以使用new_num=old_num*2+2
,给你2,6,14,30,…作为大小)。好的,非常感谢你,我明白了一切:)正如我所说的,我知道这不是最简单、更合理的方法(事实上,提供的解决方案使用了两个结构阵列,一个用于支付,另一个用于插入的人员)
$ gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
> -Wold-style-definition -Werror mm31.c -o mm31
$
struct Payment
{
char *description;
float amount;
int payer;
};