【问题标题】:C Programming: Reading data from a file, dynamically allocating memory, placing contents in struct arrayC 编程:从文件中读取数据、动态分配内存、将内容放入结构数组
【发布时间】:2021-06-09 22:50:33
【问题描述】:

这是我在 stackoverflow 上的第一篇文章。我是一名学习 C 的 CS 学生,我在处理的问题上遇到了一些问题。另外,我应该说我知道的很少,所以如果我放在这里的任何东西被认为是愚蠢或无知的,那绝对不是我的本意

我知道还有其他类似的帖子,但是到目前为止,我觉得我已经尝试了很多修改,都以相同的结果结束。

我得到一个文本文件,其中每一行都包含 studentName(tab)gpa。文件总大小未知,我必须使用动态内存分配。

文本文件格式示例

Jordan  4.0
Bhupesh 2.51

程序的一般步骤

为了让自己免于尴尬,许多细节将被省略,但我将简要概述我正在努力解决的过程:

 1.) Create dynamic memory array to hold struct for each line
 2.) Start looping through file
 3.) check the current size of the array to see if reallocation is necessary
 4.) Create dynamic array to hold name
 5.) Place name and gpa into struct
 6.) rinse & repeat

最后,最后一件事。当达到我的初始分配内存限制并且程序尝试从堆中重新分配更多内存时,就会发生错误。

Screenshot of error being thrown in clion debugger

我的代码如下所示:

#define EXIT_CODE_FAIL 1
#define ROW_COUNT 10
#define BUFFER_SIZE 255
#define VALID_ARG_COUNT 2

struct Student {
    float gpa;
    char * name;
};

// read the file, pack contents into struct array
struct Student * readFileContents(char *filename, int *rowCounter) {

    // setup for loop
    int maxDataSize = ROW_COUNT;
    float currentStudentGpa = 0;
    char studentNameBuffer[BUFFER_SIZE];

    // initial structArray pre-loop
    struct Student * structArray = calloc(maxDataSize, sizeof(*structArray));

    FILE *pFile = fopen(filename, "r");
    validateOpenFile(pFile);


    // loop through, get contents, of eaach line, place them in struct
    while (fscanf(pFile, "%s\t%f", studentNameBuffer, &currentStudentGpa) > 0) {
        structArray = checkArraySizeIncrease(*rowCounter, &maxDataSize, &structArray);
        structArray->name = trimStringFromBuffer(studentNameBuffer);
        structArray->gpa = currentStudentGpa;
        (*rowCounter)++, structArray++;
    }

    fclose(pFile);

    return structArray;
}

// resize array if needed
struct Student * checkArraySizeIncrease(int rowCount, int * maxDataSize, struct Student ** structArray) {

    if (rowCount == *maxDataSize) {
        *maxDataSize += ROW_COUNT;
        
        **// line below is where the error occurs** 
        struct Student * newStructArray = realloc(*structArray, *maxDataSize * sizeof(*newStructArray));
        validateMalloc(newStructArray);

        return newStructArray;
    }
    return *structArray;
}

// resize string from initial data buffer
char *trimStringFromBuffer(char *dataBuffer) {

    char *string = (char *) calloc(strlen(dataBuffer), sizeof(char));
    validateMalloc(string);
    strcpy(string, dataBuffer);

    return string;
}


再次,如果有人问过类似的问题,我深表歉意,但请注意,我已经尝试了我在堆栈溢出中发现的大多数建议,但都没有成功(我很清楚这是我糟糕的编程的结果C)的技能水平。

我现在将立即为我的强制性“stackoverflow 上的第一篇文章”烘焙做好准备。干杯!

【问题讨论】:

  • @paladin Umm, realloc 在大多数情况下都可以正常工作。如果失败,malloc 可能也会失败。使用另一个 malloc 会泄漏内存。
  • 如果你增加structArray,你不能realloc它。您需要在最初分配的内存位置上调用realloc。保持对开头的引用,并增加一个不同的指针。
  • calloc(strlen(dataBuffer), sizeof(char)); 中的分配缺少 0 终止符,您有可能存在缓冲区溢出
  • @paladin 你真的应该停下来。你认为的灵丹妙药是 realloc 已经做到的。我的印象是,您认为如果realloc 无法扩大当前区域,它将失败。那不是真的。它将简单地执行 malloc、复制、免费,因此没有理由不将 realloc 用于此处使用的动态数组。
  • 欢迎来到 Stack Overflow。您将获得对格式良好且问得很好的问题的投票(将来不会懈怠......)

标签: c struct malloc dynamic-memory-allocation realloc


【解决方案1】:

您正在重用structArray 作为数组的基础一个指向当前元素的指针。这行不通。我们需要两个变量。

有许多与动态数组相关的“松散”变量。定义一个struct(例如下面的dynarr_t)来包含它们并只传递struct 指针会更简洁。

当你复制字符串时,你必须分配strlen + 1 [只是strlen]。但是,整个函数做了strdup 已经做的事情。

我尝试尽可能多地保存,但我不得不重构代码以包含所有必要的更改。

通过将sizeof(*structArray) 传递给arrnew 函数,这允许该结构用于任意大小的数组元素。

不管怎样,代码如下:

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

#define sysfault(_fmt...) \
    do { \
        printf(_fmt); \
        exit(1); \
    } while (0)

#define EXIT_CODE_FAIL 1
#define ROW_COUNT 10
#define BUFFER_SIZE 255
#define VALID_ARG_COUNT 2

struct Student {
    float gpa;
    char *name;
};

// general dynamic array control
typedef struct {
    void *base;                         // base address
    size_t size;                        // bytes in array element
    size_t count;                       // current number of used entries
    size_t max;                         // maximum number of entries
    size_t grow;                        // number of entries to grow
} dynarr_t;

// arrfind -- return pointer to array element
void *
arrfind(dynarr_t *arr,size_t idx)
{
    void *ptr;

    ptr = arr->base;
    idx *= arr->size;
    ptr += idx;

    return ptr;
}

// arrnew -- create new array control
dynarr_t *
arrnew(size_t siz,size_t grow)
// siz -- sizeof of array element
// grow -- number of elements to grow
{
    dynarr_t *arr;

    arr = calloc(1,sizeof(*arr));
    if (arr == NULL)
        sysfault("arrnew: calloc fail -- %s\n",strerror(errno));

    arr->size = siz;
    arr->grow = grow;

    return arr;
}

// arrgrow -- grow array [if necessary]
// RETURNS: pointer to element to fill
void *
arrgrow(dynarr_t *arr)
{
    void *ptr;

    // grow array if necessary
    // NOTE: use of a separate "max" from "count" reduces the number of realloc
    // calls
    if (arr->count >= arr->max) {
        arr->max += arr->grow;
        arr->base = realloc(arr->base,arr->size * arr->max);
        if (arr->base == NULL)
            sysfault("arrgrow: realloc failure -- %s\n",strerror(errno));
    }

    // point to current element
    ptr = arrfind(arr,arr->count);

    // advance count of elements
    ++arr->count;

    return ptr;
}

// arrtrim -- trim array to actual number of elements used
void
arrtrim(dynarr_t *arr)
{

    arr->base = realloc(arr->base,arr->size * arr->count);
    if (arr->base == NULL)
        sysfault("arrtrim: realloc failure -- %s\n",strerror(errno));

    arr->max = arr->count;
}

void
validateMalloc(void *ptr)
{

    if (ptr == NULL) {
        perror("validateMalloc");
        exit(1);
    }
}

void
validateOpenFile(FILE *ptr)
{

    if (ptr == NULL) {
        perror("validateOpenFile");
        exit(1);
    }
}

// resize string from initial data buffer
char *
trimStringFromBuffer(char *dataBuffer)
{

#if 0
#if 0
    char *string = calloc(1,strlen(dataBuffer));
#else
    char *string = calloc(1,strlen(dataBuffer) + 1);
#endif
    validateMalloc(string);
    strcpy(string, dataBuffer);
#else
    char *string = strdup(dataBuffer);
    validateMalloc(string);
#endif

    return string;
}

// read the file, pack contents into struct array
dynarr_t *
readFileContents(char *filename)
{
    dynarr_t *arr;

    // setup for loop
    float currentStudentGpa = 0;
    char studentNameBuffer[BUFFER_SIZE];
    struct Student *structArray;

    arr = arrnew(sizeof(*structArray),10);

    FILE *pFile = fopen(filename, "r");
    validateOpenFile(pFile);

    // loop through, get contents, of eaach line, place them in struct
    while (fscanf(pFile, "%s\t%f", studentNameBuffer, &currentStudentGpa) > 0) {
        structArray = arrgrow(arr);
        structArray->name = trimStringFromBuffer(studentNameBuffer);
        structArray->gpa = currentStudentGpa;
    }

    fclose(pFile);

    arrtrim(arr);

    return arr;
}

【讨论】:

  • 我总是觉得使用 do { ... } while; 宏包装有点不舒服,除非我确定它会被 OPs 编译器接受。不知道clion支持不支持,这里可能100%没问题。
  • @DavidC.Rankin 这是 100% 直接的 C——没有魔法。之所以会引起争议,只是因为有些人使用do { ... } while (i &lt; 10);期望它来循环。在我工作过的所有地方,只有一个公司的一个同事有心理问题。在这里,在SO上,它被理解了。其实是别人建议的。见【最近】:stackoverflow.com/questions/66573666/…顶上,其他人在建议do { } while (0);
  • 我不知道为什么,也不知道它是哪个编译器,但它不需要 do { ... } while (0) 包装的宏。我认为它是 VS10,但我可能不在那儿。不管:),你都得到了我的投票
  • @CraigEstey 非常感谢您抽出宝贵的时间来做这件事。这非常有用。我花了太多时间来理解这一点。使用结构的方式真是太棒了。一旦获得我非常感谢您的帮助,您就是英雄!
【解决方案2】:

我认为您的问题在于重新分配大小的计算。而不是使用sizeof(*newStructArray),你真的不应该使用指针类型的大小吗?我会写成realloc(*structArray, *maxDataSize * sizeof(struct Student *))

这里还有很多其他我永远不会做的事情 - 将所有这些变量作为指针传递给 checkArraySizeIncrease 通常是一个坏主意,因为它可以掩盖事情正在发生变化的事实,例如。

【讨论】:

  • 谢谢!这是我的代码的另一个问题。非常感谢您的见解!
【解决方案3】:

字符串的缓冲区分配有问题

char *string = (char *) calloc(strlen(dataBuffer), sizeof(char));

应该是:

char *string = (char *) calloc(1 + strlen(dataBuffer), sizeof(char));

因为 C 字符串在末尾需要额外的 0 字节。 没有它,下面的操作:

strcpy(string, dataBuffer);

可能会损坏缓冲区后的数据,可能会弄乱malloc() 元数据。

【讨论】:

  • 非常感谢您的帮助!我做了这个改变!
猜你喜欢
  • 2012-04-18
  • 1970-01-01
  • 1970-01-01
  • 2018-11-13
  • 1970-01-01
  • 1970-01-01
  • 2021-10-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多