?
目錄
1.求字符串長度strlen
2.長度不受限制的字符串函數
字符串拷貝strcpy
字符串追加strcat
字符串比較strcmp
3.長度受限制的字符串函數介紹strncpy
strncat
?編輯strncmp
4.字符串查找strstr
5.字符串分割strtok
6.錯誤信息報告
strerror
perror
7.字符分類函數
8.字符轉換函數
?9.內存操作函數
memcpy
memmove
?memset
?memcmp?編輯
1.求字符串長度strlen
函數介紹
- 字符串已經 '\0' 作為結束標志,strlen函數返回的是在字符串中 '\0' 前面出現的字符個數(不包含 '\0' )。
- 參數指向的字符串必須要以 '\0' 結束。
- 注意函數的返回值為size_t,是無符號的( 易錯 )?
使用示例
int main()
{size_t sz = strlen("abcd");printf("%u", sz);return 0;
}
因為函數的返回值為無符號類型,所以對于下面這種代碼,我們很容易就看錯:
#include<stdio.h>
#include<string.h>
int main()
{if (strlen("abc") - strlen("abcdef") > 0){printf("大于\n");}else{printf("小于\n");}return 0;
}
輸出結果:
?正確的比較方法:
if (strlen("abc") > strlen("abcdef"))
strlen的模擬實現
size_t my_strlen(const char* str)
{int count = 0;while (*str!='\0'){count++;str++;}return count;
}
2.長度不受限制的字符串函數
字符串拷貝strcpy
- Copies the C string pointed by source into the array pointed by destination, including the terminating null character (and stopping at that point).
- 源字符串必須以 '\0' 結束。
- 會將源字符串中的 '\0' 拷貝到目標空間。
- 目標空間必須足夠大,以確保能存放源字符串。
- 目標空間必須可變
使用示例
int main()
{char arr1[20] = { 0 };char arr2[] = "hello world";strcpy(arr1, arr2);printf("%s", arr1);return 0;
}
輸出結果:?
strcpy的模擬實現
char* my_strcpy(char* dest, const char* src)
{assert(dest && src);char* ret = dest;while (*dest++ = *src++){}return ret;
}
字符串追加strcat
- ?Appends a copy of the source string to the destination string. The terminating null character in destination is overwritten by the first character of source, and a null-character is included at the end of the new string formed by the concatenation of both in destination.
- 源字符串必須以 '\0' 結束。
- 目標空間必須有足夠的大,能容納下源字符串的內容。
- 目標空間必須可修改。
- 字符串自己給自己追加,如何?
?使用示例
int main()
{char arr1[20] = "hello ";char arr2[] = "world";strcat(arr1, arr2);printf("%s", arr1);return 0;
}
輸出結果:?
?strcat的模擬實現
char* my_strcat(char* dest, const char* src)
{assert(dest && src);char* ret = dest;while (*dest != '\0')//找到目標空間的\0{dest++;}while (*dest++ = *src++){}return ret;
}
注意:字符串不能自己給自己追加?
當字符串自己給自己追加時,字符串中的'\0'將會被覆蓋,導致程序陷入死循環。
字符串比較strcmp
- ?This function starts comparing the first character of each string. If they are equal to each other, it continues with the following pairs until the characters differ or until a terminating null-character is reached.
- 標準規定:
- 第一個字符串大于第二個字符串,則返回大于0的數字
- 第一個字符串等于第二個字符串,則返回0
- 第一個字符串小于第二個字符串,則返回小于0的數字
使用示例
int main()
{int ret1 = strcmp("abcdef", "abq");int ret2 = strcmp("abc", "abc");printf("%d\n", ret1);printf("%d\n", ret2);return 0;
}
?輸出結果:?
strcmp的模擬實現
int my_strcmp(const char* str1, const char* str2)
{assert(str1 && str2);while (*str1++ == *str2++){if (*str1 == '\0'){return 0;}}return *str1 - *str2;
}
3.長度受限制的字符串函數介紹
strncpy
- Copies the first num characters of source to destination. If the end of the source C string (which is signaled by a null-character) is found before num characters have been copied, destination is padded with zeros until a total of num characters have been written to it.
- 拷貝num個字符從源字符串到目標空間。
- 如果源字符串的長度小于num,則拷貝完源字符串之后,在目標的后邊追加0,直到num個。
使用示例
int main()
{char arr1[20] = "abcdef";char arr2[20] = "xxxxxxxxxxxxxx";strncpy(arr1, arr2, 3);printf("%s\n", arr1);return 0;
}
輸出結果:?
strncat
- Appends the first num characters of source to destination, plus a terminating null-character.
- If the length of the C string in source is less than num, only the content up to the terminating null-character is copied.
使用示例
int main()
{char arr1[20] = "hello ";char arr2[20] = "worldxxx";strncat(arr1, arr2, 5);printf("%s\n", arr1);return 0;
}
輸出結果:?

strncmp
?比較到出現另個字符不一樣或者一個字符串結束或者num個字符全部比較完。
?使用示例
int main()
{char arr1[20] = "abc";char arr2[20] = "abcdef";char arr3[20] = "aba";printf("%d\n", strncmp(arr1, arr2, 3));printf("%d\n", strncmp(arr1, arr2, 4));printf("%d\n", strncmp(arr1, arr3, 5));return 0;
}
輸出結果:?
4.字符串查找strstr
strstr函數是在字符串str1中查找是否含有字符串str2,如果存在,返回str2在str1中第一次出現的地址;否則返回NULL。
使用示例
int main()
{char arr1[] = "abcdefabcdef";char arr2[] = "def";char* ret = strstr(arr1, arr2);if(ret!=NULL){printf("%s\n", ret);}return 0;
}
輸出結果:?
?strstr的模擬實現
這里使用的是BF算法來實現:
char* my_strstr(char* str1, char* str2)
{char* cp = str1;char* s1 = cp;char* s2 = str2;while (*cp){s1 = cp;s2 = str2;while (*s1 && *s2 && *s1 == *s2){s1++;s2++;}if (*s2 == '\0')return cp;cp++;}return NULL;
}
5.字符串分割strtok
- sep參數是個字符串,定義了用作分隔符的字符集合
- 第一個參數指定一個字符串,它包含了0個或者多個由sep字符串中一個或者多個分隔符分割的標 記。
- strtok函數找到str中的下一個標記,并將其用 \0 結尾,返回一個指向這個標記的指針。(注: strtok函數會改變被操作的字符串,所以在使用strtok函數切分的字符串一般都是臨時拷貝的內容并且可修改。)
- strtok函數的第一個參數不為 NULL ,函數將找到str中第一個標記,strtok函數將保存它在字符串中的位置。
- strtok函數的第一個參數為 NULL ,函數將在同一個字符串中被保存的位置開始,查找下一個標記。
- 如果字符串中不存在更多的標記,則返回 NULL 指針。
使用示例1
int main()
{char arr[] = "123@qq.com";char sep[] = "@.";char copy[20];strcpy(copy, arr);char* pc = strtok(copy, sep);while (pc){puts(pc);pc = strtok(NULL, sep);}return 0;
}
使用示例2
int main()
{char arr[] = "123@qq.com";char sep[] = "@.";char copy[20];strcpy(copy, arr);char* ret = NULL;for (ret = strtok(copy, sep); ret != NULL; ret = strtok(NULL, sep)){printf("%s\n", ret);}return 0;
}
輸出結果:?
6.錯誤信息報告
strerror
?返回錯誤碼,所對應的錯誤信息。
庫函數在執行的時候,發生了錯誤會將錯誤碼存放在errno這個全局變量中。errno是C語言提供的一個全局變量。
下面代碼是將錯誤碼0~9所對應的錯誤信息給打印出來:
int main()
{int i = 0;for (int i = 0; i < 10; i++){printf("%d:%s\n", i, strerror(i));}return 0;
}
?當然,這里展示的僅僅是一小部分。
下面演示一下當我們進行文件操作是,打開文件失敗后查找打開失敗的原因:
int main()
{FILE* pf = fopen("data.txt", "r");if (pf == NULL){printf("%s\n", strerror(errno));}return 0;
}
程序輸出了“ No such file or directory ”的錯誤提示,意思是沒有要打開的文件。
perror
perror(str) 用來將上一個函數發生錯誤的原因輸出到標準設備(stderr)。參數 str 所指的字符串會先打印出,后面再加上錯誤原因字符串。此錯誤原因依照全局變量errno的值來決定要輸出的字符串。
使用示例
int main()
{FILE* pf = fopen("data.txt", "r");if (pf == NULL){printf("%s\n", strerror(errno));perror("fopen");}return 0;
}
?輸出結果:
7.字符分類函數
8.字符轉換函數
?下面代碼的功能是輸入一串字符,將字符串中的小寫字母轉成大寫,大寫字母轉成小寫
int main()
{char arr[30] = { 0 };gets(arr);char* p = arr;while (*p){if (isupper(*p)){*p = tolower(*p);}else if (islower(*p)){*p = toupper(*p);}*p++;}puts(arr);
}
?
9.內存操作函數
memcpy
- 函數memcpy從source的位置開始向后復制num個字節的數據到destination的內存位置。
- 這個函數在遇到 '\0' 的時候并不會停下來。
- 如果source和destination有任何的重疊,復制的結果都是未定義的。
可以拷貝任意類型的數據:字符串,整形數組,結構體數據類型……
使用示例
int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[20] = { 0 };//將arr1的內容拷貝到arr2中memcpy(arr2, arr1, 40);for (int i = 0; i < 20; i++){printf("%d ", arr2[i]);}return 0;
}
?輸出結果:
?memcpy的模擬實現
void* my_memcpy(void* dest, void* src, size_t num)
{assert(dest && src);void* ret = dest;while (num--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}return ret;
}
注意下面這種寫法編譯器會報錯:
在代碼中,使用了(char*)dest++
和(char*)src++
來更新指針的位置。然而,這種寫法是不正確的,因為后綴遞增操作符++
的優先級高于類型轉換操作符(char*)
,所以實際上你只是在轉換指針類型后遞增了一個臨時變量,而沒有更新指針本身的值。
注意:mencpy 函數是用來處理不會重疊的內存拷貝,當我們拷貝兩個相同的字符串是,可以結果與預期的會不相同:
int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };my_memcpy(arr1+2, arr1, 32);for (int i = 0; i < 10; i++){printf("%d ", arr1[i]);}printf("\n");return 0;
}
對于上面的代碼,我們的預期結果是這樣的:
?實際輸出的結果卻是如下:
?如果要拷貝重疊的內存,我們就要使用下面這個函數了
memmove
- 和memcpy的差別就是memmove函數處理的源內存塊和目標內存塊是可以重疊的。
- 如果源空間和目標空間出現重疊,就得使用memmove函數處理。
使用示例
int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };memmove(arr1+2, arr1, 32);for (int i = 0; i < 10; i++){printf("%d ", arr1[i]);}printf("\n");return 0;
}
?輸出結果:
?memmove的模擬實現
不同于memcpy,mommove的拷貝需要分情況:
void* my_memmove(void* dest, const void* src, size_t num)
{assert(dest && src);void* ret = dest;if (dest < src){//從前向后拷貝while (num--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}}else{printf("從后向前拷貝\n");//從后向前拷貝while (num--){*((char*)dest + num) = *((char*)src + num);}}return ret;
}
?
memset
?memset是一個初始化函數,作用是將某一塊內存中的全部設置為指定的值。
- prr 指向要填充的內存塊。
- value 是要被設置的值。
- num 是要被設置該值的字符數。
- 返回類型是一個指向存儲區s的指針。
使用示例1
int main()
{char arr[] = "hello world";memset(arr + 1, 'x', 4);printf("%s\n", arr);return 0;
}
上面代碼的作用是將的arr數組的第2個元素到第5個元素的值設置成‘x',輸出結果:
使用示例2
int main()
{int arr[10] = { 0 };memset(arr, 1, 10);return 0;
}
?
memcmp
比較從ptr1和ptr2指針開始的num個字節
返回值如下:
?使用示例
int main()
{int arr1[] = { 1,2,1,4,5,6 };int arr2[] = { 1,2,257 };int ret = memcmp(arr1, arr2, 9);printf("%d\n", ret);return 0;
}
對于上面的代碼,可以借助編譯器的調試來查看 arr1 和 arr2 的內存:
可以看出,arr1和arr2的前9個字節是相同的,而我們比較的是前9個字節,因此輸出結果應該是0。?
?