结构数组的动态内存分配
dynamic memory allocation for array of structures
我想为结构数组"学生"动态分配
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;
}
}
}
代码编译没有错误,但在运行时,输入名字后是说"分段错误(核心转储)"并停止。我哪里出错了?
- 您不需要结构来仅保存一个字符串
- 无需在每次迭代中调用
atoi
- 您需要分配内存才能接收用户输入
工作代码可能如下所示:
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
可能更糟的是:
如果无法执行转换,则返回
0
。
这意味着您永远不会知道用户输入了"abc"还是"0"。
std::stoi
是一种安全、现代的替代方案,可让您执行错误检查。
不过,让我们假设用户只需输入"4"。
然后我们进入循环体并遇到以下行:
cin>>*(*(sptr+i)).name;
撇开可读性问题不谈,这里发生的事情是:
-
i
仍然是 0,所以你得到(sptr+0)
,等于(sptr)
。 - 取消引用
sptr
以获取对student
对象的引用。 - 您尝试取消引用
student
对象的name
指针。
后一步最终会导致未定义的行为,因为name
指针未初始化。你一定不能这样做。此时,程序的其余部分都已呈现为无效。
用裸指针做到这一点是非常困难的。您可以添加用户输入,询问每个名称的大小,以便在读取名称之前分配足够的内存。或者,您可以使用 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";
}
}
该程序定义了行为;它不会因输入错误而崩溃,并且允许包含空格的名称。它也更容易阅读,不使用外部的任何指针,并且它自动处理内存,比手动代码更智能,更快。
如果您需要存储输入中的名称,则只需使用像向量或字符串列表这样的数据结构来避免内存管理等错误:
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;
- Win32编译器选项和内存分配
- 多个文件的内存分配错误"在抛出 'std :: bad_alloc' what (): std :: bad_alloc 的实例后终止调用" [C++]
- 当需要超过16GB的连续内存时,内存分配失败
- 尝试摆脱任何堆内存分配
- 以下代码执行哪种内存分配(动态或静态)?
- 开放 CV 中的动态内存分配,用于视频处理
- 为什么类和 main() 函数中也有动态内存分配
- 使用 NTAllocateVirtualMemory 和 GetProcAddress 的内存分配问题不起作用
- C++:矢量分配器行为、内存分配和智能指针
- 介于 [固定数组] 和 [带内存分配的指针] 之间的性能
- Linux C++ 中的页面对齐内存分配
- 整数内存分配/释放
- 将内存分配返回值强制转换为 TYPE 数组
- C++程序什么都不做,但瓦尔格林德显示内存分配
- 给定特定内存地址的数组的动态内存分配
- 如何完成内存分配
- 我刚刚了解了C++中的动态内存分配
- 在先前调用 string::find 后添加内存分配和内存集会导致它返回 npos.为什么?
- 对于堆上的页面对齐内存分配是否有任何优化或不同的 API?
- 无法删除布尔动态内存分配