库存计划,可能边界值问题?

问题描述 投票:0回答:1

下面的程序似乎在大多数情况下很好地工作,但如果我添加一条记录的最后库存位置(记录10),它会导致问题。特别是,如果我添加记录10然后尝试并删除它,它仍然显示在清单我打电话的printList后()。这仅仅是最后的记录的情况下,和任何其他人的情况不会发生。

任何人都可以尝试解决问题是什么?我一直在使用-Wall使用gcc来编译的时候,它不会发出任何警告。我也一直在试图找出如何使用gdb的,但我还在学习,所以尚未有帮助的任何。

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

typedef struct {
  unsigned int record;
  char tool[30];
  unsigned int quantity;
  double price;
} hardware;

void menu(FILE *fPtr, hardware *toolsPtr);
void initialiseRecords(FILE * fPtr, hardware *toolsPtr);
void inputTool(FILE * fPtr, hardware *toolsPtr);
void printList(FILE * fPtr, hardware *toolsPtr);
void deleteRecord(FILE * fPtr, hardware *toolsPtr);

int main(void)
{
  
  FILE *fPtr;
  hardware tools = { 0, "", 0, 0.0 }, *toolsPtr;
  toolsPtr = &tools;

  if ((fPtr = fopen("hardware.dat", "wb+")) == NULL) {
    puts("File cannot be opened.") ;
  }
  else {
    menu(fPtr, toolsPtr);
  }

  fclose(fPtr);
}

void menu(FILE *fPtr, hardware *toolsPtr)
{
  unsigned int choice;
  
  printf("\n%s\n\n%s\n\n%s\n%s\n%s\n%s\n%s\n\n%s",
         "** Hardware Inventory Program **", 
         "    Enter a menu option:",
         "1 - Initialise the record file.",
         "2 - Add a record to the file.",
         "3 - Delete a record from the file.",
         "4 - Print the current inventory",
         "5 - Quit the program.", "> ");

  scanf("%u", &choice);

  while (choice != 5) {

    switch (choice) {
    case 1:
      initialiseRecords(fPtr, toolsPtr);
      break;
    case 2:
      inputTool(fPtr, toolsPtr);
      break;
    case 3:
      deleteRecord(fPtr, toolsPtr);
      break;
    case 4:
      printList(fPtr, toolsPtr);
      break;
    default:
      puts("Incorrect entry.");
      break;
    }

    printf("\n%s\n\n%s\n\n%s\n%s\n%s\n%s\n%s\n\n%s",
           "** Hardware Inventory Program **", 
           "    Enter a menu option:",
           "1 - Initialise the record file.",
           "2 - Add a record to the file.",
           "3 - Delete a record from the file.",
           "4 - Print the current inventory",
           "5 - Quit the program.", "> ");

    scanf("%u", &choice);
  }
}

void initialiseRecords(FILE * fPtr, hardware *toolsPtr)
{
  fseek(fPtr, 0, SEEK_SET);

  for (unsigned int i = 0; i < 10; ++i) {

    char s[30] = "";
    sscanf(s, "%s", toolsPtr->tool);

    toolsPtr->record = i + 1;
    toolsPtr->quantity = 0;
    toolsPtr->price = 0.0;

    fwrite(toolsPtr, sizeof(hardware), 1, fPtr);
  }
}

void inputTool(FILE * fPtr, hardware *toolsPtr)
{
  printf("\n%s\n\n%s", "Enter record # (-1 to quit):", "> ");
  scanf("%u", &toolsPtr->record);

  while(toolsPtr->record != -1) {

    fseek(fPtr, (toolsPtr->record - 1) * sizeof(hardware), SEEK_SET);
    fread(toolsPtr, sizeof(hardware), 1, fPtr);
    getchar();

    if (!strcmp(toolsPtr->tool, "")) {

      printf("\n%s\n\n%s", "Enter tool name:", "> ");
      fgets(toolsPtr->tool, 30, stdin);

      toolsPtr->tool[strlen(toolsPtr->tool) - 1] = '\0';

      printf("\n%s\n\n%s", "Enter quantity:", "> ");
      scanf("%u", &toolsPtr->quantity);

      printf("\n%s\n\n%s", "Enter price:", "> ");
      scanf("%lf", &toolsPtr->price);

      fseek(fPtr, (toolsPtr->record - 1) * sizeof(hardware), SEEK_SET);
      fwrite(toolsPtr, sizeof(hardware), 1, fPtr);
    }
    else {
      getchar();
      puts("There is an existing record with this number.");
      fseek(fPtr, 0, SEEK_SET);
    }
    printf("\n%s\n\n%s", "Enter record (-1 to quit):", "> ");
    scanf("%u", &toolsPtr->record);
  }
}

void printList(FILE * fPtr, hardware *toolsPtr)
{
  fseek(fPtr, 0, SEEK_SET);
  printf("\n%-10s%-30s%-10s%-10s\n\n", "Record #", 
             "Tool Name", "Quantity", "Price");

  for (unsigned int i = 0; i < 10; ++i) {

    fread(toolsPtr, sizeof(hardware), 1, fPtr);
    printf("%-10u%-30s%-10u$%-10.2lf\n",
           toolsPtr->record, toolsPtr->tool, 
           toolsPtr->quantity, toolsPtr->price);
  }
}

void deleteRecord(FILE * fPtr, hardware *toolsPtr)
{
  printf("\n%s\n\n%s", 
             "Enter the record number of the tool to delete:", "> ");
  scanf("%u", &toolsPtr->record);

  fseek(fPtr, (toolsPtr->record - 1) * sizeof(hardware), SEEK_SET);

  fwrite(toolsPtr, sizeof(hardware), 1, fPtr);
}
c struct inventory boundary off-by-one
1个回答
0
投票

基本上,使用stdio.h中的功能,你不能真正删除任何东西。甚至有不是ftruncate(虽然我确实有unistd.h中这样的功能)。因此,这种类型的数据库的工作时,你真的应该这样做:

一个“删除”字段添加到每个记录。用它来确定哪些记录应显示。

一个“id”字段添加到每个记录。这样,同样的记录总是有相同的密钥。

当您添加一条记录,寻找一个删除一个重用,当你使用一个新的“身份证”。

然后,你永远不必担心截断文件。

要么

如果你有文件截断到指定长度的方式,你可以简单地加载的最后一个记录(如果它不是要删除的一个),并将其移动到你希望删除或转移他们都回来了一个位置1个记录中保持秩序。然后你就可以截断该文件所需的大小。

此外,您使用的模式“WB +”,这是错误的,因为它应该打开它时截断文件(落空)。你真正想要的是什么模式“R + B”,它打开它进行读写,但不会截断。它没有透露是否创建该文件,如果它不存在,所以你可能需要检查和使用“W + B”,只有当它不存在。

另外,在initializeRecords,你有一个线路个char [30] = “”。我不知道如果真的是允许的,但它可以做的最多的是,也许初始化第一个字符(S [0] =“\ 0”)。什么会更好(更容易)是只初始化toolsPtr是这样的:

fseek(fPtr, 0, SEEK_SET);
for (unsigned int i = 0; i < 10; ++i) {
    memset(toolsPtr, 0, sizeof(hardware));
    toolsPtr->record = i + 1;
    fwrite(toolsPtr, sizeof(hardware), 1, fPtr);
}

唯一真正的区别可能是,该文件将使用零填充的,而不是从内存中随机的垃圾。它倾向于有所作为,如果你开始使用的文件hexdump都看到发生了什么,如果出现错误,至少是..

© www.soinside.com 2019 - 2024. All rights reserved.