- 字符串函数(下列头文件均为“string.h”)
- 1.==strlen== (求字符串长度)
- 原型
- 模拟实现
- 2.==strcpy== (字符串拷贝)
- 原型
- 模拟实现
- 3.==strcat==(字符串拼接)
- 原型
- 模拟实现
- 4.==strcmp==(字符串比较)
- 原型
- 模拟实现
- 5.strncpy(长度受限字符串拷贝)
- 原型
- 6.strncat(长度受限字符串拼接)
- 原型
- 7.strncmp(长度受限字符串比较)
- 原型
- 8.==strstr==(字符串子串查找)
- 原型
- 应用
- 模拟实现
- 9.==strtok==(字符串分割)
- 原型
- 应用
- 10.strerror(错误报告
- 原型
- 应用
- 字符分类函数
- 内存操作函数(下列头文件均为“string.h”)
- 1.==memcpy==(按字节内存拷贝)
- 原型
- 模拟实现+特殊应用
- 2.==memmove==( 可按字节拷贝两个重叠内存)
- 原型
- 模拟实现+特殊应用
- 3.==memset==(按字节内存赋值)
- 原型
- 应用
- 4.memcmp(比较内存内容大小)
- 原型
- 字符串计数不需修改字符串,因此用conut修饰;
#include#include #include #pragma warning(disable:4996) //方法1:计数器法--需要单独设置一个变量 size_t myStrlen1(const char *arr) //字符串不需修改字符串,因此用conut修饰 //size_t在vs2013指无符号整型unsigned int { assert(arr); //assert函数用来判断字符串是否不为空 int count = 0; while (*arr) { count++; arr++; } return count; } //方法2:递归法--不创建临时变量 size_t myStrlen2(const char *arr) { assert(arr); if (*arr == ' ') { return 0; } else { return 1 + myStrlen2(arr + 1); } } //方法3:指针法--利用尾指针-头指针获得长度 size_t myStrlen3(const char *arr) { assert(arr); char *p = arr; while (*p != ' ') { p++; } return p - arr; } int main() { const char *arr = "abcd1234"; int len1 = myStrlen1(arr); printf("方法1字符串arr长度为:%dn", len1); int len2 = myStrlen2(arr); printf("方法2字符串arr长度为:%dn", len1); int len3 = myStrlen3(arr); printf("方法3字符串arr长度为:%dn", len1); system("pause"); return 0; }
2.strcpy (字符串拷贝) 原型
- 将后面的参数拷贝到前面的参数,无需修改后者,因此后面参数用const修饰;
- 注意后面的字符串参数大小不能大于前者,否则会报错;
- 该函数返回值为char *类型,这是为了支持该类库函数的链式调用;
#include#include #include #pragma warning(disable:4996) //编程习惯之-变量名:ret->result(结果);src->source(源数据);dest->destination(目标) char *myStrcpy(char *dest, const char *src) { assert(src); assert函数用来判断字符串是否不为空 assert(dest); char * ret = dest; while ((*dest++ = *src++)); return ret; } int main() { const char *src = "abcd"; char dest[30] = { "12345" }; printf("before:%sn",dest); printf("after:%sn", myStrcpy(dest, src)); system("pause"); return 0; }
3.strcat(字符串拼接) 原型
- 将后面的参数拼接到前面的参数,无需修改后者,因此后面参数用const修饰;
- 前者字符串空间须足够大以容纳拼接后字符串;
- 注意不能进行自我拼接(这样拼接时会出现原始串不断增加的情况);
- 该函数返回值为char *类型,这是为了支持该类库函数的链式调用;
#include#include #include #pragma warning(disable:4996) char *myStrcpy(char *dest, const char *src) { assert(src); assert(dest); char * ret = dest; while (*dest) //将指针指向dest字符串尾部 { dest++; } while ((*dest++ = *src++)); //从尾部开始将后者字符串拼接 return ret; } int main() { const char *src = "abcd"; char dest[30] = { "12345" }; printf("before:%sn", dest); printf("after:%sn", myStrcpy(dest, src)); system("pause"); return 0; }
4.strcmp(字符串比较) 原型
- 两字符串进行大小比较,无需修改字符串,因此两参数用const修饰;
- 字符串比较是通过两字符串第一个不同的字符的ASSIC码值来判断;
- 若str1>str2,返回1;若str1=str2,返回0;若str1
#include#include #include #pragma warning(disable:4996) int myStrcmp(const char *str1, const char *str2) { int ret = 0; assert(str1); assert(str2); //两字符串指针相减,若相同则看下一个字符,直到出现大小不同的字符或' '。 while (!(ret = *(unsigned char *)str1 - *(unsigned char *)str2) && *str2) { ++str1, ++str2; } if (ret < 0) { ret = -1; } else if (ret>0) { ret = 1; } return (ret); } int main() { const char *str1 = "abcd4"; const char *str2 = "abcd5"; int comp = myStrcmp(str1, str2); if (comp > 0) { printf("str1>str2n"); } else if (comp < 0) { printf("str1
5.strncpy(长度受限字符串拷贝) 原型
- “安全拷贝”函数,即相比strcpy函数来说,参数增加了需要拷贝后者字符串的长度参数;
- 拷贝后不会自带’ ’,需要自己加;
6.strncat(长度受限字符串拼接) 原型
- “安全拼接”函数,相比strcat函数来说,参数增加了需要拼接后者字符串的长度参数;
- 默认会在拼接后的字符串加’ ’;
7.strncmp(长度受限字符串比较) 原型
- “安全比较”函数,比较两字符串的前num个字符;
- 运算规则同strcmp;
8.strstr(字符串子串查找) 原型应用
- 该函数能返回str1里第一次出现str2函数的第一个字符地址;
- 替换一段字符串的某一个字串
- 通过while循环实现查找一段字符串中某字符串出现的位置即次数;
(以下为两个strtr的应用举例)//strstr example_1 替换 //将一个长字符串的一个小字符串替换 int main() { char str[] = "This is a simple string."; char *pch; pch = strstr(str, "simple"); strncpy(pch, "sample", 6); puts(str); system("pause"); return 0; }//strstr example_2 查找 //从一个长字符串找到小字符串的具体位置 int main() { const char *s1 = "abcd1234worldABCD-1world,nkworld."; const char *s2 = "world"; const char *s = s1; int count = 0; char *sub_str = NULL; while (1) { sub_str = strstr(s, s2); if (sub_str == NULL) { break; } count++; //sub_str = strcpy(sub_str, s2); printf("第%d个%s的位置:%dn", count, s2, sub_str - s1); s = sub_str + 1; } system("pause"); return 0; }模拟实现char *myStrstr(const char*str1, const char *str2) { assert(str1); assert(str2); char* str_p = (char*)str1; char* str_q = (char*)str2; char* judge_p = str_p; //外部循环瞄点 if (*str2 == ' ') { return NULL; } while (*judge_p!=' ') { const char* move_p = judge_p ; //内部循环str1比较指针 char* judge_q = str_q; //内部循环str2比较指针 while (*move_p && *judge_q && *move_p == *judge_q) { move_p++; judge_q++; } if (*judge_q == ' ') { return judge_p; } judge_p++; } return NULL; } int main() { const char *s1 = "abcd1234world5678"; const char *s2 = "world"; char *s = myStrstr(s1, s2); printf("before:%sn", s1); printf("after:%sn", s); system("pause"); return 0; }
9.strtok(字符串分割) 原型应用
- 分割字符串函数:遍历参数一所指向的字符串,遇到参数二所包含的分割字符后即返回对应字符串首指针;
- 第二个参数可存放多个分割字符,比如:",._ "(双引号内的字符都是,包括空格);
- 一次使用只能返回一个分割字串,因为只有一个返回值;
- 即如果要多次对一个字符串使用该函数,第一次使用后下一次传参只能将参数一设为NULL,否则将清空记录,从新对该字符串起始位置遍历;
- strtok库函数内部有static变量保存原字符串地址作为静态全局变量,当分割一个后,自动移向第一个分隔符后一个地址;
- 分割字符串本质是将原字符串的对应分隔字符转为’ ’;
- 【注意】由上个条例可知该库函数会修改参数一,故参数一所指向的字符串不能定义在字符常量区;
- 通过多次引用该函数将一个长字符串分割为多个小字符串;
- 注意:由上面的特性4,5可知,重复调用函数以分割多个子字符串时,第一个参数不用变;
//strtok example_1 //将一个长字符串分割为多个小字符串 int main() { char s1[] = "You are so beautiful,but not belong to me."; char *s = strtok(s1, ", ."); while (s != NULL) { printf("%sn", s); s = strtok(NULL, ", ."); } system("pause"); return 0; }
10.strerror(错误报告 原型应用
- 通过错误码返回错误信息;
- 错误码的理解:Eg:return 0;中的0就是main函数返回的一个“错误码”,一般0代表success;
- 错误码的存在方便计算机,错误信息方便程序员;计算机擅长处理数字但不擅长处理字符串,与人相反;
- C语言系统全局变量 errno ,在使用C库函数出错时,会自动设置相应错误信息的错误码;
#include#include #include #include #pragma warning(disable:4996) //strerror example //通过打开错误文件了解错误信息与错误码 //使用系统全局变量errno必须加头文件 int main() { printf("before errno:%dn", errno); //输出errno原始内容 FILE * pf = NULL; pf = fopen("test.text", "r"); if (pf == NULL) { printf("after errno:%dn", errno); //输出errno遇到错误后自动修改的内容 printf("ERROR Opening file test.text:%sn", strerror(errno)); } system("pause"); return 0; }
字符分类函数字符分类函数函数(若其参数符合下列条件就返回真) 头文件:
iscntrl :任何控制字符
isspace :空白字符:空格‘ ’,换页‘f’,换行’n’,回车‘r’,制表符’t’或者垂直制表符’v’
isdigit :十进制数字 0~9
isxdigit :十六进制数字,包括所有十进制数字,小写字母af,大写字母AF
islower :小写字母a~z
isupper :大写字母A~Z
isalpha :字母az或AZ
isalnum :字母或者数字,az,AZ,0~9
ispunct :标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph :任何图形字符
isprint :任何可打印字符,包括图形字符和空白字符
内存操作函数(下列头文件均为“string.h”)1.memcpy(按字节内存拷贝) 原型
- mem系列库函数的操作基本单位是字节
- 参数类型为void *表明可以对任何类型进行该操作
模拟实现+特殊应用
- 不能进行自我拷贝,这也是与memmove的区别所在;(但现在的编辑器已支持,意味着两函数没有区别)
#include#include #include #include #pragma warning(disable:4996) //模拟实现memcpy //test2与test3为特殊应用 void* myMemcpy(void* dest, const void* src, size_t len) { void* ret = dest; assert(dest); assert(src); while (len--) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = (char*)src + 1; } return ret; } int main() { const char* src = "abcd"; char dest[30] = { "12345" }; //test1.普通测试 printf("before:%sn", dest); printf("after:%sn", (char*)myMemcpy(dest, src, strlen(src))); //test2.正向自我拷贝: myMemcpy(dest, dest + 1, (strlen(dest) - 1)); printf("正向自我拷贝:%sn", dest); //test3.逆向自我拷贝 myMemcpy(dest + 1, dest, (strlen(dest) - 1)); printf("逆向自我拷贝:%sn", dest); system("pause"); return 0; }
- 逆向拷贝的错误体现了其不能自我拷贝
- 具体原因:出现内存重叠,该函数一边拷贝一边将拷贝后的数据当作新数据拷贝给下一个;
2.memmove( 可按字节拷贝两个重叠内存) 原型 模拟实现+特殊应用#include#include #include #include #pragma warning(disable:4996) //模拟实现memmove //test2与test3为特殊应用 void* myMemmove(void* dest, const void* src, size_t len) { void* ret = dest; assert(dest); assert(src); char* p = (char*)dest; char* q = (char*)src; if (p > q && p < q + len) //逆向自我赋值情况,特殊处理 { //从尾部赋值即可 从右向左 p += (len-1); q += (len-1); while (len--) { *p = *q; p--; q--; } } else { //从左向右 while (len--) { *p = *q; p++; q++; } } return ret; } int main() { const char* src = "abcd"; char dest[30] = { "12345" }; //test1.普通测试 printf("before:%sn", dest); printf("after:%sn", (char*)myMemmove(dest, src, strlen(src))); //test2.正向自我拷贝: myMemmove(dest, dest + 1, (strlen(dest) - 1)); printf("正向自我拷贝:%sn", dest); //test3.逆向自我拷贝 myMemmove(dest + 1, dest, (strlen(dest) - 1)); printf("逆向自我拷贝:%sn", dest); system("pause"); return 0; }
- 逆向拷贝未出错,与memcpy函数差异既体现于此;
- 对于库函数来说,方法更复杂也更完善,现如今两函数无差别;
3.memset(按字节内存赋值) 原型应用
- 功能:将ptr所指向的地址逐字节将num个字节初始化为value;
- size_t为无符号整数类型简写;
#include#include #include #pragma warning(disable:4996) //memset可以使数组的初始化不用循环 int main() { int a[5]; memset(a, 0, sizeof(a)); //注意不是sizeof(a) / sizeof(a[0]) int b[5]; memset(b, 1, sizeof(b)); //按字节初始化 故赋值1不会达到预期 for (int i = 0; i < 5; i++) { printf("%d ", a[i]); } printf("n"); for (int j = 0; j < 5; j++) { printf("%d ", b[j]); } printf("n"); system("pause"); return 0; }
4.memcmp(比较内存内容大小) 原型
- 比较时严格按照ASCII码处理,包括数字类型;
- 可以比较ASCII任何字符,而strcmp只能比较字符;
- 若ptr1>ptr2,返回1;若ptr1=ptr2,返回0;若ptr1



