2016-03-13 3 views
1

Я хочу, чтобы динамически выделять для массива структуры «student'`динамического выделения памяти для массива структур

struct student 
    { 
    char* name; 
}; 
int main(int argc,char* argv[]) 
{ 
    if(argc==2) 
    { 
    student* sptr=new student[4]; 
    for(int i=0;i<atoi(argv[1]);i++) 
    { 
     cin>>*(*(sptr+i)).name; 
    } 
    for(int i=0;i<atoi(argv[1]);i++) 
    { 
     cout<<*(*(sptr+i)).name; 
    } 
    } 
} 

код компиляция без ошибок, но во время выполнения, после ввода первого имени является говорит" ошибка сегментации (ядро сбрасывается) »и останавливается. Где я ошибаюсь?

+3

Вы не выделили память для 'name'. Используйте 'std :: string' вместо' char * '. –

+0

Просто потому, что у вас есть указатель, это не значит, что вы должны использовать арифметику указателей и разыменовывать. Для любого массива * или указателя * 'p' и index' i' выражение '* (p + i)' эквивалентно 'p [i]'. Поэтому вместо того, чтобы делать '* (sptr + i)' в вашем цикле, просто сделайте 'sptr [i]'. –

+1

Кроме того, вы должны читать и печатать только один символ *? Потому что это то, что вы будете делать, когда вы разыщите (*** uninitialized! ***) указатель 'name' на входе/выходе. –

ответ

1
  1. Вам не нужно держать-структуру только одной строки
  2. Вам не нужно вызывать atoi в каждой итерации
  3. Вам необходимо выделить память для того, чтобы получить пользовательский ввод

рабочий код может быть следующим:

int main(int argc,char* argv[]) 
{ 
    // Exit if wrong number of arguments 
    if(argc != 2) 
    { 
     cout << "Incorrect number of argements"; 
     return 1; 
    } 

    // Convert argument from ASCII to int once 
    int numberOfValues = atoi(argv[1]); 
    // Create array of strings (pointer of pointers to characters) 
    char** ppValues = new char*[numberOfValues]; 

    // Loop to collect strings 
    for (int i = 0; i < numberOfValues; i++) 
    { 
     // Local variable to collect one string 
     char buffer[1024]; 
     cin >> buffer; 
     // Allocate memory in one of the array entries to hold the current entry 
     ppValues[i] = new char[strlen(buffer) + 1]; 
     // Copy value from temp string to final string location 
     strcpy(ppValues[i], buffer); 
    } 

    for (int i = 0; i < numberOfValues; i++) 
    { 
     cout << ppValues[i]; 
    } 

    return 0; 
} 
1

вы получаете ошибку сегм, потому что вы пытаетесь для доступа к неинициализированной памяти (указатель name никогда не инициализируется), как указано πάντα ῥεῖ и Joachim Pileborg.

Использование std :: string - путь; однако, если это упражнение или по какой-либо причине вы должны использовать char *, тогда его необходимо инициализировать для каждого элемента массива отдельно в цикле.

student* sptr=new student[4]; 
for(int i=0;i<atoi(argv[1]);i++) 
{ 
    sptr[i].name = new char[10]; // now name is properly initialized and can hold a string of length (10 - 1) 
    cin >> sptr[i].name; 
} 

И при печати из строки, вы должны делать

cout << sptr[i].name;

Это то же самое, как (*(sptr+i)).name. Это дает char *, который содержит строку, если вы добавите еще одно значение * перед выражением, чем оно разыскивает указатель и дает вам первый символ строки. Поэтому, если вы должны были печатать только первый символ, то ваше выражение прекрасно. Однако, чтобы распечатать всю строку, вы не должны разыгрывать указатель, добавив еще один * в начало.

0

Ошибка сегмента - вы получаете доступ к ячейке памяти без ее инициализации. Вам не нужно определять структуру. Поскольку это CPP, так почему бы не попробовать тип данных CPP std::string.

И атрибут name в student struct никогда не выделяет память в вашем коде.

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

string * st = new string[atoi(argv[1])]; 
for (int i = 0; i < atoi(argv[1]); i ++) 
{ 
    cin >> st[i]; 
    cout << st[i] << endl; 
} 

или

std::vector<std::string> name; 
0

Если вам нужно хранить имена из входных данных, вам просто необходимо использовать структуру данных как вектор или список строк, чтобы избежать ошибок, как менеджер памяти :

std::vector<std::string> names; 
// std::list<std::string> names; 
1

Вы получаете сегментацию, потому что ваша программа имеет неопределенное поведение. В вашем коде есть ошибка, которую ваш компилятор не должен диагностировать; он вместо этого генерирует двоичный файл, который может делать все, включая случайные сбои.

В зависимости от пользовательского ввода, ваши проблемы, начиная с использованием 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"; 
    } 
} 

Эта программа определенное поведение; он не будет сбой с неправильным вводом, и он позволит именам с пробелами в них. Он также легче читать, не использует никаких указателей снаружи, и он обрабатывает память автоматически, умнее и быстрее, чем ручной код.

+0

Спасибо за подробное объяснение. –

Смежные вопросы