C語(yǔ)言字符串函數(shù),字符函數(shù),內(nèi)存函數(shù)使用及模擬實(shí)現(xiàn)
求字符串長(zhǎng)度
strlen
函數(shù)功能
字符串長(zhǎng)度,求一個(gè)字符串中字符的個(gè)數(shù)(不包含’\0’)。
函數(shù)參數(shù):
size_t strlen( const char *string ); # size_t 是函數(shù)的返回類(lèi)型 # char* string 是函數(shù)參數(shù)
函數(shù)使用:
#include <stdio.h> #include <string.h> //strlen對(duì)應(yīng)頭文件 int main() { char arr[] = "abcdef"; int len = strlen(arr); printf("%d\n", len); return 0; }
模擬實(shí)現(xiàn)(初階):
#include <stdio.h> #include <assert.h> //assert對(duì)應(yīng)頭文件 size_t my_strlen(const char* str) { assert(str != NULL); //檢查str是否為空指針 int count = 0; while (*str != '\0') { str++; count++; } return count; } int main() { char arr[] = "abcdef"; int len = my_strlen(arr); printf("%d\n", len); return 0; }
模擬實(shí)現(xiàn)(進(jìn)階):
#include <stdio.h> #include <assert.h> //assert對(duì)應(yīng)頭文件 size_t my_strlen(const char* str) //const用于保護(hù)源字符串不被修改 { assert(str != NULL); char* head = str; //記錄字符串開(kāi)頭的地址 while (*str++); //找到字符串結(jié)束的地址 return str - head - 1; //指針-指針得到元素個(gè)數(shù),-1減去\0 } int main() { char arr[] = "abcdef"; int len = my_strlen(arr); printf("%d\n", len); return 0; }
注意事項(xiàng):
- 字符串以 ‘\0’ 作為結(jié)束標(biāo)志,strlen函數(shù)返回的是在字符串中 ‘\0’ 前面出現(xiàn)的字符個(gè)數(shù)(不包含’\0’)。
- 函數(shù)參數(shù)指向的字符串必須要以 ‘\0’ 結(jié)束,否則得到的就是隨機(jī)值(strlen會(huì)一直往后找,直到遇到’\0’才結(jié)束)。(??迹?/li>
- 注意函數(shù)的返回值為size_t,是無(wú)符號(hào)的。(易錯(cuò):可能出現(xiàn)算術(shù)轉(zhuǎn)換)
長(zhǎng)度不受限制的字符串函數(shù)
strcpy
函數(shù)功能:
字符串拷貝,把一個(gè)字符串里面的內(nèi)容拷貝到另一個(gè)字符串中去(包括’\0’)。
函數(shù)參數(shù);
char* strcpy(char * destination, const char * source ); # char* 是函數(shù)的返回值,返回的是目標(biāo)空間的起始地址 # source 是要拷貝的字符串 # destination 是目標(biāo)空間
函數(shù)使用:
#include <stdio.h> #include <string.h> //strcpy對(duì)應(yīng)頭文件 int main() { char arr1[] = "abcdef"; char arr2[10] = ""; strcpy(arr2, arr1); printf("%s\n", arr2); return 0; }
模擬實(shí)現(xiàn)(初階):
#include <stdio.h> #include <assert.h> //assert對(duì)應(yīng)頭文件 char* my_strcpy(char* dest, const char* src) //const用于保護(hù)源字符串 { assert(dest && src); //保證傳遞過(guò)來(lái)的不是空串 char* ret = dest; //記錄目標(biāo)空間的起始地址 while (*src != '\0') { *dest = *src; dest++; src++; } *dest = *src; //將末尾的'\0'賦給dest return ret; } int main() { char arr1[] = "abcdef"; char arr2[10] = ""; my_strcpy(arr2, arr1); printf("%s\n", arr2); return 0; }
模擬實(shí)現(xiàn)(進(jìn)階):
#include <stdio.h> #include <assert.h> //assert對(duì)應(yīng)頭文件 char* my_strcpy(char* dest, const char* src) //const用于保護(hù)源字符串 { assert(dest && src); //保證傳遞過(guò)來(lái)的不是空串 char* ret = dest; //記錄目標(biāo)空間的起始地址 while (*dest++ = *src++); //連同末尾的'\0'一起賦給了dest return ret; } int main() { char arr1[] = "abcdef"; char arr2[10] = ""; my_strcpy(arr2, arr1); printf("%s\n", arr2); return 0; }
注意事項(xiàng):
- 源字符串必須以 ‘\0’ 結(jié)束。
- 會(huì)將源字符串中的 ‘\0’ 拷貝到目標(biāo)空間。
- 目標(biāo)空間必須足夠大,以確保能存放源字符串。
- 目標(biāo)空間必須可變。
strcat
函數(shù)功能:
字符串追加,在一個(gè)字符串的末尾追加另外一個(gè)字符串(包括’\0’)。
函數(shù)參數(shù):
char * strcat ( char * destination, const char * source ); # char* 是函數(shù)的返回值,返回的是目標(biāo)空間的起始地址 # source 是要追加的字符串 # destination 是目標(biāo)空間
函數(shù)使用:
#include <stdio.h> #include <string.h> //strcat對(duì)應(yīng)頭文件 int main() { char arr1[] = "world!"; char arr2[20] = "hello "; strcat(arr2, arr1); printf("%s\n", arr2); return 0; }
模擬實(shí)現(xiàn)(初階):
#include <stdio.h> #include <assert.h> //字符串追加可以分為兩部分:1、找到目標(biāo)字符串的末尾 2、字符串拷貝 char* my_strcat(char* dest, const char* src) //const用于保護(hù)源字符串 { assert(dest && src); //保證傳遞過(guò)來(lái)的不是空串 char* ret = dest; //記錄目標(biāo)空間的起始地址 while (*dest != '\0') //找到目標(biāo)空間的末尾 { dest++; } while (*src != '\0') { *dest = *src; dest++; src++; } *dest = *src; //將末尾的'\0'賦給dest return ret; } int main() { char arr1[] = "world!"; char arr2[20] = "hello "; my_strcat(arr2, arr1); printf("%s\n", arr2); return 0; }
模擬實(shí)現(xiàn)(進(jìn)階):
#include <stdio.h> #include <assert.h> //字符串追加可以分為兩部分:1、找到目標(biāo)字符串的末尾 2、字符串拷貝 char* my_strcat(char* dest, const char* src) //const用于保護(hù)源字符串 { assert(dest && src); //保證傳遞過(guò)來(lái)的不是空串 char* ret = dest; //記錄目標(biāo)空間的起始地址 while (*dest++ != '\0'); //找到目標(biāo)空間的末尾 dest--; //抵消最后一次自增的副作用 while (*dest++ = *src++); //字符串拷貝 return ret; } int main() { char arr1[] = "world!"; char arr2[20] = "hello "; my_strcat(arr2, arr1); printf("%s\n", arr2); return 0; }
注意事項(xiàng):
- 源字符串必須以 ‘\0’ 結(jié)束。
- 目標(biāo)空間必須有足夠的大,能容納下源字符串的內(nèi)容。
- 目標(biāo)空間必須可修改。
- strcat 函數(shù)不能自己給自己追加。(追加會(huì)覆蓋掉末尾的’\0’,導(dǎo)致死循環(huán))
strcmp
函數(shù)功能:
字符串比較,以字節(jié)為單位比較兩個(gè)字符串的大小
函數(shù)參數(shù):
int strcmp ( const char * str1, const char * str2 ); # int 函數(shù)返回值 # char* str1 char* str2 用于比較的兩個(gè)字符串
函數(shù)返回值:
>0 : str1 大于 str2; =0 : str1 等于 str2; <0 : str1 小于 str2
函數(shù)使用:
#include <stdio.h> #include <string.h> //strcmp對(duì)應(yīng)頭文件 int main() { char str1[] = "abcdef"; char str2[] = "abq"; int ret = strcmp(str1, str2); printf("%d\n", ret); return 0; }
模擬實(shí)現(xiàn):
#include <stdio.h> #include <assert.h> //assert對(duì)應(yīng)頭文件 int my_strcmp(const char* str1, const char* str2) { assert(str1 && str2); while (*str1 == *str2 && *str1 != '\0' && *str2 != '\0') //找到字符串中不相等字符的位置 { str1++; str2++; } if (*str1 != '\0' && *str2 != '\0') //如果此位置不是字符串末尾,則不相等,返回差值 return *str1 - *str2; return 0; //若前面沒(méi)返回,說(shuō)明相等,返回0 } int main() { char str1[] = "abcdef"; char str2[] = "abq"; int ret = my_strcmp(str1, str2); printf("%d\n", ret); return 0; }
注意事項(xiàng):
- 對(duì)每一對(duì)字符進(jìn)行比較,直到遇到不相等的字符或者遇到’\0’。
- 比較的是每一對(duì)字符的ASCII值。
長(zhǎng)度受限制的字符串函數(shù)
由于strcpy、strcat、strcmp等字符串函數(shù)存在安全隱患(目標(biāo)空間小于源空間等問(wèn)題),C語(yǔ)言還提供了另外幾種相對(duì)安全的字符串函數(shù),即strncpy、strncat、strncmp,這些字符串函數(shù)相比于原字符串函數(shù)多了一個(gè)參數(shù),用于指定操作的字節(jié)數(shù)。(注意:strncpy、strncat、strncmp函數(shù)只是相對(duì)安全,并不是絕對(duì)安全,多一個(gè)參數(shù)只是起到一個(gè)提醒作用)
strncpy
函數(shù)功能:
字符串拷貝,把一個(gè)字符串中num個(gè)字節(jié)的內(nèi)容拷貝到另一個(gè)字符串中去。
函數(shù)參數(shù):
char * strncpy ( char * destination, const char * source, size_t num ); # char* 是函數(shù)的返回值,返回的是目標(biāo)空間的起始地址 # source 是要拷貝的字符串 # destination 是目標(biāo)空間 # num 是要拷貝的字節(jié)數(shù)
函數(shù)使用:
#include <stdio.h> #include <string.h> //strncpy對(duì)應(yīng)的頭文件 int main() { char arr1[] = "abcdef"; char arr2[10] = ""; strncpy(arr2, arr1, sizeof(arr1) / sizeof(arr1[0]) * 1); //sizeof(arr)/sizeof(arr[0]) 求得數(shù)組元素個(gè)數(shù),*1 乘以每個(gè)元素的大小,得到整個(gè)數(shù)組的字節(jié)數(shù) printf("%s\n", arr2); return 0; }
模擬實(shí)現(xiàn):
//模擬實(shí)現(xiàn)strncpy #include <stdio.h> #include <assert.h> char* my_strncpy(char* dest, const char* src, size_t num) { assert(dest && src); char* ret = dest; //記錄目標(biāo)空間的起始地址 while (num--) { *dest = *src; if (*dest == '\0') //如果*dest為0,說(shuō)明將src的結(jié)束字符賦給了dest,這種情況下源字符串的長(zhǎng)度小于num { while (num--) { //如果源字符串的長(zhǎng)度小于num,則拷貝完源字符串之后,在目標(biāo)的后邊追加0,直到num個(gè)。(與庫(kù)函數(shù)的實(shí)現(xiàn)方式保持一致) *dest++ = 0; } break; } dest++; src++; } *dest = '\0'; //在dest的末尾補(bǔ)上結(jié)束字符 return ret; } int main() { char arr1[] = "abc"; char arr2[10]; my_strncpy(arr2, arr1, 4); printf("%s\n", arr2); return 0; }
注意事項(xiàng):
- 拷貝num個(gè)字符從源字符串到目標(biāo)空間。
- 如果源字符串的長(zhǎng)度小于num,則拷貝完源字符串之后,在目標(biāo)的后邊追加0,直到num個(gè)。
strncat
函數(shù)功能:
字符串追加,將一個(gè)字符串中num個(gè)字節(jié)的內(nèi)容追加到另一個(gè)字符串的末尾,并在最后面加上’\0’。
函數(shù)參數(shù):
char * strncat ( char * destination, const char * source, size_t num ); # char* 是函數(shù)的返回值,返回的是目標(biāo)空間的起始地址 # source 是要追加的字符串 # destination 是目標(biāo)空間 # num 是要追加的字節(jié)數(shù)
函數(shù)使用:
#include <stdio.h> #include <string.h> //strncat對(duì)應(yīng)的頭文件 int main() { char arr1[] = "world!"; char arr2[20] = "hello "; strncat(arr2, arr1, sizeof(arr1) / sizeof(arr1[0]) * 1); //sizeof(arr)/sizeof(arr[0]) 求得數(shù)組元素個(gè)數(shù),*1 乘以每個(gè)元素的大小,得到整個(gè)數(shù)組的字節(jié)數(shù) printf("%s\n", arr2); return 0; }
模擬實(shí)現(xiàn):
#include <stdio.h> #include <assert.h> char* my_strncat(char* dest, const char* src, size_t num) { assert(dest && src); char* ret = dest; //記錄目標(biāo)空間的起始地址 //找到dest末尾 while (*dest != '\0') { dest++; } //strncpy while (num--) { *dest = *src; if (*dest == '\0') //如果*dest為0,說(shuō)明將src的結(jié)束字符賦給了dest,這種情況下源字符串的長(zhǎng)度小于num { //如果源字字符串的長(zhǎng)度小于num,則只復(fù)制到終止空字符的內(nèi)容。(與庫(kù)函數(shù)的實(shí)現(xiàn)方式保持一致) return ret; //直接返回 } dest++; src++; } *dest = '\0'; //如果循環(huán)正常結(jié)束,則在dest的末尾補(bǔ)上結(jié)束字符 return ret; } int main() { char arr1[20] = "abcdef"; char arr2[] = "ABCDEF"; my_strncat(arr1, arr2, 5); printf("%s\n", arr1); return 0; }
注意事項(xiàng):
- 將源字符串中num個(gè)字節(jié)的內(nèi)容追加到目標(biāo)字符串的末尾,并在最后添加’\0’。
- 如果源中字符串的長(zhǎng)度小于num,則只復(fù)制到終止空字符的內(nèi)容
strncmp
函數(shù)功能:
字符串比較,比較兩個(gè)字符串中前num個(gè)字節(jié)的大小。
函數(shù)參數(shù):
int strncmp ( const char * str1, const char * str2, size_t num ); # int 函數(shù)返回值 # char* str1 char* str2 用于比較的兩個(gè)字符串 # num 要比較的字節(jié)數(shù)
函數(shù)返回值:
>0 : str1 大于 str2; =0 : str1 等于 str2; <0 : str1 小于 str2
函數(shù)使用:
#include <stdio.h> #include <string.h> //strncmp對(duì)應(yīng)的頭文件 int main() { char arr1[] = "abcdef"; char arr2[] = "abcq"; int len = strncmp(arr2, arr1, 3); printf("%d\n", len); return 0; }
注意事項(xiàng):
- 對(duì)前num對(duì)字節(jié)中的每一對(duì)字符進(jìn)行比較,直到遇到不相等的字符。
- 比較的是每一對(duì)字符的ASCII值。
字符串查找函數(shù)
strstr
函數(shù)功能:
查找子串,查找一個(gè)字符串中是否包含子串。
函數(shù)參數(shù):
char * strstr ( const char *str1, const char * str2); # char* 函數(shù)返回值,返回字符串中子串的起始地址,若找不到,則返回NULL; # char* str1 要搜索的字符串; # char* str2 子串
函數(shù)使用:
#include <stdio.h> #include <string.h> //strstr對(duì)應(yīng)頭文件 int main() { char arr1[] = "abbbcdef"; char arr2[] = "bcd"; char* ret = strstr(arr1, arr2); printf("%s\n", ret); return 0; }
模擬實(shí)現(xiàn)(重要):
#include <stdio.h> #include <assert.h> char* my_strstr(const char* str1, const char* str2) { assert(str1 && str2); //如果中途匹配失敗需要回到str2的起始地址,所以用其他變量標(biāo)識(shí)str2,保證str2的首地址不會(huì)丟失 const char* p1 = str1; const char* p2 = str2; const char* mark = str1; //用來(lái)標(biāo)記每次第一個(gè)字符成功匹配的位置 while (*mark != '\0') { p1 = mark; //從mark處開(kāi)始往后匹配 p2 = str2; //每次匹配后p2回到str2開(kāi)頭 while (*p1 == *p2 && *p1 != '\0' && *p2 != '\0') //一直往后匹配,直到遇到不相等的字符 { p1++; p2++; } if (*p2 == '\0') //如果不相等處p2為'\0'(子串全部匹配成功),則返回Mark處地址 return (char*)mark; mark++; //否則,說(shuō)明這一次匹配失敗,從mark后面一個(gè)字節(jié)處開(kāi)始重新匹配 } return NULL; //字符串找完都沒(méi)有子串就返回空指針 } int main() { char arr1[] = "abbbcdef"; char arr2[] = "bcdq"; char* ret = my_strstr(arr1, arr2); printf("%s\n", ret); return 0;
注意事項(xiàng):
- 被查找的字符串和子串都不能是空串,且都以’\0’結(jié)尾。
- 如果查找成功,返回字符串中子串所在位置的首地址,如果查找失敗,則返回NULL。
注:我們上面模擬實(shí)現(xiàn)的查找子串的函數(shù)效率比較低,如果要追求高效率,則需要使用KMP算法,有關(guān)KMP算法的相關(guān)知識(shí),我會(huì)在后面的文章中進(jìn)行介紹。
strtok
函數(shù)功能:
字符串分割,把一個(gè)字符串按照分割標(biāo)志分割為幾個(gè)字符串。
函數(shù)參數(shù):
char * strtok ( char * str, const char * sep ); # char* 函數(shù)返回值,strtok函數(shù)會(huì)找到str中的下一個(gè)標(biāo)記,并將其用'\0'結(jié)尾,返回一個(gè)指向這個(gè)標(biāo)記的指針; # char* str 指定一個(gè)字符串,它包含了0個(gè)或者多個(gè)由sep字符串中一個(gè)或者多個(gè)分隔符分割的標(biāo)記; # char* sep 一個(gè)字符串,定義了用作分隔符的字符集合;
函數(shù)使用:
#include <stdio.h> #include <string.h> int main() { char* sep = "@."; char email[] = "1684277750@qq.com"; char tmp[20] = ""; //由于strtok函數(shù)會(huì)改變被操作的字符串,所以在使用strtok函數(shù)切分的字符串一般都會(huì)臨時(shí)拷貝一份,操作拷貝的數(shù)據(jù) strcpy(tmp, email); printf("%s\n", strtok(tmp, sep)); //第一次第一個(gè)參數(shù)傳遞被切割字符串的首地址 printf("%s\n", strtok(NULL, sep)); //第二次及以后第一個(gè)參數(shù)傳遞空指針(strtok會(huì)記住上一次切割的位置) printf("%s\n", strtok(NULL, sep)); return 0; }
這里我們知道目標(biāo)字符串會(huì)被分隔符切割為三個(gè)字符串,所以這里我們調(diào)用了三次strtok函數(shù),但是當(dāng)我們不知道目標(biāo)字符串的內(nèi)容時(shí),這種方法顯然就不能用了;那么我們?cè)撊绾握_的使用strtok函數(shù)呢?
我們知道,strtok函數(shù)第一次調(diào)用時(shí)需要傳遞目標(biāo)字符串的地址,其余調(diào)用都只需要傳遞NULL即可,那么我們可以利用這個(gè)特點(diǎn)結(jié)合for循環(huán)的特性來(lái)正確調(diào)用strtok函數(shù)。
#include <stdio.h> #include <string.h> int main() { char* sep = "@."; char email[] = "1684277750@qq.com"; char tmp[20] = ""; //由于strtok函數(shù)會(huì)改變被操作的字符串,所以在使用strtok函數(shù)切分的字符串一般都會(huì)臨時(shí)拷貝一份,操作拷貝的數(shù)據(jù) strcpy(tmp, email); char* ret = NULL; //用來(lái)保存strtok函數(shù)的返回值 for (ret = strtok(tmp, sep); //初始化部分:第一次傳遞tmp的地址 ret != NULL; //判斷部分:只要strtok的返回值ret不為空,說(shuō)明繼續(xù)分割 ret = strtok(NULL, sep)) //調(diào)整部分:第二次及以上傳遞NULL { printf("%s\n", ret); //ret不為空,說(shuō)明字符串沒(méi)被分割完,則打印 } return 0; }
注意事項(xiàng):
- sep參數(shù)是個(gè)字符串,定義了用作分隔符的字符集合;
- 第一個(gè)參數(shù)指定一個(gè)字符串,它包含了0個(gè)或者多個(gè)由sep字符串中一個(gè)或者多個(gè)分隔符分割的標(biāo) 記;
- strtok函數(shù)找到str中的下一個(gè)標(biāo)記,并將其用 \0 結(jié)尾,返回一個(gè)指向這個(gè)標(biāo)記的指針。(注: 由于strtok函數(shù)會(huì)改變被操作的字符串,所以在使用strtok函數(shù)切分的字符串一般都會(huì)臨時(shí)拷貝一份,操作拷貝的數(shù)據(jù) )
- strtok函數(shù)的第一個(gè)參數(shù)不為 NULL ,函數(shù)將找到str中第一個(gè)標(biāo)記,strtok函數(shù)將保存它在字符串中的位置;
- strtok函數(shù)的第一個(gè)參數(shù)為 NULL ,函數(shù)將在同一個(gè)字符串中被保存的位置開(kāi)始,查找下一個(gè)標(biāo)記;
- 如果字符串中不存在更多的標(biāo)記,則返回 NULL 指針;
strerror
函數(shù)功能:
C語(yǔ)言有一系列的庫(kù)函數(shù),當(dāng)這些庫(kù)函數(shù)調(diào)用失敗時(shí),會(huì)返回相應(yīng)的錯(cuò)誤碼,而strerror函數(shù)的作用就是獲取錯(cuò)誤碼對(duì)應(yīng)的錯(cuò)誤信息的首地址,讓使用者知道程序發(fā)生錯(cuò)誤的原因。
函數(shù)參數(shù):
char * strerror ( int errnum ); # char* 函數(shù)返回值,返回錯(cuò)誤碼對(duì)應(yīng)的錯(cuò)誤信息的字符串的地址; # int errnum 錯(cuò)誤碼
函數(shù)使用:
#include <stdio.h> #include <string.h> //strerror的頭文件 int main() { printf("%s\n", strerror(0)); printf("%s\n", strerror(1)); printf("%s\n", strerror(2)); printf("%s\n", strerror(3)); printf("%s\n", strerror(4)); printf("%s\n", strerror(5)); return 0; }
這里有一個(gè)問(wèn)題,C語(yǔ)言中那么多的錯(cuò)誤信息,那么我們需要記住每一個(gè)錯(cuò)誤信息對(duì)應(yīng)的錯(cuò)誤碼嗎?其實(shí),C語(yǔ)言中設(shè)置了一個(gè)全局的用于存放錯(cuò)誤碼的變量errno,只要調(diào)用C語(yǔ)言庫(kù)函數(shù)發(fā)生錯(cuò)誤,那么errno就會(huì)記錄相應(yīng)的錯(cuò)誤碼,所以strerror函數(shù)和errno一般都是配合使用的。
#include <stdio.h> #include <string.h> //strerror對(duì)應(yīng)頭文件 #include <errno.h> //errno對(duì)應(yīng)頭文件 int main() { FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { printf("%s\n", strerror(errno)); return 1; } else { printf("文件打開(kāi)成功!\n"); } return 0; }
字符函數(shù)
字符分類(lèi)函數(shù)
函數(shù) | 如果他的參數(shù)符合下列條件就返回真 |
---|---|
iscntrl | 任何控制字符 |
isspace | 空白字符:空格‘ ’,換頁(yè)‘\f’,換行’\n’,回車(chē)‘\r’,制表符’\t’或者垂直制表符’\v’ |
isdigit | 十進(jìn)制數(shù)字 0~9 |
isxdigit | 十六進(jìn)制數(shù)字,包括所有十進(jìn)制數(shù)字,小寫(xiě)字母a~f,大寫(xiě)字母A~F |
islower | 小寫(xiě)字母a~z |
isupper | 大寫(xiě)字母A~Z |
isalpha | 字母a~z或A~Z |
isalnum | 字母或者數(shù)字,a~z,A~Z,0~9 |
ispunct | 標(biāo)點(diǎn)符號(hào),任何不屬于數(shù)字或者字母的圖形字符(可打?。?/td> |
isgraph | 任何圖形字符 |
isprint | 任何可打印字符,包括圖形字符和空白字符 |
字符轉(zhuǎn)換函數(shù)
函數(shù) | 返回值 |
---|---|
tolower | 返回對(duì)應(yīng)小寫(xiě)字母的ASCII值 |
toupper | 返回對(duì)應(yīng)大寫(xiě)字母的ASCII值 |
內(nèi)存操作函數(shù)
前面我們學(xué)習(xí)的strcpy、strcat、strcmp、strncpy、strncat、strncmp等函數(shù)都是字符串函數(shù),只能對(duì)字符串進(jìn)行相關(guān)操作,如果要對(duì)其他數(shù)據(jù)類(lèi)型,如整形、字符、結(jié)構(gòu)體等進(jìn)行類(lèi)似操作的話(huà),就需要學(xué)習(xí)內(nèi)存操作函數(shù),常見(jiàn)的內(nèi)存操作函數(shù)有memcpy、memmove、memcmp、memset。
memcpy
函數(shù)功能:
內(nèi)存拷貝,將一塊內(nèi)存中num個(gè)字節(jié)的內(nèi)容拷貝到另一塊內(nèi)存中,常用來(lái)處理不重疊內(nèi)存數(shù)據(jù)的拷貝。
函數(shù)參數(shù):
void * memcpy ( void * destination, const void * source, size_t num ); # void* 函數(shù)返回值,返回dest內(nèi)存空間的地址; # void* destination 目標(biāo)內(nèi)存空間地址; # void* source 源空間地址; # size_t num 要拷貝的字節(jié)數(shù);
函數(shù)使用:
#include <stdio.h> #include <string.h> //memcpy對(duì)應(yīng)頭文件 int main() { int arr1[] = { 1,2,3,4,5,6 }; int arr2[10] = { 0 }; memcpy(arr2, arr1, 6 * sizeof(int)); for (int i = 0; i < 6; i++) { printf("%d ", arr2[i]); } return 0; }
模擬實(shí)現(xiàn):
#include <stdio.h> #include <assert.h> void* my_memcpy(void* dest, const void* src, size_t num) { assert(dest && src); void* ret = dest; //保存目標(biāo)空間的地址 while (num--) //以字節(jié)為單位進(jìn)行拷貝 { *(char*)dest = *(char*)src; //強(qiáng)轉(zhuǎn)為char*類(lèi)型后賦值 //這里不要寫(xiě)成(char*)dest++,在某些編譯器下會(huì)報(bào)錯(cuò),因?yàn)閺?qiáng)制類(lèi)型轉(zhuǎn)是一種臨時(shí)效果 dest = (char*)dest + 1; src = (char*)src + 1; } return ret; } int main() { int arr1[] = { 1,2,3,4,5,6 }; int arr2[10] = { 0 }; my_memcpy(arr2, arr1, 6 * sizeof(int)); for (int i = 0; i < 6; i++) { printf("%d ", arr2[i]); } return 0; }
注意事項(xiàng)(重要):
在C語(yǔ)言標(biāo)準(zhǔn)中,memcpy只負(fù)責(zé)處理內(nèi)存不重疊的數(shù)據(jù),內(nèi)存重疊的數(shù)據(jù)的拷貝是memmove函數(shù)負(fù)責(zé)實(shí)現(xiàn)的,即下面這種情況在C語(yǔ)言標(biāo)準(zhǔn)中memcpy函數(shù)是不能實(shí)現(xiàn)的:
memcpy(arr1 + 2, arr1, 4 * sizeof(int));
從上面我們memcpy的模擬實(shí)現(xiàn)中也可以看出,memcpy是從前向后拷貝的,這就導(dǎo)致在拷貝重疊內(nèi)存數(shù)據(jù)時(shí)會(huì)發(fā)生數(shù)據(jù)覆蓋(即arr1[2]中的數(shù)據(jù)在前面賦值中被改為1,導(dǎo)致將arr[2]中的數(shù)據(jù)賦給arr[4]時(shí)不是4,而是1),但是在VS下的memcpy函數(shù)是具備拷貝重疊數(shù)據(jù)的能力的,也就是說(shuō),VS下的memcpy函數(shù)同時(shí)實(shí)現(xiàn)了memmove函數(shù)的功能,但是其他編譯器下的memcpy函數(shù)是否也具備memmove函數(shù)功能是未知的,所以我們?cè)谔幚碇丿B內(nèi)存數(shù)據(jù)拷貝的時(shí)候盡量還是使用memmove函數(shù),以免發(fā)生錯(cuò)誤。
memmove
函數(shù)功能:
內(nèi)存移動(dòng),將一塊內(nèi)存數(shù)據(jù)中的內(nèi)容移動(dòng)覆蓋至另一塊內(nèi)存數(shù)據(jù),常用來(lái)處理重疊內(nèi)存數(shù)據(jù)的拷貝。
函數(shù)參數(shù):
# memmove 函數(shù)的參數(shù)和 memcpy 函數(shù)完全相同 void * memmove ( void* destination, const void * source, size_t num );
函數(shù)使用:
#include <stdio.h> #include <string.h> //memmove對(duì)應(yīng)頭文件 int main() { int arr1[] = { 1,2,3,4,5,6 ,7,8 }; int arr2[10] = { 0 }; memmove(arr1 + 2, arr1, 4 * sizeof(int)); for (int i = 0; i < 8; i++) { printf("%d ", arr1[i]); } return 0; }
模擬實(shí)現(xiàn)(重要):
思路分析:
在memcpy中我們提到在拷貝重疊內(nèi)存的數(shù)據(jù)時(shí)會(huì)發(fā)生內(nèi)存覆蓋的情況,其實(shí)這種覆蓋分為兩種情況:
(1):dest的地址大于src的地址
如圖,如果這時(shí)我們從前往后移動(dòng)的話(huà),那么4就會(huì)覆蓋掉6,從而導(dǎo)致將6賦給8變成4賦給8,所以我們應(yīng)該從后往前移。
(2):dest的地址小于src的地址
如果這時(shí)我們從后往前移動(dòng)的話(huà),那么7就會(huì)覆蓋掉5,導(dǎo)致將5賦給2的時(shí)候變成7賦給2,所以這里我們應(yīng)該從前往后移動(dòng)。
代碼實(shí)現(xiàn):
#include <stdio.h> #include <assert.h> void* my_memmove(void* dest, void* src, size_t num) { assert(dest && src); void* ret = dest; //保存目標(biāo)空間的地址 if (dest < src) { //前 -> 后 等價(jià)于memcpy while (num--) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = (char*)src + 1; } } else { //后 -> 前 while (num--) { *((char*)dest + num) = *((char*)src + num); } } return ret; } int main() { int arr[] = { 1,2,3,4,5,6,7,8,9 }; my_memmove(arr + 1, arr + 3, 4 * sizeof(int)); for (int i = 0; i < 9; i++) { printf("%d ", arr[i]); } return 0; }
memcmp
函數(shù)功能:
內(nèi)存比較,比較兩塊內(nèi)存中前num個(gè)字節(jié)的大小。
函數(shù)參數(shù):
int memcmp ( const void * ptr1, const void * ptr2, size_t num ); # int 函數(shù)返回值; # void* ptr1 void* ptr2 要比較的兩塊內(nèi)存; # size_t num 要比較的字節(jié)數(shù);
函數(shù)返回值:
>0 : ptr1 大于 ptr2; =0 : ptr1 等于 ptr2; <0 : ptr1 小于 ptr2;··
函數(shù)使用:
#include <stdio.h> #include <string.h> //memcmp對(duì)應(yīng)頭文件 int main() { int arr1[] = { 1,2,3,4,5 }; int arr2[] = { 1,2,3 }; int ret = memcmp(arr1, arr2, 3 * sizeof(int)); printf("%d\n", ret); return 0; }
模擬實(shí)現(xiàn):
#include <stdio.h> #include <assert.h> int my_memcmp(const void* ptr1, const void* ptr2, size_t num) { assert(ptr2 && ptr2); size_t count = 0; //用來(lái)標(biāo)記相等的字節(jié)數(shù) while (*(char*)ptr1 == *(char*)ptr2 && count < num) //一直循環(huán),直到找到不相等的字節(jié)數(shù)據(jù) { count++; //進(jìn)入一次循環(huán)表示有一對(duì)字節(jié)相等,count++ ptr1 = (char*)ptr1 + 1; ptr2 = (char*)ptr2 + 1; } if (count < num) //如果count小于num,說(shuō)明兩塊內(nèi)存的前num個(gè)字節(jié)中有不相等的數(shù)據(jù) return *(char*)ptr1 - *(char*)ptr2; //直接返回?cái)?shù)據(jù)的差值 else return 0; //count等于num,說(shuō)明相等 } int main() { int arr1[] = { 1,2,3,4,5 }; int arr2[] = { 1,2,3 }; int ret = my_memcmp(arr1, arr2, 3 * sizeof(int)); printf("%d\n", ret); return 0; }
memset
函數(shù)功能:
內(nèi)存設(shè)置,把一塊內(nèi)存中num個(gè)字節(jié)的內(nèi)容設(shè)置為指定的數(shù)據(jù)。
函數(shù)參數(shù):
void *memset( void *dest, int c, size_t count ); # void* 函數(shù)返回值,返回目標(biāo)空間的地址; # int c 函數(shù)參數(shù),指定你想要初始化的數(shù)據(jù); # size_t count 函數(shù)參數(shù),指定初始化的字節(jié)數(shù)
函數(shù)使用:
#include <stdio.h> #include <string.h> //memset對(duì)應(yīng)頭文件 int main() { char str[] = "hello world"; memset(str, 'x', 5); printf("%s\n", str); return 0; }
模擬實(shí)現(xiàn):
#include <stdio.h> #include <assert.h> void* my_memset(void* dest, int c, size_t num) { assert(dest != NULL); void* ret = dest; //記錄目標(biāo)空間的地址 while (num--) //循環(huán)將num個(gè)字節(jié)的內(nèi)容初始化為指定值 { *(char*)dest = c; dest = (char*)dest + 1; } return ret; } int main() { char str[] = "hello world"; my_memset(str, 'x', 5); printf("%s\n", str); return 0; }
到此這篇關(guān)于C語(yǔ)言字符串函數(shù),字符函數(shù),內(nèi)存函數(shù)使用及模擬實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)C語(yǔ)言字符串內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)不掛科指南之隊(duì)列詳解
這篇博客主要介紹一下隊(duì)列的概念,并且采用 C 語(yǔ)言,編寫(xiě)兩種存儲(chǔ)實(shí)現(xiàn)方式:順序存儲(chǔ)和鏈?zhǔn)酱鎯?chǔ),當(dāng)然還有常規(guī)的隊(duì)列基本操作的實(shí)現(xiàn)算法2022-09-09用while判斷輸入的數(shù)字是否回文數(shù)的簡(jiǎn)單實(shí)現(xiàn)
這篇文章主要介紹了用while判斷輸入的數(shù)字是否回文數(shù)的簡(jiǎn)單實(shí)現(xiàn),需要的朋友可以參考下2014-02-02C語(yǔ)言數(shù)據(jù)結(jié)構(gòu) 棧的基礎(chǔ)操作
這篇文章主要介紹了C語(yǔ)言數(shù)據(jù)結(jié)構(gòu) 棧的基礎(chǔ)操作的相關(guān)資料,需要的朋友可以參考下2017-05-05???????C語(yǔ)言實(shí)現(xiàn)單鏈表基本操作方法
這篇文章主要介紹了???????C語(yǔ)言實(shí)現(xiàn)單鏈表基本操作方法,文章圍繞主題展開(kāi)詳細(xì)介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-05-05C語(yǔ)言編程gcc如何生成靜態(tài)庫(kù).a和動(dòng)態(tài)庫(kù).so示例詳解
本文主要敘述了gcc如何生成靜態(tài)庫(kù)(.a)和動(dòng)態(tài)庫(kù)(.so),幫助我們更好的進(jìn)行嵌入式編程。因?yàn)橛行r(shí)候,涉及安全,所以可能會(huì)提供靜態(tài)庫(kù)或動(dòng)態(tài)庫(kù)供我們使用2021-10-10C語(yǔ)言結(jié)課設(shè)計(jì)之計(jì)算器功能
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言結(jié)課設(shè)計(jì)之計(jì)算器功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-02-02C++動(dòng)態(tài)規(guī)劃之背包問(wèn)題解決方法
這篇文章主要介紹了C++動(dòng)態(tài)規(guī)劃之背包問(wèn)題解決方法,實(shí)例分析了背包問(wèn)題的原理與C++實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-04-04