【问题标题】:Memory allocation failure on last loop iteration最后一次循环迭代的内存分配失败
【发布时间】:2014-11-20 17:24:51
【问题描述】:

所以...过去我被告知我的问题不好...我相信主要是因为我没有足够好地隔离有问题的代码。我会尽力在这篇文章中提出一个尖锐、简洁、中肯的问题。我当然愿意接受有关如何更好地提出我的问题的建议。 谢谢。

我正在使用 C 语言开发一个小项目,该项目将作为我已经研究了一段时间的大型、有缺陷的程序的原型。我正在尝试首先在一个较小的程序中解决细节。我有两个结构:

struct list
{
    char ownerName[20];
    int ownerAge;
    char sex;
}owner;

struct list2
{
    char petName[20];
    char owner[20];
    char animal[4];
    char breed[50];
    char color[20];
}pets;

该程序应该从用户输入中获取 ownerName 并将其与 pets 结构中的“.owner”进行比较。然后将 ownerName 和 petName 元素复制到一个数组中,并将所有者和他/她的宠物的名字打印在一个列表中。虽然我知道我不需要所有者结构来完成此操作,但我正在使用它来模拟我正在编写的其他程序。

我正在使用

if (strcmp(pets[i].owner, name) == 0) 

比较结构元素,似乎这部分已经下来了。

变量 j 统计满足这个条件的记录数,变量 l = j + 1。我调用数组使用:

char *petsList[l];

数组的大小由 l (j + 1) 决定,因为我需要 j 个元素作为 petNames + 1 个元素作为所有者名称。

我还通过以下方法创建了一个指向 petsList 数组的指针:

char *(*ptr)[l] = &petsList

通过以下命令将所有者名称添加到数组中:

(*ptr)[0] = (char *)malloc(sizeof(name));
strcpy ( (*ptr)[0], name);

使用 for 循环将 petNames 添加到数组 petsList 中。我已初始化 i = 1 以防止 petsList[0] 被覆盖,并尝试通过以下循环将 petNames 写入数组:

 i = 1;

        for (k=0; k < PETS; k++)
        {
            if (strcmp(pets[k].owner, name) == 0)
            {
                (*ptr)[i] = (char *)malloc(sizeof(pets[k].petName));
                if (!*(ptr)[i])
                {
                    puts("\nMemory Allocation Error");
                    exit (1);
                }
                strcpy( (*ptr)[i], pets[k].petName);
                i++;
             }
         }

假设对于给定的名称输入,我得到了三个匹配的宠物。循环迭代前两次就好了,但是在循环的第三次迭代中,我得到了内存分配错误。这始终发生在循环的最后一次迭代中。例如,如果我有 2 只宠物与 ownerName 关联,则列表将在第一次迭代中正常运行,而在第二次迭代中失败;如果我有 4 只宠物与 ownerName 相关联,则循环将在前 3 次正常运行并在第四次失败,因此循环的最终迭代似乎始终失败。我已经尝试过多次更改代码,但现在我不知道如何继续使用这个程序。任何帮助是极大的赞赏。

谢谢。

【问题讨论】:

  • *ptr 数组的大小是多少? i 是有效索引吗?还有in C you should not cast the return of malloc.
  • valgrind 必须说明程序做错了什么。 VLA 的动态分配是可行的,但很棘手;我必须非常努力地查看代码才能知道它是否正确并且现在没有时间。请考虑 MCVE (Minimal, Complete, Verifiable Example) 或 SSCCE (Short, Self-Contained, Correct Example) 的优点。
  • sizeof(name) 获取的不是字符串的长度,而是char * 的大小。试试malloc(strlen(name)+1)。另外,为什么你需要一个指向petsList 的指针而不是直接使用它?如果你这样做了,你可以使用char **ptr = petsList; 并使用ptr[i] 访问ith char *,这可能更容易(阅读和使用)。像您这样的数组指针可能对数组数组更有意义,但这不是您所拥有的。
  • 这个问题还是太长了。为了演示内存分配,我们真的需要了解宠物名称、品种和颜色吗?
  • @JoachimPileborg 未设置数组的大小。如果 3 个宠物名称与所有者相关联,则数组大小为 4,其中 3 个用于 petNames,1 个用于 ownerName。我使用 j 来计算匹配 petNames 的实例,并使用 l(等于 j + 1)来声明数组的大小。我相信 i 是一个有效的索引;它似乎在循环中迭代了几次。什么会使 i 作为索引无效?感谢您的回复。

标签: c arrays memory-management struct


【解决方案1】:

不要强制转换 malloc 返回值。

因为我不能用一个最小的例子来检查。

char *(*ptr)[l] = &petsList

你为什么要做这么复杂的结构?我什至不确定它应该完成什么。您是否希望第一个索引和下一个索引中的所有宠物都包含所有者?这可以通过petslist

来完成

最后你真正需要什么结构?

是不是有点像:

数组:

0 = owner
1 = pet 1
2 = pet 2

或类似的东西

0,0 = owner       1,0 = owner 2        etc.
0,1 = pet 1       1,1 = pet 3
0,2 = pet 2       1,2 = pet 4

好的,这是您想要做的一个工作示例。您可以轻松扩展它以进行第二次数据排列。如果您有任何问题,请随时提出

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define PETAMOUNT   40

struct list2
{
    char petName[20];
    char owner[20];
    char animal[4];
    char breed[50];
    char color[20];
};

int main() {
    struct list2 *pets;             // list of all pets
    char name[128];                 // contain name of the owner, get from stdin

    unsigned int i;                 // i and j are both counter variable
    unsigned int j;

    fgets(name, 128, stdin);        // get string from stdin
    name[strlen(name) - 1] = '\0';  // remove newline

    pets = malloc(PETAMOUNT * sizeof(struct list2));    // allocate memory for the list of all pets
    if (pets == NULL) {
        printf("malloc err\n");
        exit(1);
    }

    for (i = 0; i < PETAMOUNT; i++) {           // initialize some pets and some owners
        strcpy(pets[i].petName, "petname ");
        strcpy(pets[i].owner, "owner ");
        pets[i].petName[7] = i + '0';           // there are PETAMOUNT of petnames. petname0, petname1 etc
        pets[i].owner[5] = (i / 4) + '0';       // there are PETAMOUNT / 4 owners. owner0 has petname0 to petname3, owner1 has petname4 to 7 etc
    }


    char ***petslist;                       // a list of list of strings or 3d char array
    petslist = malloc(sizeof(char **));     // allocate pointer to contain a double array
    petslist[0] = malloc(sizeof(char *));   // allocate a pointer to contain the name of the owner
    if (petslist[0] == NULL) {
        printf("malloc err\n");
        exit(1);
    }
    petslist[0][0] = malloc(strlen(name) + 1); // allocate memory to contain the owner
    if (petslist[0][0] == NULL) {
        printf("malloc err\n");
        exit(1);
    }
    strcpy(petslist[0][0], name);             // copy owner into the first index

    for (i = 0, j = 1; i < PETAMOUNT; i++) {      // go through all pets 
        if (strcmp(pets[i].owner, name) == 0) {   // if the owner of the current pet is the same as the inputted owner  
            petslist[0] = realloc(petslist[0], (j + 1) * sizeof(char *));   // allocate pointer for the next pet
            petslist[0][j] = malloc(strlen(pets[i].petName) + 1);           // allocate memory to contain the chars of the pet
            if (petslist[0][j] == NULL) {
                printf("malloc err\n");
                exit(1);
            }
            strcpy(petslist[0][j], pets[i].petName);    // copy the petname into the array
            j++;
        }
    }

    puts("petslist:");              // print it all out
    for (i = 0; i < j; i++) {
        printf("|%s|\n", petslist[0][i]);
    }

    exit(0);

    }

目前我总是写到 [0][0] 但如果你重新分配,你可以在那之后腾出更多空间

【讨论】:

  • 感谢您的回复,罗文。就那个指针而言:我认为这就是我必须构造一个指向字符串数组的指针的方式。我不希望两个索引中的所有宠物都包括所有者。我希望拥有者为 petsList[0],并且宠物在 petsList[i] 中列出,其中 i != 0。我在该程序末尾需要的结构是第一个示例,但对于更大的项目,我需要更多的东西像第二个例子。再次感谢。
  • 哇...感谢您为这个 Rowan 所做的努力。我非常感谢。我的代码按照我编写的方式工作,但我将输入并编译您编写的代码并比较两者。再次感谢你的帮助。这非常有帮助。
【解决方案2】:

这有点奇怪。也许:

if(!*(ptr)[i])

应该是

if(!(*ptr)[i])

?

【讨论】:

  • 感谢您的回复,Shooper。共识似乎是您在我的代码中指定的指针是不必要且有问题的。我可能会尝试在不使用该指针的情况下重写。至于你的回应,改变 * 的位置不会改变优先级并改变 ptr 指向的内容吗?再次感谢。
  • 你的代码有点问题。我不得不坐在那里想想你在做什么。我刚刚发现 malloc(你不应该真的强制转换)被分配给 (*ptr)[i],然后你返回并与 *(ptr)[i] 进行比较。老实说,我不想考虑太多。
  • 实际上……这似乎有很大帮助。该程序现在按我的预期运行。非常感谢您的回复。
  • 对我来说,编码始终是一项正在进行的工作。我可能会用几种方法重写这个程序......例如没有指针来尝试更好地理解并使代码更有效率。我非常感谢您的意见。谢谢。
猜你喜欢
  • 2016-08-11
  • 1970-01-01
  • 2013-03-18
  • 1970-01-01
  • 2011-07-11
  • 1970-01-01
  • 2016-05-10
  • 2016-08-25
  • 2016-04-23
相关资源
最近更新 更多