標籤:

今天面試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& GetToken(const string str)
{

vector& res;
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& 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& left, shared_ptr& right) :op(op), left(left), right(right){}
char op;
shared_ptr& left, right;
};
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& ParseFactor(const vector& expr, int idx);
shared_ptr& ParseExpr(const vector& expr, int idx)
{
auto res = ParseFactor(expr, idx);
while (idx&(op, res, right);
}
}
return res;
}
shared_ptr& ParseFactor(const vector& expr, int idx)
{
shared_ptr& res = make_shared&(atoi(expr[idx++].c_str()));
while (idx&(atoi(expr[idx++].c_str()));
res = make_shared&(op, res, right);
}
return res;
}
//題目要求用函數指針,我這裡就強行回調一下吧……
void Calculate(std::stack& stack,std::function& operation)
{
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個單獨的函數,把這些函數的指針保存到一個數組,然後以運算符為下標索引該數組,調用函數進行求值等等。

題主還需努力。


分頁阅读: 1 2