文章目錄
- 字串和記憶體函式
- 字串函式
- 字串求長函式 `strlen`
- 函式宣告
- 函式用法
- 模擬實作
- 長度不受限制的字串函式
- 字串拷貝函式 `strcpy`
- 函式宣告
- 函式用法
- 模擬實作
- 字串追加函式 `strcat`
- 函式宣告
- 函式用法
- 模擬實作
- 字串比較函式 `strcmp`
- 函式宣告
- 函式用法
- 模擬實作
- 長度受限制的字串函式
- 字串拷貝函式 `strncpy`
- 函式宣告
- 函式用法
- 模擬實作
- 字串追加函式 `strncat`
- 函式宣告
- 函式用法
- 模擬實作
- 字串比較函式 `stnrcmp`
- 函式宣告
- 模擬實作
- 字串查找函式 `strstr`
- 函式宣告
- 模擬實作
- 字串分割函式 `strtok`
- 函式宣告
- 函式用法
- 字串報錯函式 `strerror`
- 函式宣告
- 函式用法
- 記憶體函式
- 記憶體拷貝函式 `memcpy`
- 函式宣告
- 函式用法
- 模擬實作
- 記憶體移動函式 `memmove`
- 函式宣告
- 模擬實作
- 實作方案
- 記憶體比較函式 `memcmp`
- 函式宣告
- 模擬實作
- 記憶體初始化函式 `memset`
- 函式宣告
- 函式用法
字串和記憶體函式
C語言本身并無字串型別但其對字串的操作卻較為頻繁,本章節介紹這些函式的用法和實作,
字串函式
字串求長函式 strlen
函式宣告
size_t strlen ( const char* string );
Return Value
This function returns the number of characters in string, excluding the terminal NULL. No return value is reserved to indicate an error.
Parameter
string - Null-terminated string
函式用法
-
'\0'為字串結束標志,故strlen函式計算\0之前的字符個數, -
引數是字串的地址,而字串必須為以
\0結尾的字串,
//1.
char arr1[] = "abcd";
printf("%d\n", strlen(arr1));//4
//2.
char arr2[] = { 'a','b','c','d' };
printf("%d\n", strlen(arr2));//隨機值
//3.
int a = 10;
printf("%d\n", strlen(a));//報錯
- 傳參過去的是以
\0結尾的字串地址,滿足strlen的要求,- 傳參的雖然是字串的地址但未以
\0結尾,故strlen會一直找下去,直至找到\0,- 傳參根本不是地址,該整型會被strlen當作地址看待,故訪問到
0x0A地址處的非法記憶體,如圖:

- strlen函式回傳型別為
size_t,注意無符號數的用法,
size_t ret = strlen("abcdef");
printf("%u\n", ret);
無符號數接收,以無符號的形式列印,不要使用無符號數運算結果比大小:
if (strlen("abc") - strlen("abcdef") < 0) {
printf("hehe\n");
}
else {
printf("haha\n");
}
無符號數的運算結果被整形提升為無符號數,故二者相減所得被視為無符號數是不會小于零的,
模擬實作
//1.計數器
size_t my_strlen(const char* str) {
assert(str);
size_t count = 0;
while (*str) {
count++;
str++;
}
return count;
}
//2.指標相減
size_t my_strlen(const char* str) {
assert(str);
char* begin = str;
while (*str) {
str++;
}
return str - begin;
}
//3.遞回
size_t my_strlen(const char* str) {
assert(str);
if (*str) {
return 1 + my_strlen(str + 1);
}
else {
return 0;
}
}
長度不受限制的字串函式
字串函式分為長度不受限制的字串函式和長度受限制的字串函式,先介紹長度不受限制的字串函式,
字串拷貝函式 strcpy
函式宣告
char* strcpy ( char* strDestination, const char* strSource );
Return Value
This function returns the destination string. No return value is reserved to indicate an error.
Parameters
1. strDestination - Destination string
2. strSource - Null-terminated source string
Remarks
1The strcpy function copies the string pointed by strSource to the array pointed by strDestination,including the terminating null character.
No overflow checking(溢位檢查) is performed when strings are copied. The behavior of strcpy is undefined(未定義的) if the source and destination strings overlap(重疊).
strcpy將源字串的內容(包括\0)依次拷貝到目標空間,
函式用法
- 源字串以
'\0'結尾,且strcpy會將源字串中\0也拷貝到目標空間,

拷貝時會把
\0也一同拷貝,若源字串中有\0,其后的元素自然是不會被拷貝的,因為在讀取時也就把\0作為結束標志,

若源字串不以
\0結尾,則strcpy會向后一直訪問到非法記憶體,
- 目標空間可修改且足夠大,確保能夠且足以存放源字串,
//1.
char arr1[] = "xxx";//目標空間不夠大
char arr2[] = "abcdef";
//.2
const char arr1[] = "xxxxxxxxxx";//目標空間不可修改
char arr2[] = "abcdef";
strcpy(arr1, arr2);
以上兩種情況同樣是需要避免的,
strcpy在vs2019環境下使用會報出不安全的警告,該函式在拷貝前不會去檢查是否會發生陣列越界,警告也是正常的,但作為合格的程式員,我們有義務排除風險,
模擬實作
char* my_strcpy(char* dest, const char* src) {
assert(dest && src);
char* begin = dest;
while (*dest++ = *src++) {
;
}
return begin;
}
字串追加函式 strcat
函式宣告
char* strcat ( char* strDestination, const char* strSource );
Return Value
This function returns the destination string. No return value is reserved to indicate an error.
Parameters
1. strDestination - Null-terminated destination string
2. strSource - Null-terminated source string
Remarks
The strcat function appends strSource to strDestination and terminates the resulting string with a null character.
The initial character of strSource overwrites the terminating null character of strDestination.
No overflow checking is performed when strings are copied or appended. The behavior of strcat is undefined if the source and destination strings overlap.
strcat將源字串包括\0,追加到目標字串的結尾并覆寫掉目標空間的\0,
函式用法
- 源字串必須以
'\0'結尾,strcat會將\0追加到目標空間的末尾作字串結束標志,

該例子同樣是源字串不以
\0結尾,從而訪問到非法空間,
- 目標空間必須足夠大且可修改,確保能夠且足以追加字串,

目標字串以初始化內容確定大小,這種初始化的方式顯然是不能追加字串的,同樣目標字串不可加
const修飾,strcat在呼叫時仍然不作溢位檢查,只有到溢位后才報錯,
模擬實作
char* my_strcat (char* dest, const char* src) {
assert(dest && src);
char* begin = dest;
//移到末尾
while (*dest) {
dest++;
}
//追加
while (*dest++ = *src++) {
;
}
return begin;
}
下面三種while回圈的特點:
//1.
while (*dest++) {
;
}
//2.
while (*dest) {
dest++;
}
//3.
while (*dest++ = *src++) {
;
}
- 第一種和第二種的區別在于,第一種指標解參考后就
++,第二種指標解參考后判斷為真才能++,故第一種解參考的次數和++的次數是相等的,當指標指向\0時也要++訪問下一個元素,而第二種為真后++,指向\0時便停留在\0處 , - 將賦值運算
*dest=*src放入while回圈內,當src指向\0時也要賦值給dest,在這之后判斷整個運算式為假,退出回圈,
字串比較函式 strcmp
函式宣告
int strcmp ( const char* string1, const char* string2 );
Return Value
The return value for this function indicates the relation of string1 to string2.
+-------+------------------------------------+
| Value | Relationship of string1 to string2 |
+-------+------------------------------------+
| <0 | string1 less than string2 |
| 0 | string1 identical to string2 |
| >0 | string1 greater than string2 |
+-------+------------------------------------+
Parameters
1. string1, string2
2. Null-terminated strings to compare
Remarks
The strcmp function compares string1 and string2, what starts comparing the first character of each string, if are equal, continuing with the follows until the differ or a null-character is reached, and returns a value indicating their relationship.
strcmp遍歷比較兩個字串對應位置的字符的ASCII碼值是否相等,
函式用法
- 函式的回傳值
- 字串1小于字串2時,回傳小于0的數字
- 字串1等于字串2時,回傳0
- 字串1大于字串2時,回傳大于0的數字
- 二者都要以
\0作為字串的結尾,否則仍會越界訪問,
模擬實作
int my_strcmp(const char* str1, const char* str2) {
assert(str1 && str2);
while (*str1 == *str2) {
if (*str1 == '\0') {
return 0;
}
str1++;
str2++;
}
return *str1 - *str2;
}
都是
\0就回傳0,不是就都++,直到不等時回傳二者之差,
不安全是長度不受限制的字串函式的通病,因為這些函式在呼叫時一直向后遍歷訪問且不做溢位檢查直到程式出錯才被迫停止,所以C語言還內置了相對安全的長度受限制的字串函式,以及更加通用的記憶體函式,
長度受限制的字串函式
字串拷貝函式 strncpy
函式宣告
char* strncpy ( char* strDest, const char* strSource, size_t count );
Return Value
This function returns strDest. No return value is reserved to indicate an error.
Parameters
1. strDest - Destination string
2. strSource - Source string
3. count - Number of characters to be copied
Remarks
The strncpy function copies the initial count characters of strSource to strDest and returns strDest.
If count is less than or equal to the length of strSource, a null character is not appended automatically to the copied string.
If count is greater than the length of strSource, the destination string is padded(填充) with null characters up to length count.
The behavior of strncpy is undefined if the source and destination strings overlap.
strcpy函式從源字串拷貝count個字符到目標字串,若count大于源字串長度則以\0填充,
函式用法
- 目標字串可修改且足夠大
- 若
count小于源字串個數則不追加\0,若大于源字串個數則以\0填充到count個字符,

模擬實作
char* my_strncpy(char* dest, const char* src, size_t count) {
assert(dest && src);
char* begin = dest;
//字串拷貝到count個或遇到\0
while (count-- && (*dest++ = *src++)) {
;
}
//count>源字串長度,末尾補零
while (count) {
*dest++ = '\0';
count--;
}
return begin;
}
int main()
{
char arr1[10] = "abcdef";
char arr2[] = "xxx";
my_strncpy(arr1, arr2, 5);
printf("%s\n", arr1);
return 0;
}
下列三種是進行字串拷貝時的代碼,當
count小于源字串個數時三者都沒問題,但當count大于源字串個數時,三者的情況卻是不一樣的,
//1.
while (count && src) {
*dest++ = *src++;
count--;
}
//2.
while (count && (*dest++ = *src++)) {
count--;
}
//3.
while (count-- && (*dest++ = *src++)) {
;
}
- 當指向源字串的指標遇到
\0時,不進行賦值和++也不進行count--操作,
賦值賦了3次,
count也減了3次,此時count為2,隨后追加了2個\0,

- 指標指向
\0時執行賦值操作后,賦值運算式為假,結束回圈,
由于賦值操作放在回圈的判斷部分,故賦值執行了4次,而
count減了3次,

即源字串末尾\0也被拷貝了過去,但count仍為2,所以最后還要追加2個\0,但這樣就改變了6個字符,顯然是錯誤的,
- 將第二種代碼稍作修改,將
count--的操作也放入判斷部分,這樣就避免了上述問題,

如果回圈條件只有回圈變數count大小的判斷,則進步條件放在判斷部分還是回圈體內沒有影響,
字串追加函式 strncat
函式宣告
char* strncat ( char* strDest, const char* strSource, size_t count );
Return Value
This function returns a pointer to the destination string. No return value is reserved to indicate an error.
Parameters
1. strDest - Null-terminated destination string
2. strSource - Null-terminated source string
3. count - Number of characters to append
Remarks
The strncat function appends, at most, the first count characters of strSource to strDest. The initial character of strSource overwrites the terminating null character of strDest.
If a null character appears in strSource before count characters are appended, strncat appends all characters from strSource, up to the null character.
If count is greater than the length of strSource, the length of strSource is used in place of count. The resulting string is terminated with a null character.
If copying takes place between strings that overlap, the behavior is undefined.
在目標字串末尾追加
count個源字串的字符,結尾默認添加\0,
函式用法
- 追加到目標字串末尾默認補
'\0',count超出源字串個數不在追加,

模擬實作
char* my_strncat(char* dest, const char* src, size_t count) {
assert(dest && src);
char* begin = dest;
//1. 來到目標字串末尾
while (*dest) {
dest++;
}
//2. 追加
while (count && (*dest++ = *src++)) {
count--;
}
//3. count小于字符個數補0
if (count == 0) {
*dest = '\0';
}
return begin;
}
字串比較函式 stnrcmp
函式宣告
int strncmp ( const char* string1, const char* string2, size_t count );
Return Value
The return value for each of these functions indicates the relation of string1 to string2.
+-------+------------------------------------+
| Value | Relationship of string1 to string2 |
+-------+------------------------------------+
| <0 | string1 less than string2 |
| 0 | string1 identical to string2 |
| >0 | string1 greater than string2 |
+-------+------------------------------------+
Parameters
1. string1, string2 - Null-terminated strings to compare.
2. count - Number of characters to compare
Remarks
The strncmp function lexicographically compares, at most, the first count characters in string1 and string2 and returns a value indicating the relationship between the substrings.
比較兩個字串的前
count個字符,并回傳相關的數值,
模擬實作
int my_strncmp(const char* str1, const char* str2, size_t count) {
assert(str1 && str2);
while (count-- && (*str1 == *str2)) {
str1++;
str2++;
}
return *str1 - *str2;
}
字串查找函式 strstr
函式宣告
char* strstr ( const char* string, const char* strCharSet );
Return Value
This function returns a pointer to the first occurrence of strCharSet in string, or NULL if strCharSet does not appear in string.
If strCharSet points to a string of zero length, the function returns string.
Parameters
1. string - Null-terminated string to search
2. strCharSet - Null-terminated string to search for
Remarks
The strstr function returns a pointer to the first occurrence of strCharSet in string. The search does not include terminating null characters.
查找子字串在目標字串中首次出現的位置,有則回傳之起始位置,無則回傳空指標,
模擬實作
char* my_strstr(const char* str, const char* set) {
assert(str && set);
char* s1 = str;
char* s2 = set;
while (*s1) {
//歸位
str = s1;
set = s2;
//防止s1=s1=\0
while ((*str && *set) && (*str == *set)) {
str++;
set++;
}
//判斷
if (*set == '\0') {
return s1;
}
//進位
s1++;
}
return NULL;
}
s1和s2存放str和set每次歸位時的位置,以便匹配錯誤時回傳重新開始,
- 特殊情況
set="\0"時,\0是任意字串的子字串,即無任何字符的字串是任意字串的子字串,所以回傳母字串 - 一次匹配錯誤時,str和set要歸位初始位置,即
str=++s1;set=s2;要放在判斷部分的后面,(set=s2下次回圈再執行就相當于在后面)

KMP演算法專門針對字串匹配和查找這種功能,
字串分割函式 strtok
函式宣告
char* strtok ( char* strToken, const char* strDelimit );
Return Value
This function returns a pointer to the next token found in strToken. They return NULL when no more tokens are found. Each call modifies(修改) strToken by substituting(替換) a NULL character for each delimiter(分割符) that is encountered(遇到).
Parameters
1. strToken - String containing token(s)(標記)
2. strDelimit -String Set of delimiter characters
Remarks
The strtok function finds the next token in strToken. The set of characters in strDelimit specifies(指定) possible delimiters of the token to be found in strToken on the current call.
strtok函式通過以\0替換分隔符的方式修改需分割的字串,并回傳該標記的地址,
函式用法
如字串
"192.168.11.1"或者"yourfriendyo@ms.com"二者字串都有相似之處,即整個字串由分隔符.,@分割,如果需要得到分隔符所分割出的每個小字串,則可以用strtok函式切分,
- 引數
strDelimit是所有分隔符所組成字串,引數strToken是由一個或多個分隔符和標記組成的字串,
類似于
.,@稱為分隔符,而分隔符所分割出的子字串如192,com被稱作標記,
-
strstr將標記以\0結尾且回傳指向該標記的指標,故被操作字串會發生修改,故一般使用臨時拷貝的內容, -
首個引數傳入字串地址時,找到其首個標記,隨后保存分隔符的位置,此時僅需傳入
NULL則可訪問該字串的下一個標記,
第一次呼叫傳入需分割字串的地址,函式保存首個標記后的分隔符被
\0替換的位置,之后的呼叫就無需在傳入地址,當訪問到最后一個標記時,函式 回傳NULL,
char arr[] = "www.yourfriendyo.top";
printf("%s\n", strtok(arr2, "."));
printf("%s\n", strtok(NULL, "."));
printf("%s\n", strtok(NULL, "."));
int main()
{
char arr[] = "yourfriendyo@ms.com";
char* sep = "@.";
for (char* i = strtok(arr, sep); i != NULL; i = strtok(NULL, sep)) {
printf("%s\n", i);
}
return 0;
}
字串報錯函式 strerror
函式宣告
char *strerror ( int errnum );
Return Value
strerror returns a pointer to the error-message string. Subsequent calls to strerror can overwrite the string.
Parameters
1. errnum - Error number
2. strErrMsg - User-supplied message
Remarks
The strerror function maps errnum to an error-message string, returning a pointer to the string. strerror can't actually prints the message: For that, you need to call an output function such as printf.
If strErrMsg is passed as NULL, strerror returns a pointer to a string containing the system error message.
strerror回傳內置錯誤碼所對應的錯誤資訊的字串地址,
函式用法
printf("%s\n", strerror(40));//Function not implemented
printf("%s\n", strerror(30));//Read - only file system
printf("%s\n", strerror(20));//Not a directory
一個錯誤碼對應一個錯誤資訊,手動傳參并列印函式的回傳值,當然沒有人會這樣使用報錯函式,這也不是報錯函式設計的初衷,
-
當程式發生錯誤時,程式會自動將錯誤碼存入內置全域變數
errno中,此時我們呼叫strerror函式即可獲得此次錯誤的報錯資訊, -
更為直接的報錯函式
perror,優點簡單方便加入自定義資訊,缺點必須列印錯誤資訊,
int main()
{
FILE* pFile = fopen("D:test.txt", "r");
if (pFile == NULL) {
//text.txt: No such file or directory
printf("text.txt: %s\n", strerror(errno));
perror("text.txt");
}
return 0;
}
記憶體函式
字串操作函式適用于字串內容,而記憶體操作函式適用于任意型別的資料如整形資料或是結構體,更具普適性,
記憶體拷貝函式 memcpy
函式宣告
void* memcpy ( void* dest, const void* src, size_t count );
Return Value
memcpy returns the value of dest.
Parameters
1. dest - New buffer
2. src - Buffer to copy from
3. count - Number of characters to copy(bytes)
Remarks
The memcpy function copies count bytes of src to dest.
If the source and destination overlap(重疊), this function does not ensure that the original source bytes in the overlapping region(區域) are copied before being overwritten. Use memmove to handle overlapping regions.
memcpy將源記憶體的前count個位元組的內容拷貝到目標記憶體中,
函式用法
- 若源空間和目標空間有重疊,則拷貝時會覆寫源字串內容,

C標準并未要求
memcpy完成發生記憶體重疊的內容拷貝,但編譯器也可能對其進行優化,對記憶體重疊的內容進行拷貝時,可以使用memmove,
模擬實作
以位元組為單位拷貝記憶體中的內容,以適用所有型別的資料,
void* my_memcpy(void* dest, const void* src, size_t count) {
void* begin = dest;
while (count--) {
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return begin;
}
記憶體移動函式 memmove
函式宣告
void* memmove ( void* dest, const void* src, size_t count );
Return Value
memmove returns the value of dest.
Parameters
1. dest - Destination object
2. src - Source object
3. count - Number of bytes of characters to copy
Remarks
The memmove function copies count bytes of characters from src to dest.
If some regions of the source area and the destination overlap, memmove ensures that the original source bytes in the overlapping region are copied before being overwritten.
memmove將源空間的前count個位元組的內容拷貝到目標空間中,并支持完成記憶體重疊的拷貝,
模擬實作
當發生記憶體重疊時,在源空間該位元組未拷貝之前要保護其不被修改,
當目標空間在源空間的后邊時,從后向前拷貝,當目標空間在源空間的前面時,從前向后拷貝,

實作方案

void* my_memmove(void* dest, const void* src, size_t count) {
assert(dest && src);
char* begin = dest;
if (dest > src) {
//后->前
while (count--) {
*((char*)dest + count) = *((char*)src + count);
}
}
else {
//前—>后
while (count--) {
*(char*)dest = *(char*)src;
(char*)dest += 1;
(char*)src += 1;
}
}
return begin;
}
int main() {
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
//1234->3456
my_memmove(arr + 2, arr, 4 * sizeof(int));
//3456->1234
my_memmove(arr, arr + 2, 4 * sizeof(int));
for (int i = 0; i < 10; i++) {
printf("%d ", arr[i]);
}
return 0;
}
C語言沒有字串型別,但凡遇到對字串的操作都要細化到操作每個字符,
記憶體比較函式 memcmp
函式宣告
int memcmp ( const void* buf1, const void* buf2, size_t count );
Return Value
The return value indicates the relationship between the buffers.
+-------+------------------------------------+
| Value | Relationship of string1 to string2 |
+-------+------------------------------------+
| <0 | string1 less than string2 |
| 0 | string1 identical to string2 |
| >0 | string1 greater than string2 |
+-------+------------------------------------+
Parameters
1. buf1 - First buffer
2. buf2 - Second buffer
3. count - Number of characters
Remarks
The memcmp function compares the first count bytes of buf1 and buf2 and returns a value indicating their relationship.
比較兩塊記憶體空間的前
count個對應位元組內容,并回傳相關的數值,
模擬實作
int my_memcmp(const void* buf1, const void* buf2, size_t count) {
assert(buf1 && buf2);
while (count-- && (*(char*)buf1 == *(char*)buf2)) {
(char*)buf1 += 1;
(char*)buf2 += 1;
}
return *(char*)buf1 - *(char*)buf2;
}
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 1,2,3,4,6 };
int ret = my_memcmp(arr1, arr2, 1 * sizeof(int));
printf("%d\n", ret);
return 0;
}
記憶體初始化函式 memset
函式宣告
void* memset ( void* dest, int c, size_t count );
Return Value
memset returns the value of dest.
Parameters
1. dest - Pointer to destination
2. c - Character to set
3. count - Number of characters
Remarks
The memset function sets the first count bytes of dest to the character c.
將目標空間前
count個位元組初始化為整形資料c,
函式用法

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/301818.html
標籤:其他
