C语言详解如何应用模拟字符串和内存函数
1.strlen 求字符串长度
size_t strlen ( const char * str ); //返回值是unsigned int类型
使用案例:
#include <stdio.h> int main() { char arr[] = { "abcde" }; printf("%d\n",strlen(arr)); return 0; }
我们知道在arr数组里,最后一个字符'e'后,默认是'\0',而strlen遇到'\0'则会结束运行,且返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' )。
我们可以根据这个来对strlen进行模拟实现:
1.计数法
#include <stdio.h> #include <assert.h> int my_strlen(const char* str) { assert(str); int count = 0; //计数 while (*str) //解引用判断元素是否为'\0' { count++; str++; //地址++ } return count; //返回计数 } int main() { int len = my_strlen("abcdef"); printf("%d\n", len); return 0; }
2.不创建临时变量计数器-递归
#include <stdio.h> #include <assert.h> int my_strlen(const char * str) { assert(str); if (*str == '\0') return 0; else return 1 + my_strlen(str + 1); } int main() { char arr[] = {"abcdef"}; int ret = my_strlen(arr); printf("%d\n", ret); return 0; }
3.指针-指针的方式
#include <stdio.h> #include <assert.h> int my_strlen(char *s) { char *p = s; while (*p != '\0') p++; return p - s; //指针相减是长度 } int main() { char arr[] = {"abcdef"}; int ret = my_strlen(arr); printf("%d\n", ret); return 0; }
2.长度不受限制的字符串函数
1.strcpy
char* strcpy(char * destination, const char * source );
源字符串必须以 '\0' 结束。
会将源字符串中的 '\0' 拷贝到目标空间。
目标空间必须足够大,以确保能存放源字符串。
目标空间必须可变。
使用案例:
#include <stdio.h> #include <assert.h> int main() { char arr1[] = {'a', 'b', 'c', 'd', 'e', 'f', '\0'}; char arr2[20] = "xxxxxxxxxxxx"; strcpy(arr2, arr1); printf("%s\n", arr2); return 0; }
模拟实现:
#include <stdio.h> #include <assert.h> char* my_strcpy(char* dest, const char* src) { char* ret = dest; //保留起始地址 assert(dest && src); while (*dest++ = *src++) //这里不打印'\0'之后的 { ; } return ret; } int main() { char arr1[] = {'a', 'b', 'c', 'd', 'e', 'f', '\0'}; char arr2[20] = "xxxxxxxxxxxx"; my_strcpy(arr2, arr1); printf("%s\n", arr2); return 0; }
2.strcat
char * strcat ( char * destination, const char * source );
源字符串必须以 '\0' 结束。
目标空间必须有足够的大,能容纳下源字符串的内容。
目标空间必须可修改。
使用案例:
#include <stdio.h> #include <assert.h> int main() { char arr1[30] = "hello";//注意初始化方式,必须要包含'\0' char arr2[] = "world"; strcat(arr1, arr2); printf("%s\n", arr1); return 0; }
模拟实现:
#include <stdio.h> #include <assert.h> char* my_strcat(char* dest, const char* src) { char* ret = dest; //保留起始地址,方便找回打印 assert(dest && src); //1. 找目标空间中的\0,打印的起点 while (*dest) { dest++; } //2. 追加内容到目标空间 while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[30] = "hello";//注意初始化方式,必须要包含'\0' char arr2[] = "world"; printf("%s\n", my_strcat(arr1, arr2)); return 0; }
3.strcmp-比较字符串首字母的大小
int strcmp ( const char * str1, const char * str2 );
第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字
使用案例:
#include <stdio.h> #include <assert.h> int main() { char arr1[] = "degh"; char arr2[] = "bcdefx"; int ret = strcmp(arr1, arr2); if (ret<0) { printf("arr1<arr2"); } else if (ret >0) { printf("arr1>arr2"); } else { printf("arr1==arr2"); } return 0; }
就是根据返回的值来判断两个字符串大小
模拟实现:
//模拟实现strcmp-比较对应位置字符串大小 //相同的话地址各向后加一继续比较 //注意strcmp返回的是大于等于或小于0的整型 #include <stdio.h> #include <assert.h> int my_strcmp(const char* str1, const char*str2) { assert(str1 && str2); while (*str1 == *str2) { if (*str1 == '\0') return 0; //若相等,各向后+1继续比较 str1++; str2++; } return *str1 - *str2; } int main() { char arr1[] = "degh"; char arr2[] = "bcdefx"; int ret = my_strcmp(arr1, arr2); if (ret<0) { printf("arr1<arr2"); } else if (ret >0) { printf("arr1>arr2"); } else { printf("arr1==arr2"); } return 0; }
3.长度受限制的字符串函数
1.strncpy
char * strncpy ( char * destination, const char * source, size_t num );
相较于strcpy,strncpy函数有了对字符长度的限制,更加的灵活
使用案例:
#include <stdio.h> #include <assert.h> int main() { char arr1[] = {'a', 'b', 'c', 'd', 'e', 'f', '\0'}; char arr2[20] = "xxxxxxxxxxxx"; strncpy(arr2, arr1,4); printf("%s\n", arr2); return 0; }
2.strncat
char * strncat ( char * destination, const char * source, size_t num );
使用案例:
#include <stdio.h> #include <assert.h> int main() { char arr1[30] = "hello"; char arr2[] = "world"; strncat(arr1, arr2,3); printf("%s\n", arr1); return 0; }
若给的数字超过了arr2的长度,如:
strncat(arr1, arr2,7);
则
只会追加到arr2元素中的'\0'结束。
3.strncmp
int strncmp ( const char * str1, const char * str2, size_t num );
使用案例:
#include <stdio.h> #include <assert.h> int main() { char arr1[] = "degh"; char arr2[] = "bcdefx"; int ret = strncmp(arr1, arr2,3); if (ret<0) { printf("arr1<arr2"); } else if (ret >0) { printf("arr1>arr2"); } else { printf("arr1==arr2"); } return 0; }
这里的数字3表明比较的是前3个 字符串的大小。
4.strstr-找子串
char * strstr ( const char *str2, const char * str1);
在字符串里找子串,返回找到子串的起始地址
找不到则返回空指针
使用案例:
#include <stdio.h> int main() { char arr1[] = "abbbcdef"; char arr2[] = "bbcq"; char* ret = strstr(arr1, arr2); //因为返回的是地址,要用指针接收 if (NULL == ret) printf("没找到\n"); else printf("%s\n", ret); return 0; }
模拟实现:
#include <stdio.h> #include <assert.h> char* my_strstr(const char* str, const char* substr) { const char* s1 = str; const char* s2 = substr; const char* cur = str; //记录字符串起始位置,在改变 assert(str && substr); if (*substr == '\0') { return (char*)str; } while (*cur) //如果*cur为'\0',则找不到,返回空指针 { s1 = cur; s2 = substr; while (*s1 && *s2 && *s1 == *s2) //*s1 *s2不为'\0' { s1++; s2++; } if (*s2 == '\0') //找到了 return (char*)cur; //返回起始位置 cur++; } return NULL; } int main() { char arr1[] = "abbbcdef"; char arr2[] = "bbcq"; char* ret = my_strstr(arr1, arr2); if (NULL == ret) printf("没找到\n"); else printf("%s\n", ret); return 0; }
5.strtok
应用:将字符串里面除了分隔符外的其他内容提出来
char * strtok ( char * str, const char * sep );
第一个参数指定一个字符串,包含了字符串和分隔符
sep参数是个字符串,定义了用作分隔符的字符集合
例如:
const char* p = "*.-"; char arr[] = "sukabulie*wula.wushi-baga";
sep=p
str=arr
注:strtok会改变第一个字符串里的内容(将分隔符改为\0),因此不能直接将源字符串传过去,可以临时拷贝一份
char buf[50] = { 0 }; strcpy(buf,arr);
那么接下来要传参时第一个参数用buf就好。
注:strtok函数的第一个参数不为空指针 ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
strtok函数的第一个参数为空指针 ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
如果字符串中不存在更多的标记,则返回空指针。
用法:
char* str = strtok(buf, p);//sukabulie printf("%s\n", str); str = strtok(NULL, p);//wula printf("%s\n", str); str = strtok(NULL, p);//wushi printf("%s\n", str); str = strtok(NULL, p);//baga printf("%s\n", str);
打印效果:
我们发现这样的写法很笨,当我们不知道一个字符串它有多少分段时就很麻烦,我们可以用for循环来解决这个问题。
改进:
#include <stdio.h> #include <string.h> int main() { const char* p = "*.-"; char arr[] = "sukabulie*wula.wushi-baga"; char buf[50] = { 0 }; strcpy(buf, arr); for (char* str = strtok(buf, p); str != NULL; str = strtok(NULL, p)) { printf("%s\n",str); } return 0; }
6.strerror
返回错误码所对应的错误信息
C语言中规定了一些错误码及它所对应的意思(错误信息)
char * strerror ( int errnum );
int errnum 是错误码 函数返回的是错误信息的起始地址
展示:
#include <stdio.h> int main() { int i = 0; for (i = 0; i < 10; i++) { printf("%s\n", strerror(i)); } return 0; }
使用方式:
当我们运行一个程序遇到错误,跑不过去时,可以在他执行错误代码段的下一行用
printf("%s\n",strerror(errno)); //注意要引用头文件 #include <errno.h>
errno是c语言提供的一个全局变量,可直接使用,当程序发生错误时,他会改变为相对应的错误码,我们就可以用strerror函数得到相对应的错误信息。
7.memcpy-不重复内存拷贝
void * memcpy ( void * destination, const void * source, size_t num );
将source里的元素通过字节数来拷贝到destination中
使用案例:
#include <stdio.h> #include <string.h> int main() { int arr1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; int arr2[5] = { 0 }; memcpy(arr2,arr1,5*sizeof(arr2[0])); int i = 0; for (i=0;i<5;i++) { printf("%d\n",arr2[i]); } return 0; }
模拟实现:
#include <stdio.h> #include <string.h> #include <assert.h> void* my_memcpy(void* dest, const void*src, size_t num) { void* ret = dest; //保留要返回的起始地址 assert(dest&&src); while (num--) //先使用后--,一个字节一个字节的覆盖 { *(char*)dest = *(char*)src;//强制类型转换是临时的 dest = (char*)dest+1; //因此改变地址时也要强制类型转换 src = (char*)src+1; } return ret; } int main() { int arr1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; int arr2[5] = { 0 }; my_memcpy(arr2, arr1, 5 * sizeof(arr2[0])); int i = 0; for (i = 0; i<5; i++) { printf("%d\n", arr2[i]); } return 0; }
注意:c语言中要求memcpy只要可以拷贝没有重复的内存就可以了,但是在vs下memcpy也可以处理重复的内存。
若按我们写的来举例,我们的memcpy是不支持重复内存的处理:
int main() { int arr1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; int arr2[5] = { 0 }; my_memcpy(arr1+2, arr1, 5 * sizeof(arr1[0])); int i = 0; for (i = 0; i<9; i++) { printf("%d\n", arr1[i]); } return 0; }
如若是这样拷贝,那么我们想的打印出来的应该是1 2 1 2 3 4 5 8 9
实际上是:1 2 1 2 1 2 1 8 9
用memcpy来实现:
int main() { int arr1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; int arr2[5] = { 0 }; memcpy(arr1+2, arr1, 5 * sizeof(arr1[0])); int i = 0; for (i = 0; i<9; i++) { printf("%d\n", arr1[i]); } return 0; }
在vs下库函数memcpy功能更加强大,可处理重复内存,c语言只要求它能处理不重复的内存即可,处理重复内存时用库函数memmove,因此我们模拟的memcpy正确。
8.memmove-可处理重复内存拷贝
void * memcpy ( void * destination, const void * source, size_t num );
类型与memcpy相同
使用案例:
#include <stdio.h> #include <string.h> int main() { int arr1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; int arr2[5] = { 0 }; memmove(arr1 + 2, arr1, 5 * sizeof(arr1[0])); int i = 0; for (i = 0; i<9; i++) { printf("%d\n", arr1[i]); } return 0; }
模拟实现:
分析:
#include <stdio.h> #include <string.h> #include <assert.h> void* my_memmove(void* dest,const void* src,size_t num) { void* ret = dest; assert(dest&&src); if (dest < src) //前向后 { while (num--) { *(char*)dest = *(char*)src; dest = *(char*)dest+1; src = *(char*)src + 1; } } else //后向前 { while (num--) { *((char*)dest+num)=*((char*)src + num); // //根据图来分析 } } } int main() { int arr1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; int arr2[5] = { 0 }; my_memmove(arr1 + 2, arr1, 5 * sizeof(arr1[0])); int i = 0; for (i = 0; i<9; i++) { printf("%d\n", arr1[i]); } return 0; }
9.memcmp
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
比较字节中数的大小
返回的是大于等于或小于0的无符号整型
使用方式与strncmp相似,但是比较的类型不局限于字符串,更加广泛
案例:
#include <stdio.h> #include <string.h> int main() { int arr1[] = { 1,2,7,4,5 }; int arr2[] = { 1,2,3,4,5 }; int ret = memcmp(arr1, arr2, 9); //比较arr1和arr2前九个字节中数的大小 printf("%d\n", ret); return 0; }
10.memset
void* memset(void* dest,int c,size_t count)
第一个参数是目标地址,第二个参数是要被设置的内容,第三个参数是个数
以字节为单位修改
应用方式:
#include <stdio.h> #include <string.h> int main() { char arr[20] = { 0 }; memset(arr, 'x', 10); return 0; }
到此这篇关于C语言详解如何应用模拟字符串和内存函数的文章就介绍到这了,更多相关C语言 字符串 内存函数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
c++ TCHAR转string导致中文缺失或乱码问题及解决
这篇文章主要介绍了c++ TCHAR转string导致中文缺失或乱码问题及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教2023-08-08
最新评论