strlen、strcpy、strcat等字符串處理函數的實現

最近參加不少小公司的筆試,都是關於C++開發工程師的崗位,考察的題目比較基礎。不少公司都考察了關於字符處理函數的實現,這些看起來很簡單,其實需要注意的地方還是很多的。這裏給出strlen、strcpy、strcat等函數的實現,以及指出需要注意的地方。

strlen求字符串的實際長度,其函數的原型爲:

extern unsigned int strlen(char *s);

其實現如下:

int strlen(char *s)
{
	int n=0;
	while(*s!='\0')
	{
		n++;
		s++;
	}
	return n;
}

int strlen(char *s)
{
	int n=0;
	while(*s++)//’\0’是8個比特位全0的二進制編碼,因此可以簡化
		n++;
	return n;
}

int strlen(char *s)
{
	char *p=s;
	while(*p++);
	return p-s;
}

其實上述實現都有個問題就是沒有做指針的有效性檢查,下面的幾個版本糾正了這個問題:

int strlen(const char *str){
	assert(str!=NULL);
	intlen=0;
	while((*str++)!='\0')
		len++;
	return len;
}

int	strlen(const char *str){
	assert(str);
	const char*p=str;
	while(*p++!=NULL);
	return	p-str-1;
}

int strlen(const char *str){
	assert(str);
	if(*str==NULL)
		return 0;
	else
	return (1+strlen(++str));
}
/***strlen-Find the length of a string*@s:The string to be sized*/
size_t strlen(const char *s){
	const char *sc;
	for(sc=s;*sc!='\0';++sc);/*nothing*/
	return sc-s;
}

參考:

http://baike.baidu.com/link?url=mrMiiM1wl8VBJdAXxo_SgApoTBCbl8elssSq93WEyuR_2uew1OHuT2utkKLQBMH2-bl0xKWyAzAzSmHJafD4KJUw3k6CuSNNsKq7pwcQ3wG

strcpy函數實現字符串的複製,關於這個函數,不動腦子的實現是下面幾個版本,本人寫的時候就是這麼寫的,呵呵。

void strcpy(char s[],char t[])//數組參數版 
{
	int n=0;
	while((s[n]=t[n])!='\0')
		n++;
}

void strcpy(char *s,char *t)//指針形參版
{
	int n=0;
	while((s[n]=t[n])!='\0')
		n++;
}
void strcpy(char *s,char *t)//指針形參簡化版 
{
	while((*s++=*t++));
}

實際上strcpy的函數原型是這樣的:

char *strcpy(char* dest, const char *src);

函數返回值爲char *類型,返回的是一個地址,這樣做的一個好處就是能夠支持鏈式表達式,比如說鏈式表達式的形式如:int len=strlen(strcpy(strA,strB));

又如:char * strA=strcpy(new char[10],strB);

此外,返回src的原始值也是錯誤的,主要有以下三點:

其一,源字符串肯定是已知的,返回它沒有任何意義。

其二,不能支持形如第二例的表達式。

其三,把const char *作爲char *返回,類型不符,編譯會報錯。

這個函數看似簡單,其實關於它的實現有很多需要注意的地方,像指針的有效性檢查、源字符串參數用const修飾,防止修改源字符串、內存重疊情況等問題。

下面的實現不考慮內存重疊問題,其典型的實現如下:

/**********************
*C語言標準庫函數strcpy的一種典型的工業級的最簡實現
*返回值:目標串的地址。
*對於出現異常的情況ANSI-C99標準並未定義,故由實現者決定返回值,通常爲NULL。
*參數:des爲目標字符串,source爲原字符串
*/
 char* strcpy(char* des,const char* source)
 {
 char* r=des;
  assert((des != NULL) && (source != NULL));
 while((*des++ = *source++)!='\0');
 return r;
 }
 /*while((*des++=*source++));的解釋:賦值表達式返回左操作數,所以在賦值NULL後,循環停止*/

對此,百度百科上也有詳細的說明

http://baike.baidu.com/link?url=OjQ2KCfPJ8JblYyu5PW_V3f9-iB6d4zIEOvVoiIhHnZce_FpNcegKihkzmgCodepfiR4y5J-uaZwIOtiaVYGWq

 內存重疊問題單獨拿出來討論,所謂的內存重疊就是:

char s[10]="hello";

strcpy(s, s+1); //應返回ello

strcpy(s+1, s); //應返回hhello,

但實際會報錯,因爲dest與src重疊了,把'\0'覆蓋了,即在src<=dst<=src+strlen(src)的情況下,就會發生內存重疊問題,針對這個問題給出帶有內存重疊檢測的strcpy函數實現如下:

char * strcpy(char *dest,const char *src)
{
    assert(dest != NULL && src != NULL);
    char *ret = dest;
    my_memcpy(dest, src, strlen(src)+1);
    return ret;
}
char *my_memcpy(char *dst, const char* src, int cnt)
{
    assert(dst != NULL && src != NULL);
    char *ret = dst; 

    if (dst >= src && dst <= src+cnt-1) //內存重疊,從高地址開始複製
    {
        dst = dst+cnt-1;
        src = src+cnt-1;
        while (cnt--)
            *dst-- = *src--;
    }
    else    //正常情況,從低地址開始複製
    {
        while (cnt--)
            *dst++ = *src++;
    }
    return ret;
}

參考

http://www.cnblogs.com/chenyg32/p/3739564.html

P.S.:

Linux下memcpy的函數實現如下:

void* memcpy(void *dest,const void *src,size_t count)
{
    assert(dest!=NULL && src!=NULL);
    char* tmp=dest;
    const char* s=src;
    for(size_t i=0;i<count;i++)
{
    tmp[i]=s[i];
}
    return dest;
}

strcpy和memcpy主要有以下3方面的區別。

1、複製的內容不同。strcpy只能複製字符串,而memcpy可以複製任意內容,例如字符數組、整型、結構體、類等。

2、複製的方法不同。strcpy不需要指定長度,它遇到被複制字符的串結束符"\0"才結束,所以容易溢出。memcpy則是根據其第3個參數決定複製的長度。

3、用途不同。通常在複製字符串時用strcpy,而需要複製其他類型數據時則一般用memcpy

 

函數strcat實現兩個字符串的連接,其函數原型如下:

extern char *strcat(char *dest,char *src);

其實現如下:

//將源字符串加const,表明其爲輸入參數
char* strcat(char* strDest , const char* strSrc)
{
    //後文return address,故不能放在assert斷言之後聲明address
    char* address=strDest;
    assert( (strDest!=NULL)&&(strSrc!=NULL) );//對源地址和目的地址加非0斷言
    while(*strDest)//是while(*strDest!=’\0’)的簡化形式
    {
        //若使用while(*strDest++),則會出錯,因爲循環結束後strDest還會執行一次++,
        //那麼strDest將指向'\0'的下一個位置。/所以要在循環體內++;因爲要使*strDest最後指
        //向該字符串的結束標誌’\0’。
        strDest++;
    }
 
    while(*strDest++=*strSrc++)
    {
        NULL;//該循環條件內可以用++,
    }//此處可以加語句*strDest=’\0’;無必要
    return address;//爲了實現鏈式操作,將目的地址返回
}
char *mystrcat(char *dest,const char *src)
{
	char *p=dest;
	while(*dest!='\0') dest++;
	while(*src!='\0') *dest++=*src++;
	*dest='\0';
	return p;//dest現在指向拼接後的最後一位字符,在這裏返回dst,會出現錯誤
}

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