2015-01-17 3 views
0

Я пытался написать короткую программу, позволяющую пользователю добавлять записи в «базу данных», перечисляя записи, которые они вставляли, и возможность очищать все записи без завершения программа. Вот что я получилСоздание и очистка массива структур

#include <cstdlib> 
#include <iostream> 
#include <string> 
using namespace std; 

struct BIRTH 
    {int month; int year;}; 
struct ID 
    {string name; bool vip; float score; 
     struct BIRTH date;} ; 

int main(int argc, char** argv) { 

    ID **ptrarr; 
    ptrarr = new ID * [10]; 
    for (int r=0; r<10; r++) 
     {ptrarr[r] = new ID[1] ;} 


    int counter = 0; 

    while(counter<100){ 
    cout << "Type add to create a new entry" << endl; 
    cout << "Type list to see all entries" << endl; 
    cout << "Type clear to delete all entries" << endl; 
    cout << "Type exit to terminate" << endl; 

    string command = "0"; 

    getline (cin,command); 

    if(command=="add") 
     { 
     cout << "Enter name" << endl; 
     getline (cin,ptrarr[counter][1].name); 
     cout << "VIP? 1 for yes, 0 for no" << endl; 
     cin >> ptrarr[counter][1].vip; 
     cout << "Enter score" << endl; 
     cin >> ptrarr[counter][1].score; 
     cout << "Month of birth" << endl; 
     cin >> ptrarr[counter][1].date.month; 
     cout << "Year of birth" << endl; 
     cin >> ptrarr[counter][1].date.year; 
     counter++; 

     } 
    else if(command=="list") 
     { 
      for (int i=0; i<counter; i++) 
      {int n=i+1; 
      cout << n << " " 
       << ptrarr[i][1].name << " "; 
        if (ptrarr[i][1].vip) 
        {cout << "VIP ";} 
      cout << "Score: " << ptrarr[i][1].score << "  " 
       << "Born: " << ptrarr[i][1].date.month << "/" << ptrarr[i][1].date.year << endl; 
        } 
    } 
    else if(command=="clear") 
    {delete[] ptrarr; 

     cout << "Entries cleared" << endl;} 
    else if(command=="exit") 
     {return 0;} 
    else 
     cout << "try again" << endl; 
    } 
return 0; 
} 

Теперь вот сделка: следующий код успешно компилируется, но когда я печатаю в команде «добавить», программа падает (достижение разблокировано, не думаю, что можно получить с таким коротким кодом). Самое главное, что массив состоит из многотипной структуры и что команда «clear» уничтожает все записи в массиве.

ПРИМЕЧАНИЕ: Я понимаю, что есть тысячи лучших способов написать этот фрагмент кода, но я пишу его, чтобы практиковать то, что я до сих пор рассматривал в отношении C++. Так что, если это не является абсолютно необходимым для кода для запуска, пожалуйста, не вводить никаких новых уловок =)

ответ

0

Вы создаете массив указателей, каждый из которых указывает на один элемент:

ptrarr[r] = new ID[1] ; 

Максимальный показатель, который можно использовать с ptrarr[r] является 0. Поскольку вы используете ptrarr[counter][1], вы получаете доступ к памяти, которая выходит за пределы. Это приводит к неопределенному поведению. Сбой - это одно такое неопределенное поведение.

Другие проблемы с кодом, которые вы можете исправить.

Больше из ограничивающей доступ к памяти

Вы используете:

int counter = 0; 
while(counter<100){ 

... 

getline (cin,ptrarr[counter][1].name); 

Это снова будет приводить к непредсказуемому поведению, если counter > 10, так как вы выделили только 10 указатели на ptrarr.

Удаление содержимого

Вы используете:

else if(command=="clear") 
    { 
    delete[] ptrarr; 

    cout << "Entries cleared" << endl; 
    } 

Есть несколько проблем с этим:

  1. У вас есть утечка памяти. Вы никогда не звоните delete [] на что ptrarr[0] - ptrarr[9] указывают на. Вы должны будете использовать:

    else if(command=="clear") 
    { 
        for (int i = 0; i < 10; ++i) 
        { 
         delete [] ptrarr[i]; 
        } 
    
        delete[] ptrarr; 
    
        cout << "Entries cleared" << endl; 
    } 
    

    Помните, что каждое распределение должно иметь соответствующее освобождение. В противном случае вы будете утечки памяти.

  2. Как только вы позвоните delete [] ptrarr;, это указывает на оборванную память. Я не вижу никакого кода, который перераспределяет память для ptrarr, пока вы продолжаете ее использовать.

Вам необходимо перераспределить память и сброса counter в 0, когда пользователь выбирает «ясно».

Мое предложение

Вы не делаете два уровня указателей. Вам просто нужно что-то вроде:

int const MAX_ITEMS = 100; 
ID* IDarr = new ID[MAX_ITEMS]; 

Вместо ptrarr[counter][1] используйте IDarr[counter].

Использовать MAX_ITEMS в выражении оператора while вместо магического номера 100.

int counter = 0; 
while(counter<MAX_ITEMS){ 

При обработке «ясно» вам не нужно снимать или выделять память. Просто сбросьте counter.

else if(command=="clear") 
    { 
    counter = 0; 
    cout << "Entries cleared" << endl; 
    } 

Убедитесь, что освободить память до возвращения из main.

Вот полный main функция с изменениями:

int main(int argc, char** argv) { 

    const int MAX_ITEMS = 100; 
    ID* IDarr = new ID[MAX_ITEMS]; 

    int counter = 0; 
    while(counter < MAX_ITEMS){ 
     cout << "Type add to create a new entry" << endl; 
     cout << "Type list to see all entries" << endl; 
     cout << "Type clear to delete all entries" << endl; 
     cout << "Type exit to terminate" << endl; 

     string command = "0"; 

     getline (cin,command); 

     if(command=="add") 
     { 
     cout << "Enter name" << endl; 
     getline (cin, IDarr[counter].name); 
     cout << "VIP? 1 for yes, 0 for no" << endl; 
     cin >> IDarr[counter].vip; 
     cout << "Enter score" << endl; 
     cin >> IDarr[counter].score; 
     cout << "Month of birth" << endl; 
     cin >> IDarr[counter].date.month; 
     cout << "Year of birth" << endl; 
     cin >> IDarr[counter].date.year; 
     counter++; 

     } 
     else if(command=="list") 
     { 
     for (int i=0; i<counter; i++) 
     { 
      int n=i+1; 
      cout << n << " " << IDarr[i].name << " "; 
      if (IDarr[i].vip) 
      { 
       cout << "VIP "; 
      } 
      cout 
       << "Score: " << IDarr[i].score << "  " 
       << "Born: " << IDarr[i].date.month << "/" << IDarr[i].date.year << endl; 
     } 
     } 
     else if(command=="clear") 
     { 
     counter = 0; 

     cout << "Entries cleared" << endl; 
     } 
     else if(command=="exit") 
     { 
     // Don't use return 0; 
     // Just break out of the while loop so that memory 
     // can be deallocated at the end of this function. 
     break; 
     } 
     else 
     cout << "try again" << endl; 
    } 

    delete [] IDarr; 

    return 0; 
} 
+0

Действительно полезно также =) Теперь я знаю, что именно вызвало крах кода. Кроме того, хорошая точка при удалении каждой записи из массива. Моя интуиция говорила мне, что это то, что я должен делать, но я не знал, как это сделать. То, что я сделал сейчас, это то, что опция clear освобождает записи, чем массив в целом, сбрасывает счетчик и затем создает массив еще раз с тем же кодом, что и в начале основного – Quit

0

индексы массивов начинаются с 0.

ptrarr[counter][1] относится ко второму элементу ptrarr[counter]. ptrarr[counter] указывает на массив одного элемента.

2

Заменить все ptrarr[counter][1]ptrarr[counter][0] устраняет проблему.

Дальнейшие советы:

I. Этот код имеет избыточность:

ID **ptrarr; 
ptrarr = new ID * [10]; 
for (int r=0; r<10; r++) 
{ptrarr[r] = new ID[1] ;} 

Замените его:

ID *ptrarr; 
ptrarr = new ID [10]; 

Тогда вам не нужно дополнительное [0] в конец каждого ptrarr [счетчик]

II. функции делают код более читаемым:

if(command=="add") 
    add(); 
else if(command=="list") 
    list(); 
else if(command=="clear") 
    clear(); 
else if(command=="exit") 
    return 0; 
else 
    cout << "try again" << endl; 

Тогда решения принимаются в меньшей площади

III (хорошая практика для больших программ.). Существует еще одна ошибка в коде:

else if(command=="clear") 
{delete[] ptrarr; 

    cout << "Entries cleared" << endl;} 

Здесь вы должны сбросить счетчик. Также если вы считаете мой пункт (I) эта часть в порядке. В противном случае, если вы используете new с циклом for, я боюсь, что вам понадобится delete с петлей for. Простое удаление корня дерева массивов приводит к утечке памяти!

Кроме того, если вы удалили список путем удаления, вам больше не нужно будет хранить данные в списке? Использование delete в связанных списках - хорошая идея, но здесь это не применимо. Просто перезагрузка счетчика делает работу, и она больше не отображает идентификаторы в списке. for внутри списка подсчитывает только счетчик.

Если вы выходите из программы, не освобождаете память?

Я говорю

delete [] ptrarr; 

хорош для быть на выходе.

+0

Привет - Большое спасибо за помощь здесь. Когда дело доходит до сброса счетчика, я знал об этом, не знаю, почему он не сделал это здесь для цитируемого кода. Но я хотел сбросить память в основном для тренировочных целей, первая версия этого кода была только сбросом счетчика, и я чувствовал себя обманом =) – Quit

+0

Это не обман. потому что массив всегда занимает 10 объектов памяти, либо используя их, либо нет. Это выделение памяти позволяет вам добавлять объекты позже. Если вы освободите память, вы больше не сможете добавить какой-либо объект, если вы не выделите еще 10 блоков! поэтому не стоит беспокоиться о реселлере. Это правильная работа. – Arashium

0

попробовать это:

if(command=="add") { 
     cout << "Enter name" << endl; 
     getline (cin,ptrarr[counter][0].name); 
     cout << "VIP? 1 for yes, 0 for no" << endl; 
     cin >> ptrarr[counter][0].vip; 
     cout << "Enter score" << endl; 
     cin >> ptrarr[counter][0].score; 
     cout << "Month of birth" << endl; 
     cin >> ptrarr[counter][0].date.month; 
     cout << "Year of birth" << endl; 
     cin >> ptrarr[counter][0].date.year; 
     counter++; 
    } 
    else if(command=="list") { 
     for (int i=0; i<counter; i++){ 
      int n=i+1; 
      cout << n << " "<< ptrarr[i][0].name << " "; 
      if (ptrarr[i][0].vip){ 
       cout << "VIP "; 
      } 
      cout << "Score: " << ptrarr[i][0].score << "  " 
      << "Born: " << ptrarr[i][0].date.month << "/" << ptrarr[i][0].date.year << endl; 
     } 
    } 

Заключение:

  • Подобно тому, как вы инициализирован counter с 0 вы должны иметь использовали индекс 0 для acc первый элемент;
  • То же самое относится к листингу.
  • Массивы основаны на индексах.
Смежные вопросы