visual studio2022:RangeChecks 检测代码检测到超出范围的数组访问
在写C++课设时,在更改了原程序后显示检测代码检测到超出范围的数组访问,困扰了我一周多。
课设内容是:利用二叉树解决四则运算的问题
//Filename:tree.h
#pragma once
#include
#include
#include
#ifndef _TREE_H//条件编译,若d未定义,则编译以下程序,否则不编译。
#define _TREE_H//宏定义
#include
#include
using namespace std;
bool IsOperator(string mystring)//重载函数,检查字符串是否为运算符 bool:逻辑运算
{
if (mystring == "-" || mystring == "+" || mystring == "/" || mystring == "*" || mystring == "^")
return(true);//如果是+、-、、*、/、^,返回true(1)
else
return(false);//如果不是+、-、、*、/、^,返回false(0)
}
bool IsOperator(char ops)//重载函数,检查字符是否为运算符
{
if (ops == '+' || ops == '-' || ops == '*' || ops == '/' || ops == '^' || ops == '(' || ops == ')')
return(true);
else
return(false);
}
class node_type//二叉树类结点的定义和实现
{
public:
string data;//字符串数据
node_type* left_child;//左向指针,指向该类型结点
node_type* right_child;//右向指针
node_type(string k)//在类中定义构造函数,用数据初始化结点
{
data = k;//数据域被赋值
left_child = NULL;//左右指针均为空,相当于一个叶子结点
right_child = NULL;
}
};
class binary_tree//二叉树类的定义和实现
{
public:
node_type* root;//二叉树的根结点
void print(node_type* r);//Postfix traversal后序遍历(非递归形式)
binary_tree(void) { root = NULL; }//缺省构造
void print(void) { print(root); }//print函数重载,函数体调用参数为root后序遍历函数
void evaluate()//函数重载,计算根结点数据(整个二叉树表达式的值)
{
evaluate(root);//函数体中调用参数为root的重载函数evaluate
}
void evaluate(node_type* prt)//递归计算结点标准形式的结点数据,计算prt所指结点以下的子表达式的值,并将结果赋予prt指向的结点
{
if (IsOperator(prt->data) && !IsOperator(prt->left_child->data) && !IsOperator(prt->right_child->data))
{//如果遇到的二叉树结点是标准形式(中间是运算符,两边是数字)开始计算(prt指向的结点数据是运算符,左右结点均不是运算符)
int num = 0;//num变量存放运算结果
int num1 = atoi(prt->left_child->data.c_str());//将左右子节点data域的数据有string转换为int类型,存放在num1,num2
int num2 = atoi(prt->right_child->data.c_str());
if (prt->data == "+")//若为+,执行加法运算
{
num = num1 + num2;
cout << num1 << '+' << num2 << '=' << num << endl;//输出四则运算的每一步算式及结果
}
else if (prt->data == "-")
{
num = num1 - num2;
cout << num1 << '-' << num2 << '=' << num << endl;
}
else if (prt->data == "*")
{
num = num1 * num2;
cout << num1 << '*' << num2 << '=' << num << endl;
}
else if (prt->data == "/")
{
num = num1 / num2;
cout << num1 << '/' << num2 << '=' << num << endl;
}
else if (prt->data == "^")
{
num = pow(num1, num2);
cout << num1 << '^' << num2 << '=' << num << endl;
}
stringstream bob;
/* 定义了三个类:istringstream、ostringstream 和 stringstream,分别用来进行流的输入、输出和输入输出操作。本文以 stringstream 为主,介绍流的输入和输出操作。
主要用来进行数据类型转换,由于 使用 string 对象来代替字符数组(snprintf 方式),避免了缓冲区溢出的危险;
而且,因为传入参数和目标对象的类型会被自动推导出来,所以不存在错误的格式化符号的问题。
可以使用 str() 方法,将 stringstream 类型转换为 string 类型;
可以将多个字符串放入 stringstream 中,实现字符串的拼接目的;
如果想清空 stringstream,必须使用 sstream.str(""); 方式;clear() 方法适用于进行多次数据类型转换的场景。*/
bob << num;//将num由int转换为string型
string suzzy(bob.str());
prt->data = suzzy;//将计算结果赋值给prt所指结点的数据域
prt->left_child = NULL;//将prt所指左右指针项清空,相当于删去了左右两叶子结点
prt->right_child = NULL;//此时,prt所指向的结点变成一个操作数结点(叶子结点),继续参与后面运算
}
else if (prt->left_child == NULL && prt->right_child == NULL);//若prt所指结点的左右结点是叶子结点(操作数),不进行计算
else//若prt所指结点的左右结点不全是叶子结点,则递归计算,则递归计算,先计算右子树,在计算左子树
{
evaluate(prt->left_child);//一层层递归至结点标准形式中间是运算符,两边是数字(操作数)
evaluate(prt->right_child);//再将计算结果层层返回
evaluate(prt);//最后可的prt结点以下的子表达式的计算结果,并置于prt结点中
}
}
void clear_help(node_type* rt)//删除结点,将rt指向结点及以下结点全部删除
{
if (rt != NULL)
{
clear_help(rt->left_child);//递归,从rt递归至叶子结点,再层层向上删除
clear_help(rt->right_child);//最后删除至rt所指向结点
delete rt;
}
}
void clear()//删除全部结点
{
clear_help(root);//将root结点及其以下结点全部删除
}
};
/*二叉树类定义完毕,主要包括:
* root, binary _ tree 构造函数, print (重载),evaluate (重载), clear _ help, clear等
在以上定义的类 binary _ tree 中,有些仅给出了函数原型,还需在类外对这些函数进行定义
如:void print(node _ type r )后序遍历函数*/
/*用字符串动态生成一个结点的函数,返回值为指向此新结点的指针*/
node_type* build_node(string x)//用字符串生成一个结点
{
node_type* new_node;//定义二叉树结点类型的指针
new_node = new node_type(x);//分配由结点类型确定的一片连续内存空间,首地址赋给 new _ node 指针,并将 x 作为这片内存空间的初始化值
return(new_node);//返回指针
}
void binary_tree::print(node_type* p)//后序遍历
{
if (p != NULL)//如果指向的根节点=NULL,此为空数,遍历结束
{
print(p->left_child);//处理左子树 1.向左走,递归处理
print(p->right_child);//处理右子树 2.向右走,递归处理
cout << p->data << " ";//处理打印结点内容 3.处理目前结点,输出
}
}
bool IsOperand(char ch)//判断字符是否为数字字符
{
if ((ch >= '0') && (ch <= '9'))//是数字字符
return true;
else
return false;
}
/*判断运算符A与运算符B的优先级的大小
如果A的优先级高于B,返回true ;
如果A的优先级低于B,返回false;
TakesPrecedence_1,最后else情况下返回 true ,用于对运算符是否出栈进行判断
主要是对A,B为+,-号或A,B为*,/号时其优先级进行判断,此时应该按从左到右的顺序看待其在表达式中的优先级)*/
bool TakesPrecedence_1(char OperatorA, char OperatorB)//判断运算符A与运算符B的优先级的大小
{
if (OperatorA == '(')
return false;
else if (OperatorB == '(')
return false;
else if (OperatorB == ')')
return true;
else if ((OperatorA == '^') && (OperatorB == '^'))
return false;
else if (OperatorA == '^')
return true;
else if (OperatorB == '^')
return false;
else if ((OperatorA == '*') || (OperatorA == '/'))
return true;
else if ((OperatorB == '*') || (OperatorB == '/'))
return false;
else
return true;
}
/*TakesPrecedence_2,最后else情况下返回false,用于对运算符是否入栈进行判断
主要是对A,B为+,-号或A,B为*,/号时其优先级进行判断,此时应该按从左到右的顺序看待其在表达式中的优先级)*/
bool TakesPrecedence_2(char OperatorA, char OperatorB)
{
if (OperatorA == '(')
return false;
else if (OperatorB == '(')
return false;
else if (OperatorB == ')')
return true;
else if ((OperatorA == '^') && (OperatorB == '^'))
return false;
else if (OperatorA == '^')
return true;
else if (OperatorB == '^')
return false;
else if ((OperatorA == '*') || (OperatorA == '/'))
{
if ((OperatorB == '*') || (OperatorB == '/'))
return false;
else
return true;
}
else
return false;
}
void copy(node_type*& r1, node_type*& r2)//结点拷贝,将结点r2的分支结点拷贝到r1中
{
if (r2 == NULL)//r2是空结点
r1 = NULL;
else
{
if (r1 == NULL)//r1是空结点
{
r1 = build_node(r2->data);//调用生成结点函数,建立父结点,将r2->data值域作为此父结点数据域值
copy(r1->left_child, r2->left_child);//递归调用,拷贝分支结点
copy(r1->right_child, r2->right_child);
}
else//如果r1不为空结点,进行同样操作
{
r1 = build_node(r2->data);
copy(r1->left_child, r2->left_child);
copy(r1->right_child, r2->right_child);
}
}
}
#endif//条件编译结束
// erchashu.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//二叉树后序遍历源代码及关键源代码注解如下
#include
#include <string>
#include
#include "tree.h"
/*******Checks if expression is ok*********/
bool isok(string exp)//检查输入的字符串是否合法,左右括号是否相等等,形参exp为字符数组名
{
char check;//用于存放被检查字符
int error = 0;//error表示表达式中错误的个数
int lb = 0;//lb表示左括号个数
int rb = 0;//右括号个数
for (int m = 0; m < exp.size(); m++)///逐个字符检查
{
check = exp[m];//将被检查字符放入字符型变量check
if (IsOperand(check)) {}//判断字符是否为数字字符,若是回到for循环,执行下一字符的检查
if (IsOperator(check))//判断字符是否为运算符
{
if (check == ')')//若是右括号,优先级最低
{
rb++;//右括号个数+1
if (IsOperator(exp[m + 1]) && exp[m + 1] == '+' || exp[m + 1] == '-' || exp[m + 1] == '*' || exp[m + 1] == '/' || exp[m + 1] == '^' || exp[m + 1] == ')')//右括号后跟这些运算符合法
{
m++;//判断下一个字符,相当于检查过了,省掉一次循环过程
if (exp[m] == ')')//如果是右括号,右括号数加1
rb++;//若以上其他合法运算符,则直接回到for,计数器+1,执行再下一个字符的检查
}
else if (IsOperator(exp[m + 1]))//如果是右括号后为其他运算符
error++;//非法
}
else if (check == '(')//如果是左括号
{
lb++;//左括号个数加1
if (IsOperator(exp[m + 1]) && exp[m + 1] == '(')//左括号后跟左括号合法
{
m++;//判断下一个,相当于此第二个左括号已被判断合法,省掉一次循环
lb++;//左括号数+1,回到for循环起始处
}
else if (IsOperator(exp[m + 1]))//如果左括号后跟其他运算符,非法
error++;
}
else//如果被检查字符为除了(,)以外其他运算符,如+,-,*,/,^等
{
if (IsOperator(exp[m + 1]) && exp[m + 1] == '(')//如果紧接的字符是左括号,合法
{
m++;//省掉一次循环,回到for循环起始处,判断下一个
lb++;//左括号个数加一
}
else if (IsOperator(exp[m + 1]))//如果紧接的字符后为其他运算符,非法
error++;
}/*以上是当check为数字字符或运算符的情况及其进行的合法性判断*/
}
else //除了数字字符和运算符,其他字符非法,表达式中错误个数+1
error++;
}//以上是for循环,直至扫描之整个表达式结束
if (error == 0 && lb == rb)//如果左右括号个数相等,error数为0,整个表达式合法
return(true);
else//否则非法
return(false);
}
/*二叉树解决四则运算问题的主程序*/
void main()
{
binary_tree etree;//定义二叉树对象
stackNodeStack;//定义二叉树类型的堆栈NodeStack
stack<char>OpStack;//定义字符类型的堆栈,放运算符
string infix;//字符串类型变量
char choice = 'y';//字符型变量choice,作为程序是否继续运行的控制循环变量。并赋初值y
char c;
while (choice == 'y' || choice == 'Y')
/*当choice值为y时,说明并不结束程序,而是继续运行,此处用循环语句控制程序是否结束*/
{
char str1[50], str2[50];
int i = 0, j = 0;
cout<< "\n\nIntroduce the Expression:";//(no spaces)提醒输入时不要有空格,修改为允许输入空格
cin.getline(str1, 50);
/*/修改部分:要求允许表达式在输入时有空格;因为当碰到空格时, cin 会停止读取数据,为解
决这个问题,可以采用 cin , getline 函数将输入的字符串读到一个字符读到一个数组中,再对此字符数组进行操作*/
for (i = 0; i < 50; i++)//将字符串中的空格全部去掉,再将整理好的字符串赋给 infix ,这样,
{//后面所有的对表达式有效性判断及构造二义树的处理均不需要改变
//但是,由于 cin . getline 函数从输入流读字符时,遇到中止字符(回车)时结束,指针移到该中止字符之后*/
if (str1[i] != ' ')
str2[j++] = str1[i]; /*故当 cin 后再用 cin.getline 函数时,
必须先将输入缓冲区中的回车符去掉,才可以正确读入后面输入的字符串*/
}/*所以,循环的最后 cin >> choice 之后,须加上 cin.ignore 函数,才能保证继续程序时,
cin.getline 能正确读入字符串*/
str2[j] = '\0';
infix = str2;
//将输入的表达式字符串放入字符串变最 infix
cout << "--------------------------------" << endl;
cout << "Expression: " << infix << endl;
if (isok(infix))//如果输入字符串是合法表达式,下面构造表达式二叉树
{
for (int i =0 ; i < infix.size(); i++)//扫描整个表达式,对取出的字符按照数字及运算符进行不同的处理
{
c = infix[i];//依次取字符串表达式中的字符
if (IsOperand(c))//如果是数字字符,将整个操作数作为一个二叉树(单结点)并压入二叉树类型的堆栈
{
string tempstring;//定义字符串临时变量,用于存放将整个操作数取出的中间变量
tempstring = tempstring + c;//操作数不一定只有一位,要将一串连续的数字字符作为一个操作数整体
if (i + 1 < infix.size() && IsOperand(infix[i + 1]))//如果后一个仍为数字字符
{
while (i + 1 < infix.size() && IsOperand(infix[i + 1]))
{
tempstring = tempstring + infix[++i];//将相邻的数字字符放到一起,直到遇到下一个运算符或表达式结束
}
}
binary_tree temp;//建立二叉树类的临时对象temp,用于生成数据域操作数的单结点二叉树,并完成其入栈操作
temp.root = build_node(tempstring);//生成字符串类型结点作为temp(单结点二叉树),并将操作数作为其数据域的初始化值(建立数字字符串类型的二叉树)
NodeStack.push(temp);//将temp压入二叉树栈
}//以上完成再构造二叉树中,对表达式中操作数的操作
//如果是运算符+、一、*、/、,则比较它们与与运算符栈顶元素的优先级,如果高于栈顶元素,则入栈:如果低于栈顶元素,则对栈顶运算符进行构造二叉树子树的操作
else if (c == '+' || c == '-' || c == '*' || c == '/' || c == '^')//如果取出的是运算符
{
if (OpStack.empty())//运算符栈为空,直接入栈
OpStack.push(c);//运算符压栈
else if (OpStack.top() == '(')//如果符栈的顶部为左括号,也直接入栈
OpStack.push(c);//运算符压栈
else if (TakesPrecedence_2(c, OpStack.top()))//栈顶运算符优先级低于c ,直接入栈
OpStack.push(c);//若运算符优先级高,运算符入栈
else//如果栈顶运算符优先级高于c ,则要对栈顶运算符进行构造二叉子树的操作
{
while (!OpStack.empty() && TakesPrecedence_1(OpStack.top(), c))
{
binary_tree temp_tree;//生成二叉树定义,定义二叉树类的临时对象temp _ tree ,用于对二叉树进行出栈、入钱操作,并作为构造二叉树的中间对象
string thisstring = " ";
thisstring = thisstring + OpStack.top();//将运算符栈顶元素赋值给thisstring
OpStack.pop();//运算符栈栈顶元素弹栈
etree.root = build_node(thisstring);//构造二叉树子树,并将此(优先级高的)运算符作为新构造的子二叉树的根结点
copy(temp_tree.root, NodeStack.top().root);//将二叉树堆栈栈顶的二叉树复制到新生成的临时二叉树中
NodeStack.pop();//复制过的二叉树删除,二叉树栈弹栈
etree.root->right_child = temp_tree.root;//将二叉树作为新二叉树的右分支
/*将二叉树弹出的二叉树看作一个操作数,作为运算符结点的右子树(因为先弹出二叉树栈的总是后入栈的,为运算符的右操作数)*/
temp_tree.root = NULL;//临时二叉树对象清空,准备接受另一个二叉树栈的弹栈操作(作为运算符的左子树)
copy(temp_tree.root, NodeStack.top().root);//继续将下一个栈顶二叉树复制,将二叉树栈的栈顶二叉树拷贝到临时二叉树中
etree.root->left_child = temp_tree.root;//成为新二叉树的左分支,作为运算符结点的左子树
NodeStack.pop();//删除复制过的二叉树(二叉树弹栈)
temp_tree.root = NULL;//临时二义树对象清空,准备将新构造的二叉树压入二叉树栈的操作
copy(temp_tree.root, etree.root);//将新构造的二叉树拷贝到临时二叉树对象中
NodeStack.push(temp_tree);//将新生成的二叉树进栈
etree.root = NULL;//将构造的二叉子树的二叉树对象清空,准备进行下一次操作
}
OpStack.push(c);//将低优先级的符号压栈
//以运算符栈栈顶元素构造的新子树入栈后,即将其弹栈,回到 for 循环,继续表达式中下一个字符的判断和操作
} //整个构造二叉树子树的操作实际上是利用了栈来代替了递归
}
else if (c == '(')//如果为左括号,直接压入运算符栈
OpStack.push(c);//直接压栈
else if (c == ')')
{//计算过程与上面的循环结构类似,也是运算符栈元素弹栈,作为新构子二叉树的根节点,二二叉树栈连续两次弹栈,分别作为其右子树、左子树,再将新构造的二叉树压入二叉树栈
//期间,也要用到用来构造子二叉树的 etree 和临时对象 temp _ tree
while (OpStack.top() != '(')//当栈顶不为左括号时,先计算栈中储存的运算符(则要先对运算符栈中的运算符逐一进行构造子二叉树操作),直到遇到左括号为止
{//计算过程与上面循环类似
binary_tree temp_tree;
string thisstring = "";
thisstring = thisstring + OpStack.top();
OpStack.pop();
etree.root = build_node(thisstring);
copy(temp_tree.root, NodeStack.top().root);
NodeStack.pop();
etree.root->right_child = temp_tree.root;
temp_tree.root = NULL;
copy(temp_tree.root, NodeStack.top().root);
etree.root->left_child = temp_tree.root;
NodeStack.pop();
temp_tree.root = NULL;
copy(temp_tree.root, etree.root);
NodeStack.push(temp_tree);
etree.root = NULL;
}
OpStack.pop();//删除左括号
}// 以上过程循环至对整个表达式扫描完毕
}
//如果此时运算符栈还未空,逐一将其弹栈,和上面一样以运算符为根结点进行子二叉树的构造,如此循环至运算符栈为空
while (!OpStack.empty())//将字符串表达式扫描一遍后,如果运算符堆栈没有处理完
{//同上面的循环一样,继续将运算符堆栈中的运算符与二叉树堆栈中的二叉树形成新的二叉树堆栈
binary_tree temp_tree;
string thisstring = "";
thisstring = thisstring + OpStack.top();
OpStack.pop();
etree.root = build_node(thisstring);
copy(temp_tree.root, NodeStack.top().root);
NodeStack.pop();
etree.root->right_child = temp_tree.root;
temp_tree.root = NULL;
copy(temp_tree.root, NodeStack.top().root);
etree.root->left_child = temp_tree.root;
NodeStack.pop();
temp_tree.root = NULL;
copy(temp_tree.root, etree.root);
NodeStack.push(temp_tree);
if (!OpStack.empty())
etree.root = NULL;
}
//将所有运算符弹栈后,此时 etree 即为符合要求的二叉树
//表达式的二义树构造完毕
//对此二叉树进行所需操作
//因为 etree 是作为一个类的对象定义,对二叉树操作的函数被封装在类中,可通过调用其公有成员函数对其进行操作
cout << "Postfix traversal: ";
etree.print();//后序遍历,并输出遍历结果
cout << endl;
etree.evaluate();//( evaluate 重载函数中采用后序遍历算法)计算最后的二叉树((从根结点处,即对整个二叉树表达式进行计算))
cout << "Result: " << etree.root->data << endl;//,计算结果被保存再根节点数据域中,输出结果
cout << "--------------------------------" << endl;
cout << "\n\nRun Program again? Enter :" ;//循环控制条件,由用户决定是否再次继续进行表达式计算
cin >> choice;
cin.ignore();
}
else//如果输入的表达式不符合要求,输出错误提示,并由用户决定是香继续进行表达式计算
{
cout << "****************************" << endl;
cout << "ERROR:Invalid Expression" << endl;
cout << "****************************" << endl;
cout << "\n\nRun Program again? Enter :" ;
cin >> choice;
cin.ignore();
}
}//一次表达式计算过程结束,回到 while 语句,判断控制条件 choice ,诀定是香退出程序或是继续新的计算
}//主程序结束
开始以为是由于pow函数本来是double型,却被强制转换成了int型所造成的后来发现还是不行
请问怎么修改才能不报错?