在0x5914F3BE抛出异常(基于.dll)

Exception thrown at 0x5914F3BE (ucrtbased.dll)

本文关键字:dll 基于 0x5914F3BE 抛出异常      更新时间:2023-10-16

我有一些代码可以从.txt文件中获取名称列表+双精度值,并在命令提示符下显示这些值。为此,动态分配结构数组。 代码应根据.txt文件中的第一个值知道数组的大小,然后是名称和关联值。然后,它应分两部分显示列表,其中名称的关联双精度值高于或等于 10.000 首先列出。如果所有值都不符合条件的,则在前半部分显示"无"。

程序执行,但调试器给出异常,输出与预期不符。

#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
using namespace std;
struct donor
{
string name;
double contribution = 0;
};
int main()
{
string filename;
ifstream inFile;
cout << "Enter name of data file: ";
cin >> filename;
inFile.open(filename);
cin.clear();
if(!inFile.is_open())
{
cout << "Could not open the file " << filename << endl;
cout << "Program terminating.n";
exit(EXIT_FAILURE);
}
int amount;
inFile >> amount;
cin.clear();
donor* dlist = new donor[amount];
int i;
while(inFile.good())
{
for(i = 0; i < amount; i++)
{
getline(inFile, dlist[i].name);
cin.clear();
inFile >> dlist[i].contribution;
cin.clear();
}
}
cout << "Here's the list of Grand Patrons:n";
bool grandpatrons = false;
for(i = 0; i < amount; i++)
{
if(dlist[i].contribution >= 10000)
{
grandpatrons = true;
cout << dlist[i].name << endl;
cout << dlist[i].contribution << endl;
}
}
if(grandpatrons == false)
{
cout << "None" << endl;
}
cout << "Here's the list of Patrons:n";
for (i = 0; 1 < amount; i++)
{
if (dlist[i].contribution < 10000)
{
cout << dlist[i].name << endl;
cout << dlist[i].contribution << endl;
}
}
delete[] dlist;
return 0;
}

捐赠者列表.txt文件如下所示:

4
Bob
400
Alice
11000

但输出如下所示:

Enter name of data file: donorlist.txt
Here's the list of Grand Patrons:
None
Here's the list of Patrons:
0
0
0
0

调试器给我的异常是:

Exception thrown at 0x5914F3BE (ucrtbased.dll) in 6_9.exe: 0xC0000005: Access violation reading location 0xA519E363.

现在我假设从动态分配的内存中读取时出现问题。也许有什么东西导致我从分配的数组之外的内存中读取?我无法准确找到错误所在。

您的问题始于数据文件中写入的错误amount。 修复它:

2
Bob
400
Alice
11000

然后,他们继续说明您无意中读取文件的事实。
请记住:混合operator>>getline()并不像看起来那么简单。
你看,operator>>IGNORESnewlinespace字符,直到找到任何其他字符。
然后,它会读取即将到来的字符,直到遇到下一个newlinespace字符,但不会丢弃它。

这就是getline问题所在。getline读取所有内容,直到遇到newline或指定的delim字符。 这意味着,如果您的operator>>在遇到newline后停止,getline不会读取任何内容,因为它会立即遇到newline

要解决此问题,您需要处理newline字符。 为此,您可以先检查流中的下一个字符是否确实newline,然后对其使用istream::ignore();

int next_char = stream.peek();
if(next_char == 'n'){
stream.ignore();
}

代码的一个工作示例是:

#include <iostream>
#include <fstream>
#include <string>
using namespace std;
//Suggestion: class/struct names should start with a capital letter.
struct Donor{
//Suggestion: Use member initializer lists to specify default values.
Donor() : name(), contribution(0){}
string name;
double contribution;
};
int main(){
cout << "Enter the filename: ";
string filename;
cin >> filename;
//Suggestion: Open the file immediately with the filename and use `operator bool` to check if it opened.
ifstream inFile(filename);
if(!inFile){
cout << "Could not open the file " << filename << 'n';
cout << "Program terminating.n";
exit(EXIT_FAILURE);
}
int amount;
inFile >> amount; //! Leaves 'n'
Donor* donors = new Donor[amount];
for(int i = 0; i < amount; ++i){
switch(inFile.peek()){
case 'n': inFile.ignore();
break;
case  EOF: cout << "Donor amount too big!n";
exit(EXIT_FAILURE); 
}
getline(inFile, donors[i].name);
inFile >> donors[i].contribution;
}
cout << "Here's the list of Grand Patrons:n";
bool grandpatrons_exist = false;
for(int i = 0; i < amount; ++i){
if(donors[i].contribution >= 10000){
grandpatrons_exist = true;
cout << donors[i].name << 'n';
cout << donors[i].contribution << 'n';
}
}
if(!grandpatrons_exist){
cout << "Nonen";
}
cout << "Here's the list of Patrons:n";
for(int i = 0; 1 < amount; ++i){
if(donors[i].contribution < 10000){
cout << donors[i].name << 'n';
cout << donors[i].contribution << 'n';
}
}
delete[] donors;
return 0;
}

现在,更好的解决方案是使用向量而不是原始指针并实现operator>>operator<<这将大大简化 对象的读取和打印。

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
class Donor{
public:
Donor() noexcept: name(), contribution(0){}
friend istream& operator>>(istream& stream, Donor& donor){
switch(stream.peek()){
case  EOF: return stream;
case 'n': stream.ignore();
}
getline(stream, donor.name);
stream >> donor.contribution;
return stream;
}
friend ostream& operator<<(ostream& stream, const Donor& donor){
stream << donor.name << ' ' << donor.contribution;
return stream;
}
const string& get_name() const noexcept{
return name;
}
const double& get_contribution() const noexcept{
return contribution;
}
private:
string name;
double contribution;
};
int main(){
cout << "Enter the filename: ";
string filename;
cin >> filename;
ifstream inFile(filename);
if(!inFile){
cout << "Could not open the file " << filename << 'n';
cout << "Program terminating.n";
exit(EXIT_FAILURE);
}
int amount;
inFile >> amount;
vector<Donor> donors(amount);
//Read it as `for donor in donors`
for(Donor& donor : donors){
inFile >> donor;
}
//An STL function that takes a lambda as the thirs argument. You should read up on them if you haven't.
//I would prefer using this since it greatly improves readability.
//This isn't mandatory, your implementation of this part is good enough.
bool grandpatrons_exist = any_of(begin(donors), end(donors), [](const Donor& donor){ return donor.get_contribution() >= 10000; });
cout << "Here's the list of Grand Patrons:n";
if(grandpatrons_exist){
for(const Donor& donor : donors){
if(donor.get_contribution() >= 10000){
cout << donor << 'n';
}
}   
}
else{
cout << "Nonen";
}
cout << "nHere's the list of Patrons:n";
for(const Donor& donor : donors){
if(donor.get_contribution() < 10000){
cout << donor << 'n';
}
}
return 0;
}

其他一些重大改进将是:

  • 使用partition将伟大的赞助人与普通赞助人区分开来。
  • 使用流迭代器将对象读入向量。
int main(){
cout << "Enter the filename: ";
string filename;
cin >> filename;
ifstream inFile(filename);
if(!inFile){
cout << "Could not open the file " << filename << 'n';
cout << "Program terminating.n";
exit(EXIT_FAILURE);
}
//Ignore the first line completely
inFile.ignore(numeric_limits<streamsize>::max(), 'n'); 
//Calls `operator>>` internally
vector<Donor> donors(istream_iterator<Donor>{inFile}, istream_iterator<Donor>{});
auto first_grand_patron = partition(begin(donors), end(donors), [](const Donor& donor){ return donor.get_contribution() >= 10000; });
cout << "Here's the list of Grand Patrons:n";
if(first_grand_patron == begin(donors)){
cout << "None!n";
}
for(auto patron = begin(donors); patron != first_grand_patron; ++patron){
cout << *patron << 'n';
}
cout << "nHere's the list of Patrons:n";
for(auto patron = first_grand_patron; patron != end(donors); ++patron){
cout << *patron << 'n';
}
return 0;
}

现在一些一般提示:

  • 结构/类名称应以大写字母开头。
  • 停止使用 std::endl。
  • 无需cin.clear().Cin 只使用一次,再也不会使用。
  • 使用成员初始值设定项列表。
  • (可选(在循环中使用++i而不是i++for,以习惯递增变量的正确方法,除非另有需要。
  • bool grandpatrons对于标志来说太抽象了。
  • donors是一个主观上比捐赠者名单的缩写更好的名称。