1. 程式人生 > >[c語言]將strcpy()函式從低端到高階的實現

[c語言]將strcpy()函式從低端到高階的實現

簡介

  本文將實現庫函式strcpy,並將低端的寫法到高階的寫法進行演示講解。使自己寫的strcpy()函式不再那麼low!!!

正文

  我們要實現strcpy()函式,就先要了解這個函式是幹什麼用的;strcpy()函式是用來拷貝字串的,在函式中有兩個形參,一個源字串,還有一個目標字串,使用拷貝函式將源字串中的內容拷貝到目標字串並返回目標字串,這樣就完成了拷貝。   那麼我們來實現,首先我們根據邏輯寫,給形參傳遞源字串的首地址和目標字串的首地址。然後將源字串中的字元一個一個賦給目標字串,一直到’\0’結束。 程式碼如下:

//1.low逼程式碼
#include<stdio.h>
void
my_strcpy(char *dest, char *str) { while(*str != '\0') { *dest = *str; dest++; str++; } *dest = *str;//賦值結束,將'\0'賦給目標字串,此時的str指向的就是'\0' } int main() { char *p = "hello world"; char arr[30] = {0}; my_strcpy(arr,p); puts(arr); return 0; }

這樣程式碼就算完成了,但是這種程式碼其實很low,為什麼呢?我們來看看下面這段程式碼!!!

//2.還是比較low逼的
#include<stdio.h>
void my_strcpy(char *dest, char *str)
{
 	while(*dest++ = *str++)
	{
 		 ;
 	}
}
int main()
{
 	char *p = "hello world";
 	char arr[30] = {0};
 	my_strcpy(arr,p);
 	puts(arr);
 	return 0;
}

我們將while迴圈裡的內容放進了判斷部分,這樣是不是就簡潔了許多,這樣的程式碼省去了很多麻煩,在while迴圈進行判斷的同時順便將賦值操作也進行了,在賦值進行到結束字元時,也就是0,條件為假,跳出迴圈,同時賦值也完成了

,這樣的程式碼是不是更勝一籌呢?,這種程式碼還是比較low的。   當我們的源字串為空(NULL)呢?這個時候程式就會報錯!那怎麼辦呢?看下面這種改進!!

//較高分程式碼
#include<stdio.h>
#include<assert.h>
void my_strcpy(char *dest, char *str)
{
 	assert(str != NULL);//斷言
 	assert(dest != NULL);
 	//if(str == NULL)
 	// return;
 	while(*dest++ = *str++)
 	{
  		;
 	}
}
int main()
{
 	char *p = "hello world";
 	char arr[30] = {0};
 	my_strcpy(arr,p);
 	puts(arr);
 	return 0;
}

  在while迴圈之前加上斷言assert();提前將錯誤杜絕在編譯的時候,加上斷言有助於程式猿發現這些難以發現的錯誤,比如,當源字串指向空指標的時候,這個時候將空指標賦給目標字串,那麼這個時候如果不加斷言,程式猿就很難發現程式中的錯誤。如果加上斷言,那麼在編譯的時候,程式就會報錯,並且報出錯誤的位置,減去了很多不必要的麻煩。   但是在這如果不用斷言,而用if語句進行判斷,如果源字串指向空指標,就直接return;,這樣講道理是可以的,並且沒有任何問題的,但是一個程式給使用者用的話,使用者用的是release版本的,我們程式猿用的是debug版本的,如果用了斷言assert,那麼在release版本中,編譯器會自動將assert語句優化掉,但是if語句並不會優化掉,所以在release版本中if語句是一直存在的,這樣斷言assert的優勢就顯示出來了。   這樣是不是就是高分程式碼了?算是。但是我們還能繼續提升我們的這段程式碼的逼格。看下面

#include<stdio.h>
#include<assert.h>
void my_strcpy(char *dest, const char *str)//加上const保護不用改變的變數
{
 	assert(str != NULL);
 	assert(dest != NULL);
 	while(*dest++ = *str++)
 	{
  		;
 	}
}
int main()
{
 	char *p = "hello world";
 	char arr[30] = {0};
 	my_strcpy(arr,p);
 	puts(arr);
 	return 0;
}

  我們在形參中源字串前加了const,就保護了源字串不被改變,假如我們在複製的時候不小心將目標字串和源字串的位置寫反了,那麼就是將目標字串的內容拷貝到源字串中了,這樣就適得其反了。   在源字串前加上const就會完美的規避這種情況,如果寫反,那麼編譯的時候就會報錯,並且讓我們很快的就找到問題所在。   這樣的程式碼沒毛病,但是還是有一點點小瑕疵的,我們來看看完美的程式碼!!!

#include<stdio.h>
#include<assert.h>
char *my_strcpy(char *dest, const char *str)
{
 	char *ret = dest;//將目標空間的起始地址存在ret中
 	assert(str != NULL);
 	assert(dest != NULL);
 	while(*dest++ = *str++)
 	{
  		;
 	}
 return ret;//返回目標空間的起始地址
}
int main()
{
 	char *p = "hello world";
 	char arr[30] = {0};
 	my_strcpy(arr,p);
 	puts(arr);
 	return 0;
}

  我們可以在MSDN中搜索一下庫函式strcpy()函式,結果如下 在這裡插入圖片描述 我們可以看到庫函式型別並不是我們之前寫的void,而是char*型別的 那我們再看看函式的返回值

在這裡插入圖片描述 我們可以看到返回的是目標字串   我們再將這幾處一修改,這樣的程式碼就是完美的程式碼了,毫無瑕疵。 問題來了,為什麼要用char*型別呢?為什麼要有返回值呢? 答案就是為了實現鏈式訪問,只有函式有了返回值,這樣才能使用返回值進行鏈式訪問!!!

總結

  以上就是實現庫函式strcpy的過程,總而言之,自己實現庫函式的時候還是要看看庫函式的原型是如何定義的,往原型上靠攏,寫出的程式碼就是比較完美的,以此類推也可以實現別的庫函式,比如strlen(),strcat()等等。