结构数组的动态内存分配

时间:2016-03-13 10:57:36

标签: c++

我想动态分配一组结构' 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;
     }
   }
}

代码编译没有错误,但在运行时,输入名字后是 说&#34;分段错误(核心倾销)&#34;和halts.where我错了?

5 个答案:

答案 0 :(得分:1)

  1. 您不需要结构只能容纳一个字符串
  2. 您无需在每次迭代中调用atoi
  3. 您需要分配内存才能接收用户输入
  4. 工作代码如下:

    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 :(得分:1)

你正在遇到一个seg错误,因为你试图访问未初始化的内存(指针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 *,则它会提供保存字符串的*,而不是取消引用指针,并为您提供字符串的第一个字符。所以,如果你只打印第一个字符,那么你的表达就好了。但是,要打印出整个字符串,您不应通过在开头添加另一个*来取消引用指针。

答案 2 :(得分:1)

您正在进行细分,因为您的程序有未定义的行为。您的代码中存在错误,您的编译器无需诊断;它会生成一个可以执行任何操作的二进制文件,包括随机崩溃。

根据用户输入,您的问题始于在此行中使用atoi

for(int i=0;i<atoi(argv[1]);i++)

首先,您的程序显然只有在用户输入&#34; 4&#34;时才会起作用,这让我想知道为什么您还需要用户输入。您可能希望在上面的行中写下new student[atoi(argv[1]]

然而,atoi是一个危险的功能,几乎不应该使用。正如documentation所说:

  

如果转换后的值超出相应回报的范围   类型,返回值未定义。

这意味着您永远不会知道用户是否输入了无害的数字,例如&#34; 1&#34;或类似&#34; 10000000000000000000000000&#34;,它可能大于您计算机上的最大int值。

可能更糟糕的是:

  

如果无法执行转换,则会返回0

这意味着您永远不会知道用户是否输入了&#34; abc&#34;或&#34; 0&#34;。

std::stoi是一种安全,现代的替代方案,可让您执行错误检查。

不过,我们假设用户只需输入&#34; 4&#34;。

然后我们进入循环体并遇到以下行:

cin>>*(*(sptr+i)).name;

除了可读性问题之外,这里发生的是:

  • i仍为0,因此您获得的(sptr+0)等于(sptr)
  • 取消引用
  • sptr以获取对student对象的引用。
  • 您尝试取消引用name对象的student指针。

后一步最终导致未定义的行为,因为name指针未初始化。你不能这样做。此时程序的所有其余部分都已无效。

使用裸指针实现这一点非常困难。您可以添加用户输入,询问每个名称的大小,以便在读取名称之前分配足够的内存。或者您可以使用low-level member functions of std::istream采用更复杂的方法。

幸运的是,你不必做任何这些。这是C ++:使用std::stringstd::vectorstd::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";
    }
}

该程序定义了行为;它不会因错误输入而崩溃,并且它将允许带有空格的名称。它也更容易阅读,不使用外部的任何指针,它比手动代码更自动,更智能,更快地处理内存。

答案 3 :(得分:0)

段故障是您在不初始化的情况下访问内存位置。您无需定义结构。由于它是CPP,因此为什么不尝试CPP std::string数据类型。

name结构中的student属性永远不会在代码中分配内存。

尝试:

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;

答案 4 :(得分:0)

如果你需要存储来自输入的名字,你只需要使用像向量或字符串列表这样的数据结构来避免内存管理等错误:

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