realloc与不同的指针重叠

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

我试图学习C语言,发现了一个很好的练习,它给了我一些问题。我特别编写了以下代码:

#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;
};