Вы получаете сегментацию, потому что ваша программа имеет неопределенное поведение. В вашем коде есть ошибка, которую ваш компилятор не должен диагностировать; он вместо этого генерирует двоичный файл, который может делать все, включая случайные сбои.
В зависимости от пользовательского ввода, ваши проблемы, начиная с использованием atoi
в этой строке:
for(int i=0;i<atoi(argv[1]);i++)
Прежде всего, ваша программа может явно работать только тогда, когда пользователь вводит «4» , что заставляет меня задаться вопросом, зачем вам в любом случае вводить пользовательский ввод. Скорее всего, вы хотели написать new student[atoi(argv[1]]
в строке выше.
Тем не менее, atoi
является опасной функцией и почти никогда не должен использоваться. Как documentation говорит:
Если преобразованное значение выпадает из диапазона соответствующего возврата типа, возвращаемое значение не определено.
Это означает, что вы никогда не будете знать, если пользователь ввел безвредные номер, как «1» или что-то вроде «10000000000000000000000000», который, вероятно, больше, чем максимальное значение int
на вашей машине.
Что, возможно, хуже:
Если преобразование не может быть выполнено, 0
возвращается.
Это означает, что вы никогда не узнаете, введен ли пользователь «abc» или «0».
std::stoi
- это безопасная, современная альтернатива, которая позволяет выполнять проверку ошибок.
Тем не менее, предположим, что пользователь просто вводит «4».
Затем войти в тело цикла и сталкиваются следующая строка:
cin>>*(*(sptr+i)).name;
вопросы читаемости в стороне, что происходит здесь:
i
еще 0, так что вы получите (sptr+0)
, что равно (sptr)
.
sptr
разыменовывается, чтобы получить ссылку на объект student
.
- Вы пытаетесь разыменовать указатель
name
объекта student
.
Последний шаг в конечном итоге приводит к неопределенному поведению, так как name
указатель был не инициализирован. Вы не должны этого делать. На этом этапе вся остальная часть вашей программы оказалась недействительной.
Получение этого права с помощью простых указателей чрезвычайно сложно. Вы можете добавить пользовательский ввод, запрашивающий размер каждого имени, чтобы вы могли выделить достаточное количество памяти, прежде чем читать имя. Или вы можете использовать еще более сложный подход, используя low-level member functions of std::istream
.
К счастью, вам не нужно ничего делать. Это C++: используйте std::string
, std::vector
и std::getline
. использования C++, а не C, если вы хотите писать на C++, а не C, и все ваши проблемы исчезнут:
#include <iostream>
#include <string>
#include <vector>
#include <exception>
struct student
{
std::string name;
};
int main(int argc, char* argv[])
{
try
{
if (argc == 2)
{
std::vector<student> students(std::stoi(argv[1]));
for (auto&& student : students)
{
std::getline(std::cin, student.name);
}
for (auto&& student : students)
{
std::cout << student.name << "\n";
}
}
}
catch (std::exception const& exc)
{
std::cerr << exc.what() << "\n";
}
}
Эта программа определенное поведение; он не будет сбой с неправильным вводом, и он позволит именам с пробелами в них. Он также легче читать, не использует никаких указателей снаружи, и он обрабатывает память автоматически, умнее и быстрее, чем ручной код.
Вы не выделили память для 'name'. Используйте 'std :: string' вместо' char * '. –
Просто потому, что у вас есть указатель, это не значит, что вы должны использовать арифметику указателей и разыменовывать. Для любого массива * или указателя * 'p' и index' i' выражение '* (p + i)' эквивалентно 'p [i]'. Поэтому вместо того, чтобы делать '* (sptr + i)' в вашем цикле, просто сделайте 'sptr [i]'. –
Кроме того, вы должны читать и печатать только один символ *? Потому что это то, что вы будете делать, когда вы разыщите (*** uninitialized! ***) указатель 'name' на входе/выходе. –