上過大學的計算機系的莘莘學子們,遺憾啊,在國產的教科書的惡臭的薰陶下,四年的青春流逝,悲哀啊!很幸運在大學中有很多研究gnu的人,這些人是幸運的,在學會了hello world之後就和國產教科書分道揚鑣了。
前面的文章分析了strstr的各種實現,本文分析兩個更加普遍的函式,這就是strcpy以及memcpy,strcpy是各大計算機型別考試或者面試中幾乎必不可少的考點,國產教科書或者微軟官方幾乎在某方面達成了一致,**的藝術性僅僅停留在可讀性這種展現給人的形式上,於是很多的越來越短的**產生了,將while換成for就可以節省最少一行**,然而編譯之後的二進位制形式中,機器卻沒有得到任何好處,諸如*dest++ = *src++之類的**太氾濫了,很多面試官也會因為應聘者寫出這類**而得出該應聘者基本符合要求的結論,這些人都走進了乙個誤區,**最終其實是讓機器執行的,越高效越好。我最近應聘時也面臨了這個考題,令我十分欣慰的是,在我沒有看過gnu的strcpy**的前提下我竟然寫出了與之類似的**,當面試官問我為何這麼寫的時候,我說出了這麼寫可以每次迴圈最少減少一條指令的執行,這也許和我多年來研究linux和bsd等**有關,微軟似乎十分害怕開發者得了甚解,於是要麼用可讀性很差的彙編寫庫函式(對應的c**也不是很高效),要麼就用很多巨集把你繞來繞去(也許是我的誤解,實際上windows巨集很多時候是為了相容性而設定的),微軟希望人們對它提供的介面之上的世界熟悉到不能再熟悉,而對介面之下的世界一無所知,然而我們並不買賬!我在面試時寫的strcpy我就不寫了,這裡直接寫出gnu的**:
char * strcpy (char *dest, const char *src)
reg_char c;
char *__unbounded s = (char *__unbounded) check_bounds_low (src);
const ptrdiff_t off = check_bounds_low (dest) - s - 1;
size_t n;
do //注意此演算法利用了程序平坦的記憶體模型,虛擬記憶體平坦鋪開,於是任意兩個指標的差就是二者之間的距離。
while (0)
#define page_offset(n) ((n) & (page_size - 1)) //頁面對齊
由於對齊操作可以有效利用硬體cache line,於是對齊後的操作將是十分高效的,但是如果乙個資料段的大小還不足乙個對齊資料段的大小,比如對不足乙個頁面的資料進行頁面對齊就沒有意義淨增加了管理開銷,,於是經過權衡之後,**可能並不是很直觀,但是無論如何,思想還是很重要的,下面請看gnu的memcpy:
#define op_t_thres 8
void * memcpy (void *dstpp, const void *srcpp, size_t len)
unsigned long int dstp = (long int) dstpp; //將記憶體盡可能轉為大的資料型別,可以用機器內建的操作指令進行操作,比如movl,movb
unsigned long int srcp = (long int) srcpp;
if (len >= op_t_thres) //只有在資料長度大於乙個閥值時才值得這麼做
len -= (-dstp) % opsiz; //減去將要拷貝的資料長度
byte_copy_fwd (dstp, srcp, (-dstp) % opsiz); //dstp前直接加負號然後與opsiz取模得到了到下乙個word對齊的資料段的偏移,首先要將此段資料拷貝之後才可以按照word對齊來拷貝資料
page_copy_fwd_maybe (dstp, srcp, len, len);
word_copy_fwd (dstp, srcp, len, len);
byte_copy_fwd (dstp, srcp, len); //收尾拷貝操作
return dstpp;
實際上在很多機器上根本就沒有頁面拷貝的指令,很多機器上最大的操作指令就是word操作指令,比如在x86機器上的頁面拷貝巨集實際上就是乙個空操作,很多的操作都是通過word操作來完成的。
gnu的**是很難懂的,但是處處充滿了玄機,很有意思,如果有機會除錯一下是很有趣的,如果論可讀性而言,bsd的libc的**是很好的,然而gnu的**從來就不是讓讀的,它的高效性似乎已經無庫可及了。實際上string的庫函式都可以用類似strlen的演算法來實現,char在32位系統上就乙個位元組,操作乙個位元組實際上是很低效的,於是可以轉化為操 作多個位元組來實現string的庫函式,這種演算法有個前提就是string字串在記憶體中是連續的,人可以將之看做乙個位元組乙個位元組的字元組成的串,但是 既然是連續的,那麼機器完全可以將之看做乙個word乙個word的word組成的串,需要做的僅僅就是在操作word的時候判斷是否到達串尾,而到達串 尾這個判斷很簡單,只要乙個word中的某個位元組全部為0就可以了,這個判斷的實現在strlen中是很清晰的,於是任意操作小資料型別的操作都可以轉換 為操作大資料型別的操作,這樣會使機器少執行幾輪迴圈,優化的前提是機器本身對轉換後的大資料型別提供原生支援而不是模擬支援,比如word就被x86所 支援,int,long long等都被支援,而更大的頁面大小的資料型別就不被指令直接支援。
strcpy以及memcpy的實現
上過大學的計算機系的莘莘學子們,遺憾啊,在國產的教科書的惡臭的薰陶下,四年的青春流逝,悲哀啊!很幸運在大學中有很多研究gnu的人,這些人是幸運的,在學會了hello world之後就和國產教科書分道揚鑣了。前面的文章分析了strstr的各種實現,本文分析兩個更加普遍的函式,這就是strcpy以及me...
strcpy與memcpy的區別。
strcpy與memcpy的區別。考點 字串複製與記憶體複製之間的區別。出現頻率 解析strcpy和memcpy主要有以下3方面的區別。複製的內容不同。strcpy只能複製字串,而memcpy可以複製任意內容,例如字元陣列 整型 結構體 類等。複製的方法不同。strcpy不需要指定長度,它遇到字串結...
strcpy和memcpy的區別
strcpy和memcpy都是標準c庫函式,它們有下面的特點。strcpy提供了字串的複製。即strcpy只用於字串複製,並且它不僅複製字串內容之外,還會複製字串的結束符。已知strcpy函式的原型是 char strcpy char dest,const char src memcpy提供了一般記...