【问题标题】:Moving index of an array and then freeing it移动数组的索引然后释放它
【发布时间】:2021-06-11 07:32:52
【问题描述】:

我正在读取文件,然后将其放入结构中。在阅读名称时,我也在检查重复名称,然后测试他们的 ID 是否大于另一个。如果它更大,那么我将向下移动更大的条目 ID 以替换它,然后释放较小的条目 ID。但是,当我释放较小的条目 ID(我不想要的那个)时,它会释放两个内存位置。

下面我的 GDB 测试器更详细地显示了我的内存位置问题:

89                              cards[upd_j - 1] = cards[j];
1: cards[0]->name = 0x55555555a688 "Stolen by the Fae"
2: cards[1]->name = <error: Cannot access memory at address 0x10>
3: cards[2]->name = 0x55555555a898 "Eternal Isolation"
4: cards[3]->name = <error: Cannot access memory at address 0x91>
5: cardsaccum = 2
6: j = 2
7: num_entries = 3
8: upd_j = 2
(gdb) n
91                          free(cards[upd_j]->start);
1: cards[0]->name = 0x55555555a688 "Stolen by the Fae"
2: cards[1]->name = 0x55555555a898 "Eternal Isolation"
3: cards[2]->name = 0x55555555a898 "Eternal Isolation"
4: cards[3]->name = <error: Cannot access memory at address 0x91>
5: cardsaccum = 2
6: j = 2
7: num_entries = 3
8: upd_j = 2
(gdb) n
92                          free(cards[upd_j]);
1: cards[0]->name = 0x55555555a688 "Stolen by the Fae"
2: cards[1]->name = 0x55555555a898 "\020\220UUUU"
3: cards[2]->name = 0x55555555a898 "\020\220UUUU"
4: cards[3]->name = <error: Cannot access memory at address 0x91>
5: cardsaccum = 2
6: j = 2
7: num_entries = 3
8: upd_j = 2

这是我的结构:

typedef struct card
{
    char* start;
    unsigned int id;
    char* name;
    char* cost;
    unsigned int converted_cost;
    char* type;
    char* text;
    char* stats;
    enum rarity rarity;
} card_t;

代码:

#include "card.h"
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

void parser(struct card **cards,int i, char *pointer);

// FUNCTION FOR QSORT()
int cmpname (const void *pa, const void *pb) {
    const card_t * const *p1 = pa;
    const card_t * const *p2 = pb;
        return strcmp((*p1)->name, (*p2)->name);
}

// FOR OUTPUTTING THE RARITY WITH A CHAR
const char *rarities[] = { "common", "uncommon", "rare", "mythic" };

int main(int argc, char **argv) {   
    int padding;
    ssize_t result;
    int num_entries = 0;
    int i = 0;
    int cardsaccum = 0;
    int boolean;
    
    FILE *input_file;
    
    if((input_file = fopen(argv[1], "r")))
    {
        card_t **cards = malloc(sizeof(*cards));
        
        // getline() STORES THE LINE IN BUF
        char *buf = NULL;
        size_t bufsiz = 0;
    
        // SKIPS HEADER LINE
        getline(&buf, &bufsiz, input_file);
        
        // WHILE THE FILE STILL HAS TEXT
        while ((result = getline(&buf, &bufsiz, input_file)) != -1)
        {       
            //if (boolean != 1)
            //{
                num_entries++;
                
                // ALLOCATES MEMORY
                cards = realloc(cards, sizeof(*cards) * num_entries);
                cards[cardsaccum] = malloc(sizeof(card_t));
            //}
            
            boolean = 0;
            
            // STORE CHARACTERS OF BUF IN ARRAY
            char *array = strdup(buf);
            // MAKE COPY OF ARRAY TO MAKE CHANGES TO 
            char *stringp = array;
            
            // COPIES MEMORY ADDRESS TO FREE LATER ON 
            cards[cardsaccum]->start = array;
            
            // SEPERATES FILE WHEN , IS FOUND
            cards[cardsaccum]->id = atoi(strsep(&stringp, ","));
                
            stringp++; 
            cards[cardsaccum]->name = strsep(&stringp, "\"");;  
            
            
            //TESTING FOR DUPLCIATES
            for (int j = 0; j < cardsaccum; j++)
            {
                //CHECK IF CURRENT = PREVIOUS
                if (strcmp(cards[cardsaccum]->name, cards[j]->name) == 0)
                {
                    // ID > THAN PREVIOUS?
                    if(cards[cardsaccum]->id > cards[j]->id)
                    {
                        // FREE CARD AT POSITION J
                        free(cards[j]->start);
                        free(cards[j]);
                        cards[j] = NULL;
                        // GET THE REST OF THE INFO FOR THE CORRECT ENTRY
                        parser(cards,cardsaccum, stringp);
                        
                        j++;

                        int upd_j = j;
                        // FREEING THE LESSER CARD ID
                        cards[upd_j - 1] = cards[j];    
                        free(cards[upd_j]->start);
                        free(cards[upd_j]);
                        cards[upd_j] = NULL;
                        // NO NEED TO ACCUM SINCE WE FREED ONE CARD
                        num_entries--;
                        
                    }
                    else
                    {
                        free(cards[cardsaccum]->start);
                        free(cards[cardsaccum]);
                        cards[cardsaccum] = NULL;
                        num_entries--;
                    }
                    //  IF DUPLICATE IS FOUND, SET BOOLEAN TO 1
                    boolean = 1;
                    break;
                }   
            }
            
            // IF NOT DUPLICATE
            if (boolean != 1)
            {
                parser(cards,cardsaccum,stringp);
                cardsaccum++;
            }
        }
        
        if(boolean == 1)
        {
            cards[cardsaccum] = NULL;
        }

        // QSORT
        qsort(cards, cardsaccum, sizeof(*cards), cmpname);  
            
        for(i = 0; i < cardsaccum;i++)
        {
            padding = strlen(cards[i]->name);
            padding = 51 - padding;
            printf("%-s %*s\n", cards[i]->name, padding, cards[i]->cost);
            printf("%-*s %*s\n", 43, cards[i]->type, 8, rarities[cards[i]->rarity]);
            printf("----------------------------------------------------\n");
            printf("%s \n", cards[i]->text);
            printf("----------------------------------------------------\n");
            printf("%*s\n", 52, cards[i]->stats);
            printf("\n");
        }
            
        for(i = 0; i < cardsaccum; i++)
        {
            free(cards[i]->start);
        }
            
        for(i=0; i < cardsaccum ;i++)
        {
            free(cards[i]);
        }
        free(buf);
        free(cards);
        
        // CLOSING FILE
        fclose(input_file); 
        
        return 0;
    }
    else
    {
        fprintf(stderr, "./parser: cannot open(%s%s%s): No such file or directory\n", "\"", argv[1], "\""); 
        return 1;
    }
}

【问题讨论】:

  • 您是否尝试过使用 gdb 逐步检查会发生什么?你能分享一个触发问题的输入文件吗?
  • 我没有详细调查你的代码,所以我不知道错误发生在哪里,但我认为如果你按顺序执行代码会更容易:(1)阅读所有卡片,现在不关心重复。 (2) 按名称对卡片进行排序。无论如何,你都会这样做。现在所有相同的卡片都彼此相邻。 (3) 遍历数组并对其进行过滤,以便您只保留相同卡片块中的最高 id。 (您可以扩展您的排序标准,以便对于相等的字符串,最高 ID 始终是第一张卡片。)

标签: arrays c memory struct allocation


【解决方案1】:

当您在位置j 找到重复的卡并且您希望将新卡保留在cardsaccum 时,您应该:

  • “销毁”(即释放所有资源)j 的卡;
  • 将新卡放入插槽j

你不应该对邻接卡片做任何事情,所以请忘记你用upd_jj++做的那个小舞。

您的代码应如下所示:

if(cards[cardsaccum]->id > cards[j]->id)
{
    // destroy old card
    free(cards[j]->start);
    free(cards[j]);

    // copy current card
    cards[j] = cards[cardsaccum];;
        
    // complete parsing current card
    parser(cards, j, stringp);

    num_entries--;
}

一般来说,您的代码有点复杂。比如为什么num_entriescardsaccum都有,基本上是一样的东西,只是你更新的地方不同而已。

正如我在 cmets 中已经说过的,我认为以下策略会更容易实现:

  • 阅读所有卡片,不要在意重复。
  • 按名称对卡片进行排序,或者先按名称排序,然后按 ID 排序。
  • 过滤掉现在都是相邻的重复项。

我还建议为你的卡片写一个析构函数,这样你就不用写了

free(cards[j]->start);
free(cards[j]);

一直都是。当您在销毁卡片时向free 添加更多需要的东西时,析构函数会派上用场。

【讨论】:

    猜你喜欢
    • 2016-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-27
    • 1970-01-01
    • 1970-01-01
    • 2011-10-07
    • 1970-01-01
    相关资源
    最近更新 更多