使用函数调整动态结构数组的大小
Resize a dynamic array of structures with a function
根据我的阅读,我的代码应该没有问题。
我正在阅读 Alex Allain 的 jumping into C++,他像这样复制动态 int 数组,但是当我尝试以相同的方式复制结构数组时,它给了我这个错误:
Unhandled exception at 0x777108B2 in Friend Tracker.exe: Microsoft C++ exception: std::length_error at memory location 0x006FF838.
根据我的理解,这告诉我 运行 堆数据不足。但是我有 14GB 未使用的 RAM,这怎么可能?
在看别人同样错误的问题时,他们正在保存多个大小为1GB的数组副本。
这让我认为问题出在我内存中的位置,但为什么编译器没有在我的新数组中分配正确数量的堆数据?
struct frnd
{
string firstName;
string lastName;
int lastTalked;
};
int main()
{
int size = 1;
frnd* friendsList;
friendsList = new frnd[2];
friendsList[1].firstName = "larry";
frnd* temp;
temp = new frnd[4];
for (int i = 1; i < 3; i++)
{
temp[i] = friendsList[i];
}
delete[] friendsList;
friendsList = new frnd[4];
for (int i = 1; i < size; i++)
{
friendsList[i] = temp[i];
}
delete[] temp;
cout << "\n\n" << friendsList[1].firstName << "\n\n";
system("PAUSE");
return 0;
}
你的错误是由于 C++ 中的数组索引是从零开始的,但你使用它就像数组是从一开始的。
for (int i = 1; i < 3; i++) {
temp[i] = friendsList[i];
}
friendsList
指向二元素数组中的第一个元素。这访问 friendsList[1]
,没问题。 (但请注意,它是数组中的第二个元素,而不是第一个。)
在下一次迭代中它访问 friendsList[2]
,这将是数组的 third 元素,但您的数组只有两个元素。因此,这是越界数组访问,它会导致未定义的行为。在您的情况下,这表现为以下事件序列的异常:
- 您的代码试图复制
friendsList[2]
对象(一个 frnd
),但这超出了数组分配的末尾。 这是未定义的行为,不好,句号。 但是,为了解释 为什么 你得到 这个特定的异常.. .
- 复制
frnd
对象使用默认复制构造函数,它只是将源对象的每个数据成员复制到目标。
- 当编译器尝试复制
friendsList[2].firstName
时,您实际上是在使用 与您的分配 相邻的一些其他内存作为 std::string
对象,当没有这样的对象是在那个内存区域中构造的。
std::string
复制赋值运算符尝试读取字符串内部数据成员并获取垃圾(未初始化的 and/or 不确定值,这里的区别毫无意义,因为我们已经进入 UB 领域) .它可能尝试做的第一件事是使用来自不存在的源字符串对象的垃圾 "string length" 值分配一个 char
数组,如果 that 成功,则它会尝试从未初始化指针指向的内存区域复制那么多字符。毫无例外地退出此 "copy my garbage data as though it were a string
object" 操作的可能性非常非常低。
- 另一种可能的结果:
&friendsList[2]
可能指向尚未映射到进程地址的内存区域 space,在您尝试从中读取时触发访问冲突. 这实际上比调用 UB 时可能发生的其他事情更可取。
您可以通过调整循环的边界来解决此问题:(int i = 0; i < 2; i++)
另一方面,你的第二个循环什么也不做,因为 i
和 size
都是 1,所以测试 i < size
失败。
正如在对您的问题的评论中所指出的,在尝试复制对象之前,您也没有初始化对象的 lastTalked
成员。除了一些例外,reading an uninitialized value causes undefined behavior。因此,从技术上讲,即使 i
为 1 时的第一个循环迭代也会调用未定义的行为,但是对于您的特定编译器和体系结构,这可能表现为不确定值的副本。
这可以通过添加初始化成员的默认 frnd
构造函数来解决:
struct frnd
{
frnd() : lastTalked(0) { }
string firstName;
string lastName;
int lastTalked;
};
关于您的代码的其他想法:
- 我知道这段代码是一个练习。但是,对于生产代码,我需要推荐使用
std::vector
。
using namespace std;
是 bad practice.
根据我的阅读,我的代码应该没有问题。 我正在阅读 Alex Allain 的 jumping into C++,他像这样复制动态 int 数组,但是当我尝试以相同的方式复制结构数组时,它给了我这个错误:
Unhandled exception at 0x777108B2 in Friend Tracker.exe: Microsoft C++ exception: std::length_error at memory location 0x006FF838.
根据我的理解,这告诉我 运行 堆数据不足。但是我有 14GB 未使用的 RAM,这怎么可能?
在看别人同样错误的问题时,他们正在保存多个大小为1GB的数组副本。
这让我认为问题出在我内存中的位置,但为什么编译器没有在我的新数组中分配正确数量的堆数据?
struct frnd
{
string firstName;
string lastName;
int lastTalked;
};
int main()
{
int size = 1;
frnd* friendsList;
friendsList = new frnd[2];
friendsList[1].firstName = "larry";
frnd* temp;
temp = new frnd[4];
for (int i = 1; i < 3; i++)
{
temp[i] = friendsList[i];
}
delete[] friendsList;
friendsList = new frnd[4];
for (int i = 1; i < size; i++)
{
friendsList[i] = temp[i];
}
delete[] temp;
cout << "\n\n" << friendsList[1].firstName << "\n\n";
system("PAUSE");
return 0;
}
你的错误是由于 C++ 中的数组索引是从零开始的,但你使用它就像数组是从一开始的。
for (int i = 1; i < 3; i++) {
temp[i] = friendsList[i];
}
friendsList
指向二元素数组中的第一个元素。这访问 friendsList[1]
,没问题。 (但请注意,它是数组中的第二个元素,而不是第一个。)
在下一次迭代中它访问 friendsList[2]
,这将是数组的 third 元素,但您的数组只有两个元素。因此,这是越界数组访问,它会导致未定义的行为。在您的情况下,这表现为以下事件序列的异常:
- 您的代码试图复制
friendsList[2]
对象(一个frnd
),但这超出了数组分配的末尾。 这是未定义的行为,不好,句号。 但是,为了解释 为什么 你得到 这个特定的异常.. . - 复制
frnd
对象使用默认复制构造函数,它只是将源对象的每个数据成员复制到目标。 - 当编译器尝试复制
friendsList[2].firstName
时,您实际上是在使用 与您的分配 相邻的一些其他内存作为std::string
对象,当没有这样的对象是在那个内存区域中构造的。 std::string
复制赋值运算符尝试读取字符串内部数据成员并获取垃圾(未初始化的 and/or 不确定值,这里的区别毫无意义,因为我们已经进入 UB 领域) .它可能尝试做的第一件事是使用来自不存在的源字符串对象的垃圾 "string length" 值分配一个char
数组,如果 that 成功,则它会尝试从未初始化指针指向的内存区域复制那么多字符。毫无例外地退出此 "copy my garbage data as though it were astring
object" 操作的可能性非常非常低。- 另一种可能的结果:
&friendsList[2]
可能指向尚未映射到进程地址的内存区域 space,在您尝试从中读取时触发访问冲突. 这实际上比调用 UB 时可能发生的其他事情更可取。
- 另一种可能的结果:
您可以通过调整循环的边界来解决此问题:(int i = 0; i < 2; i++)
另一方面,你的第二个循环什么也不做,因为 i
和 size
都是 1,所以测试 i < size
失败。
正如在对您的问题的评论中所指出的,在尝试复制对象之前,您也没有初始化对象的 lastTalked
成员。除了一些例外,reading an uninitialized value causes undefined behavior。因此,从技术上讲,即使 i
为 1 时的第一个循环迭代也会调用未定义的行为,但是对于您的特定编译器和体系结构,这可能表现为不确定值的副本。
这可以通过添加初始化成员的默认 frnd
构造函数来解决:
struct frnd
{
frnd() : lastTalked(0) { }
string firstName;
string lastName;
int lastTalked;
};
关于您的代码的其他想法:
- 我知道这段代码是一个练习。但是,对于生产代码,我需要推荐使用
std::vector
。 using namespace std;
是 bad practice.