C - fclose() 触发断点

标签 c breakpoints stdio fclose

我正在编写一个函数 (*rwObjects()),它将读取格式化文件并保存它的字符串,一次一个对象。不幸的是,它对我的​​研究有限制 - stdio.h、stdlib.h 和 string.h 几乎是我可以使用的所有内容。

问题是:每当我运行代码时,当它到达 fclose(input) 时,VS17 说我的项目触发了一个断点,然后打开一个选项卡,上面写着“wntdll.pdb not加载”或其他东西。

问题是:如何不触发断点并正确关闭文件?或者,如果问题不在文件中,那么它在哪里?

代码(C):

#define _CRT_SECURE_NO_WARNINGS
#define cnCOUNTRY_LENGTH 3
#define cnOBJECT_NAME_LENGTH 30
#define cnOBJECT_MAX 1000

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

//--Поле объекта objectType--
//Country (0)       - строка названия страны
//ObjectName (1)    - строка названия объекта
//Square (2)        - площадь объекта
//Error (3)         - ошибка в содержании строки
typedef enum IOOptions {Country, ObjectName, Square, Error} IOType;

//--Тип обрабатываемых объектов--
//char Country      - строка названия страны
//char ObjectName   - строка названия объекта
//int Square        - площадь объекта
typedef struct object {
    char Country[cnCOUNTRY_LENGTH];
    char ObjectName[cnOBJECT_NAME_LENGTH];
    int Square;
} objectType;

//--Копирование текущего элемента строки objects.txt--
//strMod            - Строка, в которую идёт копирование
//strPos            - Позиция в считываемой строке
//strBlueprint      - Строка, из которой идёт копирование
//writeType         - Поле объекта objectType. При "Country" - переводит вводимые символы в верхний регистр ('a' -> 'A' и т.д.)
void copyInputStr(char *strMod, int *strPos, char *strBlueprint, IOType writeType) {
    for (*strPos; *strBlueprint != ' ' && *strBlueprint != '\n' && *strBlueprint != NULL; *strPos = *strPos + 1) {
        *strMod = *strBlueprint;
        if (writeType == Country) toupper(*strMod);
        strBlueprint++; strMod++;
    }
}

//--Запись текущего элемента строки objects.txt в текущий объект--
//strInput          - Строка, из которой идёт запись
//objectOutput      - Объект, в который идёт запись
//writeType         - Поле объекта, в которое идёт запись
void writeObject(char *strInput, objectType *objectOutput, IOType writeType) {
    if (writeType == Country)
        strcpy(objectOutput->Country, strInput);
    else if (writeType == ObjectName)
        strcpy(objectOutput->ObjectName, strInput);
    else if (writeType == Square)
        objectOutput->Square = atoi(strInput);
    else printf("Error 1. Invalid parameters");
}

//--Чтение objects.txt и запись в массив objectType--
//Возвращает указатель на первый элемент массива объектов
objectType *rwObjects() {
    FILE *input = fopen("objects.txt", "r");
    char objectQttStr[4], objectStr[38];
    fgets(objectQttStr, 4, input);
    objectType *objectList = (objectType *)malloc(atoi(objectQttStr)), *currentObject = objectList;
    currentObject = (objectType *)malloc(atoi(objectQttStr));
    for (int i = 0; i < atoi(objectQttStr); i++) {
        fgets(objectStr, 38, input);
        IOType inputType = Country;
        for (int j = 0; objectStr[j] != NULL && objectStr[j] != '\n'; j++) {
            char strBuf[cnOBJECT_NAME_LENGTH];
            memset(&strBuf, 0, sizeof(strBuf));

            copyInputStr(&strBuf, &j, &objectStr[j], inputType);

            writeObject(&strBuf, currentObject, inputType);

            inputType++; 
        }
        currentObject++;
    }
    fclose(input);         //this is where it happens
    return objectList;
}

void main() {
    objectType *objectList = rwObjects();
    printf("");
}

这是一个令人困惑的程序,但我找不到其他方法来符合该死的规则,所以让我们把编码风格放在一边,好吗?

此外,我知道如果它成功运行,则不会发生任何事情 - 这是设计使然。还没完。

编辑:不要担心输入数据的有效性。所有输入数据格式都在任务中明确说明,因此不需要检查。不过,出于好奇,这里是:

对象.txt:

3
USA WelfareArrangement 120
Rus PoiskZemli 30
usa asdfEstate 1

编辑 2:当我停止使用 malloc 时,一切都很好。问题是 - 为什么会出现这样的问题,如果不使用 malloc,我将如何创建一个我需要的确切大小的数组,而不是每次都创建最大大小?

最佳答案

第一个问题:

objectType *objectList = (objectType *)malloc(atoi(objectQttStr)), *currentObject = objectList;
currentObject = (objectType *)malloc(atoi(objectQttStr));

malloc 函数分配给定数量的字节。所以如果你有 5 个对象,你只分配 5 个字节。这对您的结构来说还不够。这会导致您写入超过调用 undefined behavior 的已分配内存的末尾。 .

如果你想让它为特定数量的对象分配空间,你需要乘以对象大小:

objectType *objectList = malloc(sizeof(*objectList)*atoi(objectQttStr));

此外,don't cast the return value of malloc .

您还可以将 currentObject 分配给与 objectList 相同的值,然后使用单独的内存分配覆盖它。所以去掉第二个 malloc

第二个问题:

        memset(&strBuf, 0, sizeof(strBuf));

        copyInputStr(&strBuf, &j, &objectStr[j], inputType);

        writeObject(&strBuf, currentObject, inputType);

您的copyInputStrwriteObject 函数需要一个char *,但是您传入了strBuf 的地址类型为 char (*)[30] 的数组。在这里去掉地址运算符:

        memset(strBuf, 0, sizeof(strBuf));

        copyInputStr(strBuf, &j, &objectStr[j], inputType);

        writeObject(strBuf, currentObject, inputType);

第三个问题:

void copyInputStr(char *strMod, int *strPos, char *strBlueprint, IOType writeType) {
    for (*strPos; *strBlueprint != ' ' && *strBlueprint != '\n' && *strBlueprint != NULL; *strPos = *strPos + 1) {
        *strMod = *strBlueprint;
        if (writeType == Country) toupper(*strMod);
        strBlueprint++; strMod++;
    }
}

当您复制 strMod 中的字符时,您不会在末尾添加空字节。 C 中的字符串是一个以 null 结尾的字符数组,因此您最终得到的不是字符串,而只是一个字符数组。当您稍后对该数组调用 strcpy 时,该函数找不到空字节,因此它会一直读取直到找到为止。这会导致函数读取未初始化的字节和/或读取超过数组末尾的内容,这将再次调用未定义的行为。

在循环后添加空终止字节。此外,toupper 函数的结果未分配给任何内容,因此它什么也不做。您需要将其分配回 *strMod:

void copyInputStr(char *strMod, int *strPos, char *strBlueprint, IOType writeType) {
    for (*strPos; *strBlueprint != ' ' && *strBlueprint != '\n' && *strBlueprint != NULL; *strPos = *strPos + 1) {
        *strMod = *strBlueprint;
        if (writeType == Country) *strMod = toupper(*strMod);
        strBlueprint++; strMod++;
    }
    *strMod = 0;
}

关于C - fclose() 触发断点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47893005/

相关文章:

c - 为使用 C 和 Haskell 的项目构建系统

c - 当 scanf(fscanf) 遇到 EOF 时,参数变量中存储的值是多少?

c - malloc 上的 Valgrind 内存泄漏

c - 从文件 C 中读取并跳转行

c - 使用 fscanf() 时不理解 C 格式说明符

更改 argp 上的默认输出流

c - 为什么单步执行代码时 printf 输出没有立即出现?

gdb - 在 GDB 中设置断点

python - 忽略前 N 个命中的 pycharm 断点

c - 快速 'C' 库可透明地管理非常大的文件