memcpy和strcpy區別,以及源代碼學習

因爲自己主要用到得是C語言,所以這裏只學習C語言的,C++應該是一樣的。

常見用法和區別

頭文件

#include<string.h>

聲明

char *strcpy(char* dest,const char* src);
void *memcpy(void*dest, const void *src, size_t n);

兩者區別

strcpy只能拷貝字符串。strcpy遇到 '\0'拷貝結束(當dest的內存長度大於src的長度,拷貝時將'\0’帶過去,'\0’後面的內容不再拷貝);如果當dest的內存長度小於src的長度,那麼會造成內存溢出等問題,所以有了strncpy函數,就是增加了長度控制。

memcpy從源src所指的內存地址的起始位置開始拷貝n個字節到目標dest所指的內存地址的起始位置中,可以拷貝任意數據。除了拷貝字符串外,還能拷貝其他的類型的數據,比如結構體,整型數據,類等。memcpy拷貝時需要帶有長度參數。

Linux man手冊關於二者的描述

strcpy

    The strcpy() function copies the string pointed to by src, including the terminating null byte ('\0'), to the buffer pointed to by dest. The strings may not overlap, and the destination string dest must be large enough to receive the copy. Beware of buffer overruns!  (See BUGS.)
    The strncpy() function is similar, except that at most n bytes of src are copied. Warning: If there is no null byte among the first n bytes of src, the string placed in dest will not be null-terminated.
    If the length of src is less than n, strncpy() writes additional null bytes to dest to ensure that a total of n bytes are written.

中文翻譯:

strcpy()函數將src指向的字符串,包括終止null字節('\0')複製到dest指向的緩衝區,字符串不能重疊,目標字符串dest必須足夠大才能接收到拷貝。小心緩衝區溢出!(見缺陷)

strncpy()函數與此類似,只是最多複製了n個字節的src。警告:如果src的前n個字節中沒有null字節,放置在dest中的字符串將不會以null結尾。如果src的長度小於n, strncpy()將額外的空字節寫入dest,以確保總共寫入了n個字節。

memcpy

The  memcpy() function copies n bytes from memory area src to memory area dest.  The memory areas must not overlap.  Use memmove(3) if the memory areas do overlap.

中文翻譯:

memcpy()函數從內存區域src複製n個字節到內存區域dest。內存區域不能重疊。如果內存區域確實重疊,則使用memmove(3)。

兩個函數的例子這裏就不寫了,網上有很多。

二者的效率

網上有很多strcpy和memcpy的源代碼都是差不多的,都是一個字節一個字節的拷貝的,那memcpy(指定長度全拷貝)的效率豈不低於strcpy(遇到'\0'就結束)?和我們的瞭解有悖啊。

這裏說下個人觀點,C語言經過幾十年的發展,標準庫函數是經過一代又一代的大佬們優化過的,如果memcpy和strcpy差不多,不合常理,怕也會是個笑話。所以個人認爲那都是網上貼的自己實現的memcpy函數,可以理解爲my_memcpy函數。這裏先說下結論,memcpy的效率絕大多數要高於strcpy,具體的看後面的源碼分析。

總結

memcpy和strcpy都可以拷貝字符串。但memcpy可以拷貝任意類型,strcpy只能拷貝字符串,且在拷貝字符串的時候要清楚'\0'是否拷貝進去了,是否會發生溢出等,建議使用strncpy。

效率:絕大多數情況下memcpy的效率要高於strcpy。

安全:memcpy因爲有長度控制,相對來說更安全。但有內存重疊的風險。

思考1:既然memcpy完勝strcpy,那麼strcpy存在的意義是什麼?這個需要繼續學習。

源代碼學習

strcpy源代碼

char * __cdecl strcpy(char * dst, const char * src)
{
        char * cp = dst;
 
        while( *cp++ = *src++ ); /* Copy src over dst */
 
        return( dst );
}

很簡單,也很精煉,就是一個字節一個字節的複製。

 

memcpy源代碼

void *memcpy (void *dstpp, const void *srcpp, size_t len )
{
  unsigned long int dstp = (long int) dstpp;
  unsigned long int srcp = (long int) srcpp;
 
  if (len >= OP_T_THRES)
    {
      len -= (-dstp) % OPSIZ;
      BYTE_COPY_FWD (dstp, srcp, (-dstp) % OPSIZ);
      PAGE_COPY_FWD_MAYBE (dstp, srcp, len, len);
      WORD_COPY_FWD (dstp, srcp, len, len);
    }
 
  BYTE_COPY_FWD (dstp, srcp, len);
 
  return dstpp;
}

源碼的具體分析看文末的參考鏈接,這裏只寫一下結論:

1.對於特殊平臺,可以使用page copy的方法。由於限制條件太多,一般x86平臺下不會使用。

2.使用word copy的方法進行one word by one word進行拷貝,此處是memcpy的優化關鍵,優化的條件是拷貝地址處於對齊邊界。

3.剩餘的不能採用word copy的尾部使用one byte by one byte進行拷貝。


如上,memcpy 好點兒的平臺可以優化到按頁拷貝,一般的也是one word by one word按照機器字長(32位設備4字節,64位設備8字節)拷貝。而strcpy只能按照one byte by one byt單字節字節拷貝,所以這個效率就不用說了,自然是memcpy的效率更高。

前面說過絕大多數情況下memcpy的效率比strcpy高,這裏說的是絕大多數,也就是說還有strcpy效率高於memcpy的情況?

答案是有的,我們不妨假設一個極端情況。src內容的長度只有1個字符,需要拷貝的內容長度是1000個字節。請注意看strcpy循環結束的條件src == ’\0‘,所以strcpy只循環了兩次就完成了拷貝,而memcpy是按照長度拷貝的,所以不管src後面有沒有內容,依然拷貝1000個字節的內存,所以這種情況下,strcpy的效率是高於memcpy的。

這裏貼下strncpy的源代碼;

/* Copyright (C) 1991, 1997, 2003 Free Software Foundation, Inc.
   This file is part of the GNU C Library.
 
   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.
 
   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.
 
   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <http://www.gnu.org/licenses/>.  */
 
#include <string.h>
#include <memcopy.h>
 
#undef strncpy
 
#ifndef STRNCPY
#define STRNCPY strncpy
#endif
 
char *
STRNCPY (char *s1, const char *s2, size_t n)
{
  char c;
  char *s = s1;
 
  --s1;
 
  if (n >= 4)
	{
		size_t n4 = n >> 2;

		for (;;)
		{
			c = *s2++;
			*++s1 = c;
			if (c == '\0')
				break;
			c = *s2++;
			*++s1 = c;
			if (c == '\0')
				break;
			c = *s2++;
			*++s1 = c;
			if (c == '\0')
				break;
			c = *s2++;
			*++s1 = c;
			if (c == '\0')
				break;
			if (--n4 == 0)
				goto last_chars;
		}
		n = n - (s1 - s) - 1;
		
		if (n == 0)
			return s;
		goto zero_fill;
	}
 
last_chars:
  n &= 3;
  if (n == 0)
    return s;
 
  do
	{
		c = *s2++;
		*++s1 = c;
		if (--n == 0)
	return s;
	}
  while (c != '\0');
 
zero_fill:
  do
    *++s1 = '\0';
  while (--n > 0);
 
  return s;
}
libc_hidden_builtin_def (strncpy)

分析strncpy的源碼,關鍵代碼

zero_fill:
  do
    *++s1 = '\0';
  while (--n > 0);

  只有當n等於0時,才返回dest地址,否則即使src後面的數據爲空('\0'),依然在dest後面的內存中置0,直到複製完n個字符爲止。所以可以看到strncpy增加了長度參數提升字符拷貝的安全性,但效率卻降低了,儘管這裏的算法已經優化,但是後面置0的操作犧牲了效率和性能。

參考:

《大併發服務器內存轉換的靈活運用,memcpy的思考》

《glibc--memcpy源碼分析》

簡說GLIBC strncpy實現》

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章