【问题标题】:Dynamic array prints garbage after adding elements添加元素后动态数组打印垃圾
【发布时间】:2017-03-14 01:19:10
【问题描述】:

我已经查看我的代码几个小时了,但我无法弄清楚为什么它不起作用。该程序使用动态数组创建购物清单。我可以添加新元素就好了。我还可以保存和读取文件中的元素。我的问题是,每当我从文件中读取然后添加一个新元素时,我的数组就会变得混乱,并且我的打印函数会打印垃圾。它应该做的是,每当我加载文件或添加新元素时,它应该始终将其添加到列表的末尾。这是我的代码:

typedef struct ShoppingList {
    int id;
    char name[100];
    int amount;
    char unit[10];
}ShoppingList;

void PrintShoppingList( ShoppingList *list, int *itemsLoaded )
{
    if ( *itemsLoaded == 0 ) {
        printf( "Inköpslistan är tom.\n" );
        return;
    }
    for ( int i = 0; i<*itemsLoaded; i++ ) {
        printf( "%d\t %s\t\t %d\t %s\n", list[i].id, list[i].name, list[i].amount, list[i].unit );
    }
}

void AddNewItem( ShoppingList *list, int *itemsLoaded ) {
    ShoppingList *temp;
    printf( "Namn på vara: " );
    scanf_s( "%s", list[*itemsLoaded].name, sizeof( list[*itemsLoaded].name ) );
    printf( "Antal: " );
    while ( scanf_s( "%d", &list[*itemsLoaded].amount ) != 1 ) {
        scanf_s( "%*s" );
        printf( "Antal: " );
    }
    printf( "Enhet: " );
    scanf_s( "%s", list[*itemsLoaded].unit, sizeof( list[*itemsLoaded].unit ) );
    list[*itemsLoaded].id = *itemsLoaded;
    temp = (ShoppingList*)realloc( list, ( *itemsLoaded + 2 ) * sizeof( ShoppingList ) );
    if ( temp != NULL ) {
        list = temp;
        *itemsLoaded = *itemsLoaded + 1;
        printf( "Vara lades till.\n" );
    } else {
        //free( list );
        printf( "Kunde inte allokera minne.\n" );
    }
}

void SaveShoppingListToFile( ShoppingList *list, int *itemsLoaded ) {
    if ( *itemsLoaded == 0 ) {
        printf( "Inköpslistan är tom. Kan ej spara.\n" );
        return;
    }
    char filename[20];
    int i;
    printf( "Spara fil som: " );
    scanf_s( "%s", filename, sizeof(filename) );
    FILE *fp;
    fopen_s( &fp, filename, "w" );
    if ( fp )
    {
        fprintf( fp, "%d", *itemsLoaded );
        for ( i = 0; i<*itemsLoaded; i++ ) {
            fprintf( fp, "\n%s\n%d\n%s", list[i].name, list[i].amount, list[i].unit );
        }
        fclose( fp );
        printf( "Fil sparad.\n" );
    } else {
        printf( "Kunde ej spara filen.\n" );
    }
}

void LoadShoppingListFromFile( ShoppingList *list, int *itemsLoaded ) {
    char filename[20];
    int nEntries = 0;
    ShoppingList *temp;
    printf( "Läs in fil: " );
    scanf_s( "%s", filename, sizeof( filename ) );
    FILE *fp;
    fopen_s( &fp, filename, "r" );
    if ( fp )
    {
        fscanf_s( fp, "%d", &nEntries );
        for (int i = 0; i<nEntries; i++ ) {
            fscanf_s(fp, "%s", list[*itemsLoaded].name, sizeof( list[*itemsLoaded].name ) );
            fscanf_s(fp, "%d", &list[*itemsLoaded].amount );
            fscanf_s(fp, "%s", list[*itemsLoaded].unit, sizeof( list[*itemsLoaded].unit ) );
            list[*itemsLoaded].id = *itemsLoaded;
            temp = (ShoppingList*)realloc( list, ( *itemsLoaded + 2 ) * sizeof( ShoppingList ) );
            if ( temp != NULL ) {
                list = temp;
                *itemsLoaded = *itemsLoaded + 1;
            } else {
                //free( list );
                printf( "Kunde inte allokera minne.\n" );
            }
        }
        fclose( fp );
        printf( "Fil inläst.\n" );
    } else {
        printf( "Kunde ej läsa filen.\n" );
    }
}

void Menu() {
    int menu = 0, *itemsLoaded, index = 0;
    itemsLoaded = &index;
    ShoppingList *list = NULL;
    list = (ShoppingList*)malloc( sizeof( ShoppingList ) );
    if ( list != NULL ) {
        do
        {
            system( "CLS" );
            menu = 0;
            printf( "%d\n", *itemsLoaded );
            printf( "Meny\n 1 - Lägg till en vara till inköpslistan\n 2 - Skriv ut inköpslistan\n 3 - Skriv inköpslistan till fil\n 4 - Läs in inköpslista från fil\n 5 - Ändra vara\n 6 - Ta bort vara\n 7 - Avsluta\nAnge Val: " );
            while ( scanf_s( "%d", &menu, sizeof( int ) ) != 1 ) {
                scanf_s( "%*s" );
                printf( "\nFelaktigt val. Försök igen.\n" );
                printf( "Ange Val: " );
            }
            if ( menu < 7 ) {
                switch ( menu ) {
                    case 1:
                        AddNewItem( list, itemsLoaded );
                        PrintShoppingList( list, itemsLoaded );
                        break;
                    case 2:
                        PrintShoppingList( list, itemsLoaded );
                        break;
                    case 3:
                        SaveShoppingListToFile( list, itemsLoaded );
                        break;
                    case 4:
                        LoadShoppingListFromFile( list, itemsLoaded );
                        PrintShoppingList( list, itemsLoaded );
                        break;
                }
            } else if ( menu > 7 || menu < 1 ) {
                printf( "Felaktigt val. Försök igen." );
            }
            system( "pause" );
        } while ( menu != 7 );
    } else {
        printf( "Kunde inte allokera minne." );
    }
    free( list );
}

int main() {
    Menu(); 
    return 0;
}

【问题讨论】:

  • 尝试使用你的调试器!许多现代 IDE 允许您逐行运行代码,您将看到错误发生的确切位置。如果您仍然不知道问题出在哪里,请缩小您的代码并仅发布相关部分。没有人愿意阅读整整 200 行代码。
  • 出于风格和易用性的考虑,我发现在每个函数的开头使用numItems = *itemsLoaded 并在返回之前使用*itemsLoaded = numItems 会更好。这有助于处理。在输出列表之前还要打印numItems,以确保正确更新它。
  • 我已经使用了我的调试器,它在我的系统之后("pause");数组中断的菜单中的语句。每次我添加一个项目或从文件中读取时,我都会打印列表,这很好,但是当它超过系统暂停时它会中断,当我尝试添加或读取它时崩溃并打印我得到垃圾。
  • 当您使用 realloc'd 指针执行 list = temp; 时,只会影响函数本地的 list 参数。它不会改变调用函数中的指针。

标签: c arrays dynamic malloc realloc


【解决方案1】:

函数void AddNewItem( ShoppingList *list, int *itemsLoaded ) { 在数组已满时重新分配list,但指向新分配数组的指针从未传回给仍使用先前值的调用者。这会调用未定义的行为。

您应该传递指针的地址,以便函数可以更新调用者的值。

int AddNewItem(ShoppingList **listp, int *itemsLoaded) {
    ShoppingList *list = *listp;
    printf( "Namn på vara: " );
    scanf_s( "%s", list[*itemsLoaded].name, sizeof( list[*itemsLoaded].name ) );
    printf( "Antal: " );
    while ( scanf_s( "%d", &list[*itemsLoaded].amount ) != 1 ) {
        scanf_s( "%*s" );
        printf( "Antal: " );
    }
    printf( "Enhet: " );
    scanf_s( "%s", list[*itemsLoaded].unit, sizeof( list[*itemsLoaded].unit ) );
    list[*itemsLoaded].id = *itemsLoaded;
    list = (ShoppingList*)realloc( list, ( *itemsLoaded + 2 ) * sizeof( ShoppingList ) );
    if ( list != NULL ) {
        *listp = list;
        *itemsLoaded = *itemsLoaded + 1;
        printf( "Vara lades till.\n" );
        return 1; // success
    } else {
        //free( list );
        printf( "Kunde inte allokera minne.\n" );
        return 0; // failure
    }
}

main()调用这个函数为AddNewItem( &amp;list, itemsLoaded );

请注意,解析额外项目之前重新分配数组实际上会更简单。对于这种方法,初始指针可能是NULL

函数LoadShoppingListFromFile也有同样的问题。

【讨论】:

  • 这行得通,除非我需要更改 ShoppingList **list = *listp;到购物清单 *list = *listp;非常感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 2015-09-21
  • 2019-11-26
  • 2015-06-28
  • 1970-01-01
  • 2021-04-08
  • 2020-01-13
  • 2022-01-03
相关资源
最近更新 更多