Я попытался написать программу на CPP, которая получает инфиксное выражение, преобразует его в постфиксное выражение, а затем вычисляет выражение, используя выполненный мной стек-класс. Я думал, что то, что я написал, работает, потому что я провел несколько тестов в Visual Studio. Но я не знаю почему, тесты дают результат X в Visual Studio и результат Y в GCC. Например, когда я запускаю программу и ввожу (5+3)*((20/10)+(8-6)) в Visual Studio, она печатает 32. С другой стороны, в GCC печатается 0.
Итак, мой вопрос: знает ли кто-нибудь, что может вызвать эту разницу? Может в компиляторах что-то другое?
Я думаю, что это может быть вызвано неопределенным поведением. Но даже если это так, я понятия не имею, где он вызывается...
(Кстати, в GCC вместо разделения Stack.h в другой файл я просто написал его выше основного - вывод в Visual Studio все равно отличается)
основной.cpp:
#include "Stack.h"
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
bool operatorsPrecedenceCheck(string& str, Stack<char>& stk, const char& ch)
{
if (ch == '+' || ch == '-')
{
Stack<char> help(stk.getSize());
char stkTop = '\0';
if (!stk.isEmpty())
stkTop = stk.top();
while (!stk.isEmpty() && stkTop != '(')
{
stkTop = stk.top();
if (stk.top() == '*' || stk.top() == '/')
{
str += stk.pop();
str += ' ';
}
else
help.push(stk.pop());
}
while (!help.isEmpty())
stk.push(help.pop());
stk.push(ch);
return 1;
}
else if (ch == '*' || ch == '/')
{
stk.push(ch);
return 1;
}
return 0;
}
bool parenthesisCheck(string& str, Stack<char>& stk, const char& ch)
{
if (ch == '(')
{
stk.push(ch);
return 1;
}
else if (ch == ')')
{
while (stk.top() != '(')
{
str += stk.pop();
str += ' ';
}
stk.pop();
return 1;
}
return 0;
}
string infixToPostfix(const string& exp)
{
string str = "";
Stack<char> stk(exp.length());
bool actionExecuted = 0;
for (int i = 0; i < exp.length(); ++i)
{
actionExecuted = parenthesisCheck(str, stk, exp[i]);
if (!actionExecuted)
actionExecuted = operatorsPrecedenceCheck(str, stk, exp[i]);
if (!actionExecuted)
{
if (exp[i] >= '0' && exp[i] <= '9')
{
for (; exp[i] >= '0' && exp[i] <= '9'; ++i)
str += exp[i];
str += ' ';
--i;
}
else
throw"An illegal value in the expression\n";
}
}
while (!stk.isEmpty())
{
str += stk.pop();
str += ' ';
}
str = str.substr(0, str.length() - 1); //Cause of space
return str;
}
int getValueFromString(const string& str, int& i)
{
int result = 0;
int amountOfDigits = 0;
while (str[i + amountOfDigits] >= '0' && str[i + amountOfDigits] <= '9')
++amountOfDigits;
for (int j = pow(10, amountOfDigits - 1); j >= 1; j /= 10)
result += (str[i++] - '0') * j;
--i;
return result;
}
float calcPostfix(const string& str)
{
Stack<int>stk(str.length() / 2);
for (int i = 0; i < str.length(); i += 2)
{
if (str[i] >= '0' && str[i] <= '9')
stk.push(getValueFromString(str, i));
else if (str[i] == '/')
stk.push((float)1 / stk.pop() * stk.pop());
else if (str[i] == '*')
stk.push(stk.pop() * stk.pop());
else if (str[i] == '+')
stk.push(stk.pop() + stk.pop());
else if (str[i] == '-')
stk.push(-1 * (stk.pop() - stk.pop()));
else
throw"Unknown Operator in Calculating Postfix\\n";
}
return stk.top();
}
int main()
{
try
{
string exp;
cout << "enter an infix expression as a string" << endl;
cin >> exp;
string postfix = infixToPostfix(exp);
cout << postfix << endl;
cout << calcPostfix(postfix) << endl;
}
catch (const char* exc)
{
cout << exc;
exit(-1);
}
catch (...)
{
cout << "Unknown Error has occured\\n";
exit(-1);
}
return 0;
}
Стек.h:
#ifndef MYSTACK_H
#define MYSTACK_H
template <class T>
class Stack
{
//Inner class help
void swap(Stack<T> &a, Stack<T>& b);
T* data;
int capacity;
int size;
public:
//Constructors & Destructor
Stack();
Stack(int capacity_);
Stack(const Stack<T>& toCopy);
Stack(Stack<T>&& toMove);
~Stack();
//Methods
int getCapacity() const;
int getSize() const;
void clear();
bool isEmpty() const;
bool isFull() const;
T pop();
void push(const T& toInsert);
T top() const;
//Operators
Stack<T>& operator= (const Stack<T>& toAssign);
Stack<T>& operator=(Stack<T>&& toMove);
};
//Inner class help
template<class T>
void Stack<T>::swap(Stack<T>& a, Stack<T>& b)
{
T* temp_1 = a.data;
a.data = b.data;
b.data = temp_1;
int temp_2 = a.size;
a.size = b.size;
b.size = temp_2;
temp_2 = a.capacity;
a.capacity = b.capacity;
b.capacity = temp_2;
}
//Constructors & Destructor
template<class T>
Stack<T>::Stack() : data(nullptr), capacity(0), size(0) {}
template<class T>
Stack<T>::Stack(int capacity_) : capacity(capacity_), size(0)
{
data = new T[capacity];
if (!data)
throw "ERROR - couldn't allocate memory\n";
}
template<class T>
Stack<T>::Stack(const Stack<T>& toCopy)
{
size = 0;
capacity = toCopy.capacity;
data = new T[capacity];
if (!data)
throw "ERROR - couldn't allocate memory\n";
for (int i = 0; i < toCopy.size; ++i, ++size)
data[i] = toCopy.data[i];
}
template<class T>
Stack<T>::Stack(Stack<T>&& toMove)
{
data = nullptr;
size = capacity = 0;
swap(*this, toMove);
}
template<class T>
Stack<T>::~Stack()
{
clear();
}
//Methods
template<class T>
int Stack<T>::getSize() const
{
return size;
}
template<class T>
int Stack<T>::getCapacity() const
{
return capacity;
}
template<class T>
void Stack<T>::clear()
{
if (data)
{
delete[] data;
data = nullptr;
}
size = capacity = 0;
}
template <class T>
bool Stack<T>::isEmpty() const
{
return size == 0;
}
template <class T>
bool Stack<T>::isFull() const
{
return size == capacity;
}
template <class T>
T Stack<T>::pop()
{
if (isEmpty())
throw"Couldn't pop - Stack is empty\n";
T toReturn = data[--size];
T* temp = new T[capacity];
if (!temp)
throw "ERROR - couldn't allocate memory\n";
for (int i = 0; i < size; i++)
temp[i] = data[i];
delete[]data;
data = temp;
return toReturn;
}
template <class T>
void Stack<T>::push(const T& toInsert)
{
if (isFull())
throw "Couldn't push - stack is full\n";
T* temp = new T[capacity];
if (!temp)
throw "ERROR - couldn't allocate memory\n";
for (int i = 0; i < size; i++)
temp[i] = data[i];
temp[size++] = toInsert;
delete[]data;
data = temp;
}
template<class T>
T Stack<T>::top() const
{
if (isEmpty())
throw"Couln't return top - Stack is empty\n";
return data[size - 1];
}
//Operators
template<class T>
Stack<T>& Stack<T>::operator=(const Stack<T>& toAssign)
{
if (data)
delete[]data;
Stack<T>help = toAssign;
swap(*this,help);
return *this;
}
template<class T>
Stack<T>& Stack<T>::operator=(Stack<T>&& toMove)
{
if (data)
delete[]data;
data = nullptr;
size = capacity = 0;
swap(*this, toMove);
return *this;
}
#endif
Код на GCC
template <class T>
class Stack
{
//Inner class help
void swap(Stack<T> &a, Stack<T>& b);
T* data;
int capacity;
int size;
public:
//Constructors & Destructor
Stack();
Stack(int capacity_);
Stack(const Stack<T>& toCopy);
Stack(Stack<T>&& toMove);
~Stack();
//Methods
int getCapacity() const;
int getSize() const;
void clear();
bool isEmpty() const;
bool isFull() const;
T pop();
void push(const T& toInsert);
T top() const;
//Operators
Stack<T>& operator= (const Stack<T>& toAssign);
Stack<T>& operator=(Stack<T>&& toMove);
};
//Inner class help
template<class T>
void Stack<T>::swap(Stack<T>& a, Stack<T>& b)
{
T* temp_1 = a.data;
a.data = b.data;
b.data = temp_1;
int temp_2 = a.size;
a.size = b.size;
b.size = temp_2;
temp_2 = a.capacity;
a.capacity = b.capacity;
b.capacity = temp_2;
}
//Constructors & Destructor
template<class T>
Stack<T>::Stack() : data(nullptr), capacity(0), size(0) {}
template<class T>
Stack<T>::Stack(int capacity_) : capacity(capacity_), size(0)
{
data = new T[capacity];
if (!data)
throw "ERROR - couldn't allocate memory\n";
}
template<class T>
Stack<T>::Stack(const Stack<T>& toCopy)
{
size = 0;
capacity = toCopy.capacity;
data = new T[capacity];
if (!data)
throw "ERROR - couldn't allocate memory\n";
for (int i = 0; i < toCopy.size; ++i, ++size)
data[i] = toCopy.data[i];
}
template<class T>
Stack<T>::Stack(Stack<T>&& toMove)
{
data = nullptr;
size = capacity = 0;
swap(*this, toMove);
}
template<class T>
Stack<T>::~Stack()
{
clear();
}
//Methods
template<class T>
int Stack<T>::getSize() const
{
return size;
}
template<class T>
int Stack<T>::getCapacity() const
{
return capacity;
}
template<class T>
void Stack<T>::clear()
{
if (data)
{
delete[] data;
data = nullptr;
}
size = capacity = 0;
}
template <class T>
bool Stack<T>::isEmpty() const
{
return size == 0;
}
template <class T>
bool Stack<T>::isFull() const
{
return size == capacity;
}
template <class T>
T Stack<T>::pop()
{
if (isEmpty())
throw"Couldn't pop - Stack is empty\n";
T toReturn = data[--size];
T* temp = new T[size];
if (!temp)
throw "ERROR - couldn't allocate memory\n";
for (int i = 0; i < size; i++)
temp[i] = data[i];
delete[]data;
data = temp;
return toReturn;
}
template <class T>
void Stack<T>::push(const T& toInsert)
{
if (isFull())
throw "Couldn't push - stack is full\n";
T* temp = new T[size + 1];
if (!temp)
throw "ERROR - couldn't allocate memory\n";
for (int i = 0; i < size; i++)
temp[i] = data[i];
temp[size++] = toInsert;
delete[]data;
data = temp;
}
template<class T>
T Stack<T>::top() const
{
if (isEmpty())
throw"Couln't return top - Stack is empty\n";
return data[size - 1];
}
//Operators
template<class T>
Stack<T>& Stack<T>::operator=(const Stack<T>& toAssign)
{
if (data)
delete[]data;
Stack<T>help = toAssign;
swap(*this,help);
return *this;
}
template<class T>
Stack<T>& Stack<T>::operator=(Stack<T>&& toMove)
{
if (data)
delete[]data;
data = nullptr;
size = capacity = 0;
swap(*this, toMove);
return *this;
}
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
bool operatorsPrecedenceCheck(string& str, Stack<char>& stk, const char& ch)
{
if (ch == '+' || ch == '-')
{
Stack<char> help(stk.getSize());
char stkTop = '\0';
if (!stk.isEmpty())
stkTop = stk.top();
while (!stk.isEmpty() && stkTop!='(') //Not mentioned but required
{
stkTop = stk.top();
if (stk.top() == '*' || stk.top() == '/')
{
str += stk.pop();
str += ' ';
}
else
help.push(stk.pop());
}
while (!help.isEmpty())
stk.push(help.pop());
stk.push(ch);
return 1;
}
else if (ch == '*' || ch == '/')
{
stk.push(ch);
return 1;
}
return 0;
}
bool parenthesisCheck(string& str, Stack<char>& stk, const char& ch)
{
if (ch == '(')
{
stk.push(ch);
return 1;
}
else if (ch == ')')
{
while (stk.top() != '(')
{
str += stk.pop();
str += ' ';
}
stk.pop();
return 1;
}
return 0;
}
string infixToPostfix(const string &exp)
{
string str = "";
Stack<char> stk(exp.length());
bool actionExecuted = 0;
for (int i = 0; i < exp.length(); ++i)
{
actionExecuted = parenthesisCheck(str, stk, exp[i]);
if (!actionExecuted)
actionExecuted = operatorsPrecedenceCheck(str, stk, exp[i]);
if (!actionExecuted)
{
if (exp[i] >= '0' && exp[i] <= '9')
{
for (; exp[i] >= '0' && exp[i] <= '9'; ++i)
str += exp[i];
str += ' ';
--i;
}
else
throw"An illegal value in the expression\n";
}
}
while (!stk.isEmpty())
{
str += stk.pop();
str += ' ';
}
str = str.substr(0,str.length() - 1); //Cause of space
return str;
}
int getValueFromString(const string& str, int &i)
{
int result = 0;
int amountOfDigits = 0;
while (str[i+amountOfDigits] >= '0' && str[i+amountOfDigits] <= '9')
++amountOfDigits;
for (int j = pow(10,amountOfDigits-1); j>=1; j /= 10)
result += (str[i++] - '0') * j;
--i;
return result;
}
float calcPostfix(const string& str)
{
Stack<int>stk(str.length()/2);
for (int i = 0; i < str.length(); i += 2)
{
if (str[i] >= '0' && str[i] <= '9')
stk.push(getValueFromString(str, i));
else if (str[i] == '/')
stk.push((float)1 / stk.pop() * stk.pop());
else if (str[i] == '*')
stk.push(stk.pop() * stk.pop());
else if (str[i] == '+')
stk.push(stk.pop() + stk.pop());
else if (str[i] == '-')
stk.push(-1 * (stk.pop() - stk.pop()));
else
throw"Unknown Operator in Calculating Postfix\n";
}
return stk.top();
}
int main()
{
try
{
string exp;
cout << "enter an infix expression as a string" << endl;
cin >> exp;
string postfix = infixToPostfix(exp);
cout << postfix << endl;
cout << calcPostfix(postfix) << endl;
}
catch (const char* exc)
{
cout << exc;
exit(-1);
}
catch (...)
{
cout << "Unknown Error has occured\n";
exit(-1);
}
return 0;
}
Я попытался написать программу на CPP, которая получает инфиксное выражение, преобразует его в постфиксное выражение и затем вычисляет это выражение. -- Если это ваша цель, почему бы не использовать std::stack вместо написания своего? Логику инфиксного -> постфиксного выражения все еще необходимо написать, но без необходимости микроуправления классами стека.
@CSStudent — Если код работает «по-другому», значит, он вообще не работает.
@PaulMcKenzie Потому что это обычное назначение структур данных? Учащимся необходимо написать стек, и им дается задание, в котором он используется. Если вы намерены поставить под сомнение логику профессора, спрашивать ОП — неправильный подход.
@CSStudent — В любом случае используйте std::stack при первом написании кода. Почему? Потому что, по всей вероятности, вы делаете со своим классом Stack что-то, что можно было бы легче обнаружить, если бы вы изначально использовали std::stack. Если вы используете Visual C++, среда выполнения отладки обнаружит ошибки такого типа. Как только вы получите код, работающий с std::stack, снова представьте самодельный класс стека.
@CSStudent — Если такой простой код дает два разных результата от двух разных компиляторов, то в 99,9% случаев код неисправен.
Причиной обычно является неопределенное поведение: https://stackoverflow.com/questions/367633/what-are-all-the-common-undefined-behaviours-that-a-c-programmer-should-know -а
@CSStudent - Я предлагаю вам придерживаться g++ и заставить его работать, поскольку вы знаете, что можете воспроизвести там испорченные результаты. Как только вы заставите его работать с внесенными вами изменениями, внесите те же изменения для Visual C++. Кроме того, я не знаю, написал ли ваш учитель класс Stack, но есть одно предложение — не возвращать pop никакого значения. Это сделает класс Stack более похожим на std::stack. Другими словами, если вам нужно верхнее значение в стеке, вы должны вызвать top(), чтобы получить его.
Более простой способ написать стек — обернуть другую структуру, например вектор или связанный список. По крайней мере, когда я преподавал, стопка и очередь были простыми домашними заданиями. Связанный список из предыдущего задания был тщательно протестирован, и стек просто обернул его. Тогда во всей этой isFull() ерунде нет необходимости. Совет по использованию std::stack позволит вам тщательно протестировать ваш инфиксно->постфиксный код. Как только все будет готово, вы сможете вернуть свой собственный стек и устранить неполадки в нем. Всегда нужно разбить все на управляемые части.
@CSStudent — Что, если все, что я хочу сделать, это сохранить popping и не заботиться о возвращаемом значении? Ваш код неэффективен, поскольку pop всегда будет возвращать копию значения, а копирование может быть дорогостоящим, если тип, хранящийся в Stack, имеет дорогостоящую операцию копирования. C++ создан для повышения эффективности, и если мне нужно максимальное значение, я явно попрошу об этом, позвонив top. Еще одно различие между вашим классом и std::stack заключается в том, что top() возвращает ссылку на std::stack::top, а не копию значения.
И я не использовал связанные списки или вектор, потому что это для следующего задания.
Ниндзя на пенсии — я обновил код примерно через 1 минуту после того, как опубликовал эту проблему. Это действительно странно. ты!
@PaulMcKenzie - После этого назначения мы должны научиться использовать std::stack, поэтому мы узнали, как использовать стек и неясно, как он реализуется. Но спасибо за предложения. Думаю, мне следует написать свой собственный стек, более похожий на std::stack. Подробности я посмотрю позже.
@sweenish -- Спасибо, ты был прав, проблема в моем стеке. Завтра еще раз посмотрю. Спасибо вам всем!
@CSStudent - Похоже, проблема в вычитании. Перепишите вычитание так, как указано: сохраните верхнее значение во временном файле, затем извлеките его, затем сохраните верхнее значение в другом временном файле и извлеките его. Затем выполните вычитание, используя временные символы, вместо двух вызовов pop в одной строке.
‼Это может быть актуально. Я считаю, что проблема в том, что вы меняете стек несколько раз в одной логической строке кода, и не указан порядок, какой pop будет выполнен первым. Побочным результатом написания pop, чтобы не возвращать значение, было бы то, что вам пришлось бы сделать два (или более) явных вызова top, а затем pop, что сделало бы порядок вычитания четко определенным. Так что да, ваш класс стека ошибочен, но в каком-то смысле вы этого не осознавали.
Пройдя через это самостоятельно и протестировав, вы должны написать ответ, действительно ли изменение класса Stack дает правильные результаты (так и должно быть).
else if (str[i] == '-') stk.push(-1 * (stk.pop() - stk.pop())); -- Точнее, похоже, виноват именно этот код.
«Итак, мой вопрос: знает ли кто-нибудь, что может вызвать эту разницу? Может быть, в компиляторах что-то другое?» - Первое правило программирования: всегда твоя вина. См. также Что такое отладчик и как он может помочь мне диагностировать проблемы? и Неопределенное поведение.
@PaulMcKenzie -- Огромное спасибо! Раньше я не знал о «Порядке оценок», и это очень полезно, и теперь все это имеет смысл. Сейчас я поработаю со стеком, но просто хочу спросить, не ошибаюсь ли я, говоря, что строка else if (str[i] == '/') stk.push((float)1 / stk.pop() * stk.pop()); тоже ошибочна
да, в этой строке присутствует та же проблема (см. мой ответ), она работает в msvc, gcc и clang, но нет гарантии, что она будет такой же в других компиляторах или даже в будущих версиях этих компиляторов.
@JesperJuhl -- Спасибо, я прочту то, что актуально
@PaulMcKenzie — Могу ли я спросить, почему top() возвращает ссылку?
@CSStudent — ссылка возвращается для повышения эффективности, а также позволяет пользователю напрямую изменять верхнее значение. Если пользователю std::stack действительно нужна копия, он может сделать это явно для std::stack<T> theStack;, используя T val = theStack.top();, в противном случае T& val = theStack.top(); предоставит ссылку.
Большое спасибо @PaulMcKenzie, ты мне очень помог! Кстати, я только что получил 100 за задание. Спасибо!





В stk.pop() - stk.pop() нет никакой гарантии, что левая рука pop() произойдет первой, если вы измените на:
auto a = stk.pop();
auto b = stk.pop();
stk.push(-1 * (a - b));
Вы получите одинаковое поведение в разных компиляторах.
У вас та же проблема в (float)1 / stk.pop() * stk.pop().
Ваш исходный код с добавленной отладкой: https://godbolt.org/z/xEKzeTzvn
Исправленная версия: https://godbolt.org/z/oczef5hrj
Спасибо! Но у меня возникло несколько вопросов: во-первых, почему вы использовали auto в функции CalcPostfix? CalcPostfix вычисляет выражение, имеющее целые числа. Во-вторых, цель дополнительной отладки — позволить вам увидеть размер стека и функцию top() в каждой строке? А если это так, то зачем вам печатать размер стека и функцию top(), а не что-то еще? В-третьих, почему вы изменили \n в строках 337 (throw"Unknown Operator in Calculating Postfix\\n";) и 362 (cout << "Unknown Error has occured\\n";)?
Почему бы не использовать auto? это избавляет меня от необходимости искать типы данных. Не уверен, о чем вы спрашиваете в своем втором вопросе, я просто напечатал несколько значений, чтобы вы могли увидеть, где поведение компиляторов различается. Я не менял строку, я просто скопировал исходный код из вопроса.
Хорошо, я попытаюсь задать более ясный вопрос: зачем вам распечатывать информацию, которую вы напечатали, особенно там, где вы ее распечатали. а не где-нибудь еще в коде? И почему вы печатаете эту информацию, а не другую информацию?
@CSStudent, потому что именно эта информация используется на данном этапе программы и показывает, где что-то пошло не так.
Да, но вы решили распечатать там информацию для обнаружения ошибки? Или за то, что показал мне это?
Чтобы найти ошибку, сравнив выходные данные, я увидел, где программа расходится между clang и gcc. Конечно, лучше использовать отладчик, чем печатать значения.
Вы могли бы сказать, где программа расходится между clang и gcc, напечатав там значения, но вы не могли знать, что печать там значений приведет к обнаружению точки отклонения. Следовательно, как вы печатали значения именно там, где возникает проблема? Это была чистая удача?
@CSStudent — Это не чистая удача. Суть в том, что вы знаете, что в какой-то момент помещаете что-то в стек. Вы берете те критические точки, где вы это делаете, и записываете, что происходит. Это навык, который вам понадобится как программисту. Это не просто «Я знаю, что ошибка здесь, поэтому позвольте мне записать все», нет, это не лучший подход к этому. Вы записываете все моменты, когда ваша программа может пойти не так. Метод, который использовал АланБиртлес, — это тот, который я использовал, чтобы увидеть, что вычитание было ошибочным, и заключается в регистрации того, что помещалось в стек.
Постфиксная строка была одинаковой для обоих компиляторов, поэтому проблема должна была быть в calcPostfix, тогда я просто инструментировал всю функцию.
Хорошо, спасибо. Кстати, я только что закончил и получил 100. Огромное спасибо!
Спасибо @PaulMcKenzie, я спросил то, что просил, чтобы лучше узнать, как достичь этого навыка.
Кстати, последнее: в следующем коде я получаю предупреждение C6011. const char *stkTop = nullptr; if (!stk.isEmpty()) stkTop = &stk.top(); while (!stk.isEmpty() && *stkTop!='(') {} Получаю ли я предупреждение, потому что он не знает, равен ли &stk.top() значению nullptr?
Предупреждение, вероятно, выдается потому, что вы условно устанавливаете stkTop в операторе if, поэтому возможно, что stkTop все еще остается nullptr, когда выполняется цикл while.
@PaulMcKenzie — Да, но он назначает stkTop=&stk.top(); if (!stk.isEmpty). И в цикле while это будет работать *stkTop!='(' только в том случае, если !stk.isEmpty). Следовательно, он будет разыменовывать stkTop только в том случае, если !stk.isEmpty, а если ``!stk.isEmpty`, это означает, что stkTop уже назначен с помощью &stk.top() .
Компилятор знает только то, что происходит во время компиляции. Он не смотрит на то, что произойдет во время выполнения. То же самое: int foo(int x) { if (x > 0 && x <= 100) return 1; else if (x >100) return 2; } и предупреждение о том, что не все пути возвращают значение, хотя во время выполнения вы вызываете foo(1);.
Компилятор может определить, что stk.isEmpty() не меняется между двумя его вызовами и, следовательно, stkTop всегда будет присваиваться, но это, вероятно, более глубокий анализ, чем большинство компиляторов, самый простой способ отключить предупреждение - переместить while в тело оператора if, это может даже сделать ваш код немного более эффективным.
@AlanBirtles - Спасибо, теперь я понял концепцию. Спасибо и за подсказку.
Я предлагаю исправить ваш код и предоставить правильный минимально воспроизводимый пример.