结构数组的动态内存分配

dynamic memory allocation for array of structures

本文关键字:内存 分配 动态 数组 结构      更新时间:2023-10-16

我想为结构数组"学生"动态分配

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;
     }
   }
}

代码编译没有错误,但在运行时,输入名字后是说"分段错误(核心转储)"并停止。我哪里出错了?

  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;
} 

您遇到 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 *。因此,如果您只打印第一个字符,那么您的表达式就可以了。但是,要打印出整个字符串,不应通过在开头添加另一个*来取消引用指针。

您正在获得分段,因为您的程序具有未定义的行为。代码中存在编译器不需要诊断的错误;相反,它会生成一个可以做任何事情的二进制文件,包括随机崩溃。

根据用户输入,您的问题从使用此行中的atoi开始:

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

首先,你的程序显然只有在用户输入"4"时才能工作,这让我想知道为什么你无论如何都需要用户输入。您可能想在上面的行中写new student[atoi(argv[1]]

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

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

这意味着您永远不会知道用户输入的是无害的数字,如"1"或类似"1000 int

00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

可能更糟的是:

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

这意味着您永远不会知道用户输入了"abc"还是"0"。

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

不过,让我们假设用户只需输入"4"。

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

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

撇开可读性问题不谈,这里发生的事情是:

  • i仍然是 0,所以你得到 (sptr+0) ,等于 (sptr)
  • 取消引用sptr以获取对student对象的引用。
  • 您尝试取消引用student对象的name指针。

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

用裸指针做到这一点是非常困难的。您可以添加用户输入,询问每个名称的大小,以便在读取名称之前分配足够的内存。或者,您可以使用 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";
    }
}

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

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

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

段错误是您正在访问内存位置而没有对其进行初始化。无需定义结构。既然是CPP,那么为什么不尝试CPP std::string数据类型。

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

尝试:

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;