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