strcpy以及memcpy的實現

2021-09-21 04:43:41 字數 2901 閱讀 8749

上過大學的計算機系的莘莘學子們,遺憾啊,在國產的教科書的惡臭的薰陶下,四年的青春流逝,悲哀啊!很幸運在大學中有很多研究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)

while (c != '/0');

n = s - src;

(void) check_bounds_high (src + n);

(void) check_bounds_high (dest + n);

return dest;

}另外可以用gnu的strlen中的演算法來實現,一次乙個word的copy而不是乙個char,該演算法在字串很長的時候是很高效的,一會我會分析gnu的memcpy的實現,它就用了類似的演算法,將記憶體進行分割,按照機器支援的page,word,byte的順序從大到小的對齊粒度copy,在討論memcpy之前先看看以上的strcpy中的另乙個技巧,看一下check_bounds_low,這個機制是編譯器相關的,其實就是判斷陣列越界的,當檢測到陣列下標越界的時候就會觸發乙個5號異常,由作業系統負責處理,該巨集的一般寫法是:

#define bounds_violated int $5

#define check_bounds_low(val_reg, bp_mem)/

cmpq 8+bp_mem, val_reg; /

jae 0f; /

bounds_violated; /

0:然而看看gnu的寫法,巧妙了利用了&&操作符,只有在前面為真的情況下才執行後面的,而不是將兩端的值都計算完畢之後再相與操作:

#define bounds_violated (__builtin_trap (), 0)

#define check_bounds_low(arg) /

(((__ptrvalue (arg) < __ptrlow (arg)) && bounds_violated), /

__ptrvalue (arg))

下面看看gnu的memcpy的操作,首先先看看幾個預定義的巨集:

#define op_t unsigned long int

#define opsiz (sizeof(op_t))

#define page_copy_fwd_maybe(dstp, srcp, nbytes_left, nbytes) /

do /

//拷貝整頁的資料 /

page_copy_fwd (dstp, srcp, nbytes_left, nbytes); /

} /} 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)

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提供了一般記...