почему я могу ввести только tickcket_code и last name дальше программа просто завершается и также не работает функция delete Travelers?
#include <iostream>
#include <string>
using namespace std;
const int maxSize = 100;
struct Travelers
{
int ticket_code;
char lastname[maxSize];
int numberofthings;
int luggage_weight;
Travelers* Next;
};
Travelers* Head;
void TravelersInput(int& ticket_code,char lastname[maxSize], int& numbeofthings,int& luggage_weight)
{
cout << "Enter tour ticket code" << endl;;
cin >> ticket_code;
cout << "Enter your Lastname:" << endl;
cin.get();
cin.get(lastname, sizeof(lastname[maxSize]));
cout << "Enter number of things:" << endl;
cin >> numbeofthings;
cout << "Enter your luggage weight:"<<endl;
cin >> luggage_weight;
cout << endl;
}
void PrintTravelers(Travelers* Head)
{
Travelers* j = Head;
while (j != NULL)
{
cout << "Travelers: " << j->ticket_code<< ";" << j->lastname<< ";" << j->numberofthings<< ";" << j->luggage_weight<< ";";
j = j->Next;
}
cout << endl;
}
Travelers* TravelersSearch(Travelers* Head, int numberofthings)
{
Travelers* p = Head;
while (p != NULL)
{
if (p->numberofthings == numberofthings)
break;
p = p->Next;
}
return p;
}
void DeleteTravelers(Travelers*& pbeg, Travelers* pos)
{
if (pbeg == NULL || pos == NULL)
return;
if (pos == pbeg)
{
pbeg = pbeg->Next;
delete pos;
}
else
{
Travelers* prev = pbeg;
while (prev != NULL && prev->Next != pos)
prev = prev->Next;
if (prev != NULL)
{
prev->Next = pos->Next;
delete pos;
}
}
}
void FreeTravelers(Travelers*& pbeg)
{
Travelers* p;
while (pbeg != NULL)
{
p = pbeg;
pbeg = pbeg->Next;
delete p;
}
}
int main()
{
int ticket_code;
char lastname[maxSize]= { '\0' };
int numberofthings;
int luggage_weight;
Travelers* Head = nullptr;
int n;
cout << "Number of Travelers = ";
cin >> n;
for (int i = 0; i < n; i++)
{
TravelersInput(ticket_code, lastname, numberofthings, luggage_weight);
}
cout << " deleted Travelers = ";
cin >> luggage_weight;
DeleteTravelers(Head, TravelersSearch(Head, luggage_weight));
PrintTravelers(Head);
FreeTravelers(Head);
system("pause");
return 0;
}
Ответы (1 шт):
Давайте посмотрим детальнее на это выражение
cin.get(lastname, sizeof(lastname[maxSize]));
Нас тут интересует два момента - значение sizeof(lastname[maxSize]) и что вообще происходит. Главное, что нужно знать о sizeof - это то, что выражение не вычисляется, а только оценивается его тип, что бы знать размер и это делается на момент компиляции. В данном случае тип - это элемент массива, это char. значит все выражение равно 1.
По поводу UB - я нашел вот такое обсуждение - Does not evaluating the expression to which sizeof is applied make it legal to dereference a null or invalid pointer inside sizeof in C++? и похоже, что там как раз и сходятся к тому, что не, это не UB.
А теперь, а что же происходит? Теперь, когда мы немного разобрались с деталями, выражение можно переписать вот так
cin.get(lastname, 1);
и все очевидно - прочитать нужно... 0 символов. Почему? потому что count-1. И тут наступает проблема - в буфере то остаются еще символы(как минимум то вводился один символ (фамилии обычно меньше двух не бывает, но вряд ли кто то просто жал перевод строки). И в результате с буфера ничего не читается. И что же будет с этим буфером? а его попробуют читать последующие операции чтения. А там у нас
cin >> numbeofthings;
попытка прочитать число. А так как "фамилия" (lastname) обычно не содержит цифры вначале, то поток переходит в состояние "ай-ай-ай" (выставляется failbit) и отказывается дальше читать (то есть, все операции чтения игнорируются!).
Это выглядит красиво, но это не так. Оказывается, failbit выставляется чуточку раньше. В документации есть маленькая приписка
If no characters were extracted, calls setstate(failbit)
а у нас должно прочитаться не более 0 символов. То есть, попытка вызвать get(char_array,1) будет приводить к установке failbit...
Но почему возникла такая странная строка? скорее всего хотелось написать такое
cin.get(lastname, sizeof(lastname));
но дрогнула рука, скопировался параметр... и все скомпилировалось...
В целом, организация корректного ввода - не легкая задача, а организация ввода, понятного пользователю - ещё сложнее. Поэтому, я бы не заморачивался особо. Но немного можно сделать.
Но это только вершина айсберга. Дальше веселее. Посмотрим на TravelersInput. Она принимает по ссылке переменные, в которые с разной долей успеха что то вводит, а потом.... А потом ничего не делает. Она не добавляет новую структуру в список. Она просто выбрасывает значения. Список Head продолжает указывать на nullptr... Забавно
Теперь чуточку очевиднее - нужно эти данные все таки куда то добавить, плюс организовать корректный ввод. Я предлагаю так переделать функцию ввода
bool TravelersInput(Travelers* t)
{
cout << "Enter tour ticket code" << endl;;
cin >> t->ticket_codeticket_code;
cout << "Enter your Lastname:" << endl;
cin.get();
cin.get(t->lastname, sizeof(t->lastname));
cout << "Enter number of things:" << endl;
cin >> t->ticket_codenumbeofthings;
cout << "Enter your luggage weight:"<<endl;
cin >> t->luggage_weight;
cout << endl;
return cin.good();
}
Она теперь получает указатель на структуру и заполняет её данными. И самое главное, в конце возвращает признак успешности ввода.
И теперь ввод будет выглядеть где то так
for (int i = 0; i < n; i++)
{
Travelers* t = new Travelers;
if (!TravelersInput(t)) {
// почистим память. Вроде я все правильно написал.
delete t;
FreeTravelers(Head);
// выйдем
return 1;
}
// а тут нужен код добавления этой структуры в список
// напишите сами:)
}