如何寫出一個好的程式
阿新 • • 發佈:2019-02-13
我們以memmove函式作為例子,來看我們如何一步步精簡和優化你的程式碼。
寫之前我們應該知道memmove這個函式有什麼用?
他就是一個按位元組的拷貝函式,把目標的內容按位元組拷貝到你指定的地址,他和strcpy
不同的地方就是他是按位元組拷貝,他可以拷貝任意的型別的。
現在我們考慮一下,開始第一種方案,只完成拷貝功能
NO'1
void MyMemMove(char *dst,char *src,int count)
{
while(count--)
{
*dst++=*src++;
}
}
如果可以寫出這個說明,你的水平對於大學的C語言來說就是可以及格的,對於那些白卷的 和函式亂宣告的你已經算是很厲害的,但是你距離精英還是有距離的。因為這個程式是可
NO'2
void MyMemMove(void *dst, void *src , int count)
{
while (count --)
{
*( char *)dst = *(char *) src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
}
我們可以發現它使用了void*現在這個函式可以接受這種各樣的型別,然後經過強制
NO'3
我們給src加上了一個const,其實這個const說到底是一個約束程式設計師的關鍵詞,他可以防止你在後 面更改src的值現在再來考慮這樣一種情況,有使用者這樣呼叫庫:MyMemMove(NULL,src, count), 這是完全可能的,因為一般來說這些地址都是程式計算出來的,那就難免會算錯,出現零地址或者 其它的非法地址也不足為奇。可以預料的是,如果出現這種情況的話,則程式馬上就會down掉。事 實上在標準庫裡已經存在解決這些功能的巨集:assert(關於assert我還是會有部落格介紹的),而且 更加好用,它還可以在定義DEBUG時指出程式碼在那一行檢查失敗,而在沒有定義DEBUG時完全可以把 它當作不存在。void * MyMemMove(void *dst, const void *src, int count ) { assert(dst ); assert(src ); void *ret = dst ; while (count --) { *( char *)dst = *(char *) src; dst = (char *)dst + 1; src = (char *)src + 1; } return ret; }
NO'4
void * MyMemMove(void *dst, const void *src, int count )
{
assert(dst );
assert(src );
void * ret = dst ;
if (dst <= src || ( char *)dst >= ((char *) src + count )) {
while (count --) {
*( char *)dst = *(char *) src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
}
else {
dst = (char *)dst + count - 1;
src = (char *)src + count - 1;
//逆序拷貝
while (count --) {
*( char *)dst = *(char *) src;
dst = (char *)dst - 1;
src = (char *)src - 1;
}
}
return(ret);
}
我原來看到這裡也是很吃驚,後面那麼長的東西是幹什麼的,其實寫到NO'3的時候你已經很優秀了
,但是寫程式最重要的是注意程式的嚴密性,曾經看到過一句話,其實寫一個程式主幹道用的程式碼
一般少於個個分枝用來完善的程式碼。寫出一個程式碼你要保證它的使用範圍,保證它不會出BUG。所以
得顧及個個方面,而記憶體這東西是最容易出問題的。
請看下面的程式碼
# define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<Windows.h>
#include<assert.h>
void * MyMemMove(void *dst, const void *src, int count)
{
assert(dst);
assert(src);
void *ret = dst;
while (count--)
{
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
return ret;
}
int main()
{
char p[256] = "hello,world!";
MyMemMove(p + 1, p, strlen(p) + 1);
printf("%s\n", p);
system("pause");
return 0;
}
我們一起來看看他的執行結果
這是為什麼呢?
呼叫這個函式的記憶體分佈有4種情況,分別如下
現在我們例子中呼叫方法圖中是第三個,這個hhhhhhhhhh是如何發生的呢? 我們開始分析一下 首先讓指標分別指向src指標和dst指標指向p和p+1; 然後開始拷貝,現在src第一個值為h,既p的第一個值為h,然後dst經過拷貝也為h,既p+1為h。 接下來的一步就是把p+1(src++)的值拷貝給p+2(dst++),結果p+2的值還是h,然後就這樣一直 下去,知道src結束,所有拷貝過去的值都是h,也就成了我們的hhhhhhhhh. 但是我們應該怎麼解決呢? 看著第三張圖,如何拷貝才能保證給dst賦值,沒錯是倒序拷貝,當拷貝src後面的內容時,dst開 闢的那些空間剛剛不重疊。所以我們就有了最後一個版本。完美的解決掉所有問題。 寫程式碼的人很多,寫出好程式碼的就沒有多少了,一個好的程式碼需要你面面俱到,有人說過一個好的 程式設計師一定是是一個好的偵探,所以寫程式碼一定要細心,從我做起,寫這些東西也在警示我自己。