strcpy的幾個焦點集中在一下幾點:
是否效率優先
NULL指針處理
重疊(源和目的相同是其一種特殊情況)處理
字符串結束字符
字符類型
效率優先版本
extern "C" char * unchecked_strcpy(char *d,const char *s )
{
//the simplest and most efficient way
assert(d && s);
char* tmp = d;
while( *s)
*tmp++ = *s++;
*tmp = '/0';
return d;
}
這個版本已經沒有什麼好改動的地方了,在效率方面它可以得滿分。注意extern “C”,不是隨便寫上去的,沒錯,它可以是一個C函數。作爲一種低層程序庫,完全有理由要求程序員自己控制好數組越界,空指針,野指針的情況,並將這些規格明確寫於說明書上。
安全優先版本
extern "C" char * unchecked_strcpy_backward(char *d, const char *s )
{
//the simplest and most efficient way
assert(d && s);
size_t size = strlen(s);
char* tmp = d+size;
s+=size;
size++;
while( size )
{
*tmp-- = *s--;
--size;
}
return d;
}
extern "C" int strcpy_safe(char *d,
const char *s )
{
//return a exit code
//handle the overlapping situation
if(d == s)
return 1;//source and destination are the same
if(!d || !s)
return 2;//null string
size_t size = strlen(s);
//non-overlapped
if(d < s || d>s+size)
{
unchecked_strcpy(d,s);
return 0;//no error
}
//overlapped
unchecked_strcpy_backward(d,s);
return 0;//no error
}
unchecked_strcpy_backward實現逆向字符串拷貝。
strcpy_safe,處理了幾種常見的情況,所以這樣的效率是不高的。當源和目的相同時直接返回錯誤碼1,但是我們想想在實際情況中,這種概率又能有多高?串指向空時,直接返回錯誤碼2。重疊時,逆向拷貝,然而逆向拷貝一定就是正確的結果嗎?不見得。只是我們按照普通思維認爲這是正確的。
即使這樣,還有兩個安全問題無法解決,源或目的字符串是野指針,目的字符串的緩衝區溢出。從理論上說,這兩種情況超越了C++的控制。
所以,從安全意義講,永遠沒有一個安全的strcpy實現。我們不本着信任程序員的態度開發程序,永遠也沒有安全可言,這樣只會給自己帶來無盡煩惱。
完全定製版本
template<typename char_type>
struct NonNullStringChecker
{
static bool check(char_type* p1 , char_type* p2)
{
return true;
}
static void handle(char_type* p1 , char_type* p2)
{
}
};
template<typename char_type>
struct NonOverlappingChecker
{
static bool check(char_type* d,char_type* s)
{
return false;
}
static void handle(char_type* d,char_type* s)
{
}
};
template<
typename char_type = char,
char_type end_char = '/0',
class NullStringChecker = NonNullStringChecker<char_type>,
class OverlappingChecker = NonOverlappingChecker<char_type>
>
class strcpy_template
{
public:
char_type* operator()(char_type* d, char_type* s)
{
if(!NullStringChecker::check(d,s))
NullStringChecker::handle(d,s);
if(OverlappingChecker::check(d,s))
{
OverlappingChecker::handle(d,s);
}
else
{
char_type* tmp = d;
while( *s != end_char)
*tmp++ = *s++;
*tmp=end_char;
return d;
}
return 0;
}
};
是的,這是一個functor template,不是一個function template,因爲function template不支持缺省參數。
從字符類型,字符串結束符號,空指針處理,重疊處理4個方面參數化這個類模版。當然,不能強迫所有人的模版技術都能達到Alexandrescu的水平(它把policy class和meta template programming在loki中運用的出神入化),所以必須提供一組缺省的參數給普通用戶使用。
從程序看出NullStringChecker和OverlappingChecker都要提供check和handle接口。爲了更安全的目的,你可以在check中做出各種條件檢查,並在handle中做處理或者拋出異常。
這個應該是最具有彈性的版本,如果面試者看重template技術,那麼這個版本一定能得一個高分。
總結
沒有完美的解決方案。
公司的考察編程風格,思維嚴密程度,語言細節等等都太片面。編程風格是個人問題,強迫別人改變是一種不禮貌和粗魯的行爲,從深層次看,是一種不尊重人權的做法。而思維嚴密無非就是做錯誤檢查,難道效率和安全是矛盾的,這個道理沒人明白?在沒有要求效率或者安全優先的情況下,任何一種實現方法都是正確的。考察思維的嚴密程度使用這種方法實在是愚蠢至極。如果要考細節,我倒想問問他們一些問題:一個編譯單元的定義是什麼?成員函數模版全特化版本在沒有被調用時,編譯器會爲其生成代碼嗎?export關鍵字的用途?
我同意侯捷的“天下之事,必做於細”。但是,在編程方面未必可以以小見大。Lippman的《Inside C++ Object》和Alexandrescu的《Modern C++ Design》錯誤頻繁,仍然不影響是經典。Scott Meyers到現在還沒有準備寫《Effective Template》,仍然不影響他是一個大師。無聊的人才以細節定成敗。