今天面試C++,機試面試官看完代碼說代碼結構混亂?
編程小白,準備轉行IT,今天面試C++崗位,面試題目是:
1、寫一個簡單的計算機實現加減乘除
2、使用指針遍曆數組
3、使用二維指針,函數指針
這是需要上機的,我大概做了&>1個小時,代碼如下(VS2012 控制台項目,所以有個頭):
面試官看完我的代碼,也不知道有沒運行,說結構混亂!!!
我沒好意思問他哪裡寫的不對,這樣顯得我很low!結果可想而知,求知乎大神幫我看看這段代碼,指正一下謝謝!
---------------------------7.18----------------------
感謝各位大佬的指導!我現在覺得應該要用堆棧和遞歸來做,當時也沒想這麼多,覺得按要求輸出就好了,真的太low了。
有評論說到大話設計模式,我正在學習中,挺好的適合我這種菜鳥,我這個代碼居然是書里第一個反面教材!這本感覺比設計模式通俗多了,設計模式反正我看不下去。謝謝各位!
--------------------------7.18下班-------------------
今天擼了大半本大話設計模式,收穫還是有的。龍書買了很久,下個月再看吧。現在基本上一周擼一本書,慢慢找狀態了。雖然我30了沒有開發經驗,不過我是以興趣為驅動力。另外問題被舉報了,既然我已經得到問題答案了,我就把問題關了,感謝編程大佬們指導。
下面是摘自Programming -- Principles and Practice Using C++ by Bjarne Stroustrup
建議你去看看人家是怎麼寫的caculator,代碼也就200行不到。有算術優先順序,支持括弧。
/*
*Caculator Parser
*
* expression
* term
* expression + term
* expression - term
*
* term
* primary
* term * primary
* term / primary
*
* primary
* number
* "("expression")"
*
* number
* float-pointing-literal
*/
#include "../std_lib_facilities.h"
class Token
{
public:
Token(char ch)
: kind(ch), value(0){};
Token(char ch, double val)
: kind(ch), value(val){};
char kind;
double value;
};
class Token_stream
{
public:
Token_stream() : full(false), buffer(0){};
Token get();
void putback(Token c);
private:
bool full;
Token buffer;
};
void Token_stream::putback(Token t)
{
buffer = t;
full = true;
}
Token Token_stream::get()
{
if (full)
{
full = false;
return buffer;
}
char ch;
cin &>&> ch;
switch (ch)
{
case (:
case ):
case +:
case -:
case *:
case /:
case q:
case ;:
return Token(ch);
case .:
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
cin.putback(ch);
double val;
cin &>&> val;
return Token(8, val);
default:
error("Bad Token");
}
}
Token_stream ts;
double expression();
double primary()
{
Token t = ts.get();
switch (t.kind)
{
case (:
{
double d = expression();
t = ts.get();
if (t.kind != ))
error(") expected");
return d;
}
case 8:
return t.value;
default:
error("primary expected");
}
}
double term()
{
double left = primary();
Token t = ts.get();
while (true)
{
switch (t.kind)
{
case *:
left *= primary();
t = ts.get();
break;
case /:
{
double val = primary();
if (val == 0)
error("divide by zero");
left /= val;
t = ts.get();
break;
}
default:
ts.putback(t);
return left;
}
}
}
double expression()
{
double left = term();
Token t = ts.get();
while (true)
{
switch (t.kind)
{
case +:
left += term();
t = ts.get();
break;
case -:
left -= term();
t = ts.get();
break;
default:
ts.putback(t);
return left;
}
}
}
int main()
{
cout &<&< "welcome to our simple caculator." &<&< endl;
cout &<&< "please enter expressions using float numbers." &<&< endl;
try
{
double val;
while (cin)
{
Token t = ts.get();
if (t.kind == q)
break;
if (t.kind ==
cout &<&< "=" &<&< val &<&< endl;
else
ts.putback(t);
val = expression();
}
keep_window_open();
}
catch (exception e)
{
cerr &<&< e.what() &<&< endl;
keep_window_open();
return 1;
}
catch (...)
{
cerr &<&< "exception
";
keep_window_open();
return 2;
}
return 0;
}
這個題真的是什麼水平的人寫什麼樣的代碼
我的話可能先十五分鐘封裝一下輸入流做出來一個token stream,然後十分鐘寫個遞歸下降把表達式處理成樹,最後二十分鐘寫個簡單的解釋環境運行。
其中大概會用到遞歸下降,正則表達式轉dfa狀態機等演算法,可能用到visitor設計模式,可能用到標準庫的智能指針和function,C++語言的lambda表達式。
面試官要用函數指針和二級指針,有點low感覺確實不太好。
自頂向下寫一個應該能拿高分。但是函數指針這裡我不知道怎麼用。
BNF
E -&> T +- E | T
T -&> F * / T | F
F -&> (E) | num
用大概一個半小時擼了一個遞歸下降+visit語法樹的版本(手速還是太慢),要求輸入的是合法的算數表達式(基本沒做錯誤處理),支持空格和tab以及算數優先順序。
不支持括弧和輸入負數。括弧好支持,再加二三十行就能解決,負號稍微麻煩點,需要處理歧義。
回手機上看代碼慘不忍睹,電腦上效果好很多。
#include &
#include &
#include &
#include &
#include &
#include &
using namespace std;
vector&
{
vector&
string buffer;
for(auto ch:str)
{
switch (ch)
{
case +:case -:case *:case /:
if(!buffer.empty())
res.push_back(buffer), buffer.clear();
res.push_back("");
res.back().push_back(ch);
break;
case 0:case 1:case2:case3:case4:case5:case 6:case7:case8:case9:
buffer.push_back(ch);
break;
case : case :
if (!buffer.empty())
res.push_back(buffer),buffer.clear();
break;
default:
throw "error input character.";
}
}
if (!buffer.empty())
res.push_back(buffer);
return res;
}
struct Op;
struct Num;
struct Visitor
{
virtual ~Visitor() = default;
stack&
//這裡不繼承其實可以不用virtual的
virtual void Visit(Op op);
virtual void Visit(Num num);
};
struct Expr
{
virtual ~Expr() = default;
virtual void Accept(Visitor visitor) = 0;
};
struct Op :Expr
{
void Accept(Visitor visitor)override { visitor.Visit(*this); }
explicit Op(char op, shared_ptr&
char op;
shared_ptr&
};
struct Num :Expr
{
explicit Num(int num) :num(num) {}
void Accept(Visitor visitor) override { visitor.Visit(*this); }
int num;
};
//Expr = Factor { (+|-) Fcator }
//Factor = num { (*|/) num }
shared_ptr&
shared_ptr&
{
auto res = ParseFactor(expr, idx);
while (idx&
}
}
return res;
}
shared_ptr&
{
shared_ptr&
while (idx&
res = make_shared&
}
return res;
}
//題目要求用函數指針,我這裡就強行回調一下吧……
void Calculate(std::stack&
{
auto right = stack.top();
stack.pop();
auto left = stack.top();
stack.top() = operation(left, right);
}
void Visitor::Visit(Op op)
{
op.left-&>Accept(*this);
op.right-&>Accept(*this);
switch (op.op)
{
case +:
Calculate(stack, [](int a, int b) {return a + b; }); break;
case -:
Calculate(stack, [](int a, int b) {return a - b; }); break;
case *:
Calculate(stack, [](int a, int b) {return a*b; }); break;
case /:
Calculate(stack, [](int a, int b) {if (b == 0)throw"cannot devide 0"; return a / b; }); break;
default:
throw "unknown operation";
}
}
void Visitor::Visit(Num num)
{
stack.push(num.num);
}
int main()
{
string input;
getline(cin, input);
try
{
auto tonkens = GetToken(input);
int idx = 0;
auto tree = ParseExpr(tonkens, idx);
Visitor visitor;
tree-&>Accept(visitor);
cout &<&< visitor.stack.top() &<&< endl;
}
catch(char* str)
{
puts(str);
}
return 0;
}
這明顯是一道題,你把它當三道題了。
雖然我覺得上機題最好應該給個輸入輸出樣例的,但是兄弟你的理解能力實在是驚到我了…
這個題應該是寫一個計算器,代碼要求用指針遍曆數組,並要求體現函數指針和二維指針。這個要求差不多已經告訴你該怎麼寫了。
如果我來寫的話,應該是用戶輸入一個字元串作為算式,指針遍曆數組,對算式進行合法性檢查(1.非法字元 2.算式不完整 3.空格縮進 4.除數為0 5.數值大小),然後根據四則運算以及括弧的優先順序對這個算式進行解析。
首先去括弧,申請一個棧將算式放入棧中(出棧的時候遇到後括弧找前括弧),再分別申請棧保存去掉括弧的部分算式(這裡就能用到二維指針)。
然後寫個輸入字元串進行四則運算輸出結果的方法,分別將各棧中保存的部分算式算完,再各部分結果代入總算式再次調用這個函數得出最終結果就行了(結果可以用函數指針獲取)。
何況這題也不需要用到運算符重載什麼的體現C++特點的東西,面試官說你結構混亂可能是他都不知道你在寫什麼……
只看代碼風格:
這一堆if-else不如switch
雙目運算符前後沒空格,擠在一起不美觀
代碼都在main里tag_x的變數命名風格這樣看來似乎的確不太行。
有些東西的確是繁文縟節,但大項目里還少不了這個。另外……面試題目是簡單計算器,你就真的寫得這麼簡單啊,這麼耿直……你那一串else if看著辣眼睛
同病相憐啊!我也是從這裡走過來的。
C++只是一門語言,單學C++難找到工作,至少需要了解常用的數據結構與演算法、並發、網路編程、設計模式才能說具備找工作的條件。多看書多動手,不要急功近利,C++的學習曲線相較與java是又陡又長,不過投入與產出是成正比的,加油!推薦你幾本書吧
精讀:《C和指針》《C++ Primer》第五版《數據結構與STL》或《數據結構C++語言描述》《設計模式》《C++ 並髮指南》泛讀了解:
《深度探索C++對象模型》《C++ template中文版》《C++標準庫》第二版
《深入理解計算機系統》《圖解TCP/IP》《道德經》具體方向上的可自行搜索,遊戲、GUI、機器學習等等。有時間把計算機專業的所有課程都了解一下你的面試官還告訴你結構混亂,換我直接說:「你走吧」
其實最簡單就是調用系統的calc。。
寫個毛啊
面試官說你的代碼有問題的時候,你應該讓他詳細的點評一下。這沒有什麼low不low的,學習都是一步一步積累的,了解哪裡不足才會有進步。
另外,我個人覺得這個面試官有問題,或者說有他自己的想法吧。
代碼結構混亂只是小問題,關鍵是你明顯沒有理解這道題想要考察的東西。
就好比寫一篇作文,你跑題了沒有得分,他不提這個主要原因,非要說你字寫得不好看。
———————————————————————————————————————————
扯點題外話,話說別的專業轉IT還真有往C++轉的啊。
輸入和邏輯混在一起還不混亂?
這個題我猜主考官是考棧的使用,利用棧實現中綴表達式轉後綴表達式吧,不過其實用遞歸寫計算表達式最簡單了。
我覺得說這是一道題的在理掰開了說就是用數組做一個棧出來(考察數據結構知識),棧頂元素的調用靠指針實現(考察對C++指針理解),用這個棧實現四則運算功能(考察數據結構知識),這個功能寫在函數裡面,主函數靠調用這個計算器(考察對C++函數指針的理解)。
我覺得吧,敏而好學不恥下問是傳統美德,何況你都說自己是編程小白了,小白就是low,為什麼要遮遮掩掩「怕自己顯得很low」呢,心態不對。
這道題的本意其實是這樣的:
寫一個計算器程序,該程序接受一個字元串輸入,該字元串為包含數值字面量和加減乘除運算符的表達式,要求輸出該表達式的值。
所以題主應當是理解錯了這道題的思路。
同時,從題主給出的程序來看,結構確實是有些混亂的,比如說,那些else-if完全可以改掉嘛,不說可以改為switch-case結構,還可以把加減乘除四個運算寫成4個單獨的函數,把這些函數的指針保存到一個數組,然後以運算符為下標索引該數組,調用函數進行求值等等。
題主還需努力。