目录
前言:
函数介绍:
1.1 strlen
1.2 strcpy
1.3 strcat
1.4 strcmp
1.5 strstr
1.6 strtok
1.7 strerror
1.8 perror
2. 字符分类函数
2.1 memcpy
2.2 memmove
前言:
C语言中,字符串函数和字符函数的使用是很频繁的,如果我们能够熟练使用,能够帮助我们解决很多的字符问题。
函数介绍:
1.1 strlen
格式:strlen( const char* str )
字符串以 ‘ ’ 作为结束标志,返回的是 ‘ ’ 前面出现的字符个数。(不包括 ‘ ’)
参数指向的字符串必须要以 ‘ ’ 结尾,不然结果就是未知的了。
函数的返回值是 size_t 类型,是无符号的。
使用:
#include
#include
int main()
{
char arr1[] = "abcdefg";
printf("%dn", strlen(arr1));
return 0;
}
实现的三种方式:
#include#include int main() { char arr1[] = "abcdefg"; printf("%dn", strlen(arr1)); return 0; }
廒1、局部变量统计
size_t my_strlen(char* str) { int count = 0; while (*str != ' ') { count++; str++; } return count; }每走过一个不是 ‘ ’ 的字符,局部变量就+1,最后返回这个值。
廒2、递归调用
size_t my_strlen(const char* str) { if (*str != ' ') { return 1 + my_strlen(str + 1); } else { return 0; } }如果不是 ‘ ’ ,那么返回1+my_strlen。否则,就返回0。
廒3、指针法
size_t my_strlen(const char* str) { const char* temp = str; while (*str != ' ') { str++; } return (str - temp); }用一个局部变量记录指针初始位置,如果不是遇到 ‘ ’ 指针就一直往后走,最后返回指针-局部变量。(两个指针相减得到之间相差元素个数)
缺点:
这段代码的结果就是,hehe,因为返回值是无符号整形,而strlen(p1)-strlen(p2)得到的值是负数,自动转为非负的数,最终得到一个正整数。所以还是会打印hehe。所以我们应该避免相减为负数的情况,直接改成 strlen(p2) > strlen(p2) 就好了。
1.2 strcpy
格式:strcpy( char* dest,const char* src )
源字符串必须以 ‘ ’ 结束。
会将源字符串的 ‘ ’ 拷贝到目标空间。
目标空间必须足够大,确保能够放得下源字符串。
目标空间必须可修改。
如果是 char* p=“abcdef”;这样是不可修改的,因为指针指向的是常量字符串
使用:
#include#include int main() { char arr1[20] = "ABCDEFG"; char arr2[30]; strcpy(arr2, arr1); printf(arr2); return 0; }
实现:
char* my_strcpy(char* dest, const char* src)
{
assert(dest);
assert(src);
char* temp = dest;
while (*dest++ = *src++)
{
;
}
return temp;
}
断言assert判断传入指针非空,创建一个临时变量记录木目的空间首地址,然后把src指向的空间中的内容赋值给dest,然后++。使用断言需要包含头文件
注意:由于最后遇到 ‘ ’ 了,它的ASCII码值是0,所以会退出循环。
char* my_strcpy(char* dest, const char* src)
{
assert(dest);
assert(src);
char* temp = dest;
while (*dest++ = *src++)
{
;
}
return temp;
}
断言assert判断传入指针非空,创建一个临时变量记录木目的空间首地址,然后把src指向的空间中的内容赋值给dest,然后++。使用断言需要包含头文件
注意:由于最后遇到 ‘ ’ 了,它的ASCII码值是0,所以会退出循环。
1.3 strcat
格式:strcat( char* dest,const char* src )
源字符串必须以 ‘ ’ 结束。
目标空间足够大,能够容纳下源字符串的内容。
目标空间必须可以修改。
字符串是不可以给自己追加的,不然陷入死循环。
使用:
#include
#include
int main()
{
char arr1[20] = "hello ";
char arr2[20] = "wrold";
strcat(arr1, arr2);
printf("%s", arr1);
return 0;
}
实现:
char* my_strcat(char* dest, const char* src)
{
assert(dest);
assert(src);
char* temp = dest;
while (*dest)
{
dest++;
}
while (*dest++ = *src++)
{
;
}
return temp;
}
由于不需要对src进行修改,所以加上const修饰,增加代码健壮性。第一个while循环能让指针找到结尾 ‘ ’ 处,然后第二个循环把每一个src处的字符赋值给dest。最后返回dest的首元素地址。
#include#include int main() { char arr1[20] = "hello "; char arr2[20] = "wrold"; strcat(arr1, arr2); printf("%s", arr1); return 0; }
char* my_strcat(char* dest, const char* src) { assert(dest); assert(src); char* temp = dest; while (*dest) { dest++; } while (*dest++ = *src++) { ; } return temp; }由于不需要对src进行修改,所以加上const修饰,增加代码健壮性。第一个while循环能让指针找到结尾 ‘ ’ 处,然后第二个循环把每一个src处的字符赋值给dest。最后返回dest的首元素地址。
1.4 strcmp
格式:strcmp( const char* str1,const char* str2 )
第一个字符串大于第二个字符串,则返回大于0的数字。
第一个字符串等于第二个字符串,则返回0。
第一个字符串小于第二个字符串,则返回小于0的数字。
判断两个字符是靠他们的ASCII码值来比较的,所以小写字母大于大写字母。
使用:实现:#include#include int main() { char arr1[20] = "hello "; char arr2[20] = "Hello"; int ret = strcmp(arr1,arr2); printf("%dn", ret); return 0; }
int my_strcmp(const char* s1, const char* s2) { assert(s1); assert(s2); while (*s1 == *s2) { //让s1和s2比完了就停下来 if (*s1 == ' ') { return 0; } s1++; s2++; } //直接返回他们差值 return *s1 - *s2; }第一个while循环让两个指针指针指向的字符串逐个字符比较。如果其中*s1= ‘ ’,由于进入循环是要两个比较的字符相等,说明两个都是结尾了,两个字符串比较到结尾说明他们相等,所以return 0。如果退出循环了,就返回 *s1 - *s2,如果小于返回的值就是负数,如果大于返回的值就是正数。
1.5 strstr
格式:strstr( const char* str1,const char* str2 )
用于查找子集。
第二个字符串如果是第一个字符串的子集,那么返回第二个字符串在第一个字符串中的首元素地址。否则,返回空指针。
使用:实现:#include#include int main() { char arr1[20] = "helio world"; char arr2[20] = "world"; char* p = strstr(arr1, arr2); printf("%sn", p); return 0; }
//s2在s1中查找,cp代表每次要查找的起始位置,避免了部分符合回不去的情况. char* my_strstr(const char* str1, const char* str2) { //断言 assert(str1); assert(str2); const char* s1 = str1; const char* s2 = str2; const char* cp = str1; //如果传过来一个空集,一定是子集. if (*str2 == ' ') { return (char*)str1; } while (*cp) { //每次回到有效起始位置 s1 = cp; s2 = str2; //两个都不是末尾,并且相等的时候就都++ while (*s1 && *s2 && *s1 == *s2) { s1++; s2++; } //如果s2到底了,说明查完了 if (*s2 == ' ') { return (char*)cp; } cp++; } //如果都不符合,说明没有查到,返回空指针 return NULL; }创建指针s1、s2、cp,其中s1和s2用来比较是否字符相等,cp来记录从这一个字符开始的对比是否符合,因为其中可能出现部分符合的情况。
先if判断 *str2 是否为空,如果为空,直接返回str1的首元素地址。
s1每次都回到cp的位置,s2回到第二个字符串首元素位置。然后进入比较,如果两个都不为空并且相等的话,那么s1++,s2++。如果*s2 == ‘ ’ ,那么说明s2比到头了,说明是子集,所以返回cp指向的位置。否则,就是不相等的情况,那么cp++,从下一个元素开始比较。最后如果都找不到,说明不是子集,返回NULL。
1.6 strtok
格式:char * strtok ( char* str, const char* sep );
第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
strtok函数找到str中的下一个标记,并将其用 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
如果字符串中不存在更多的标记,则返回 NULL 指针。
光看定义比较难理解,通俗来说,这是一个查找函数,如果在第一个字符串找都第二个字符串中含有的字符,那么就会将其改成
‘ ’ ,并返回首元素地址。
第二使用的时候,第一个参数传
NULL就行了,函数会从上次找到元素的地址后面一个地方开始查找。如果没找到,返回空指针。
使用:
#include
#include
int main()
{
char arr1[] = "abc.def@ghijk";
char arr2[] = "@.";
char temp[30] = { 0 };
strcpy(temp, arr1);
char* p = NULL;
for (p = strtok(temp, arr2); p != NULL; p = strtok(NULL, arr2))
{
printf("%sn", p);
}
return 0;
}
结果:abc
def
hijk
这个函数由于会修改第一个参数,所以一般是在临时变量中进行。for循环进入的时候调用一次,传参拷贝过后的temp和arr2,用一个指针p来接收。判断条件为返回的指针 p!=NULL,最后每次执行完都会继续调用,只不过第一个参数改为NULL。
这样的做法比一次一次调用要效率高。
#include#include int main() { char arr1[] = "abc.def@ghijk"; char arr2[] = "@."; char temp[30] = { 0 }; strcpy(temp, arr1); char* p = NULL; for (p = strtok(temp, arr2); p != NULL; p = strtok(NULL, arr2)) { printf("%sn", p); } return 0; }
结果:abc
def
hijk
这个函数由于会修改第一个参数,所以一般是在临时变量中进行。for循环进入的时候调用一次,传参拷贝过后的temp和arr2,用一个指针p来接收。判断条件为返回的指针 p!=NULL,最后每次执行完都会继续调用,只不过第一个参数改为NULL。
这样的做法比一次一次调用要效率高。
1.7 strerror
格式: char *strerror( int errnum )
返回错误码所对应的信息
#include
需要手动打印错误信息
1.8 perror
格式:perror( const char *string )
相比于strerror,这个更为有效,能够自动打印错误信息。
使用:#includeint main() { FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { perror("打印错误信息"); } else { printf("打印成功n"); fclose(pf); pf = NULL; } return 0; }
2. 字符分类函数
使用需要包含头文件
用法:
小写转换大写
#include#include int main() { char ch = 'a'; if (isalpha(ch)) { printf("%cn", toupper(ch)); } return 0; } 打印结果:A
2.1 memcpy
格式:memcpy ( void * destination, const void * source, size_t num )
函数memcpy从source的位置开始向后赋值num个字节的数据到destination的内存位置。
这个函数在遇到 ‘ ’ 的时候不会停下来。
如果 source 和 destination 有任何的重叠,复制的结果都是未定义的。
size_t num 是总字节大小,如果是用其他类型的算,就要折算成字节大小。
使用:
实现:#include#include / int main() { int arr1[20] = { 1,2,3,4,5,6,7,8,9,10 }; int arr2[20] = { 0 }; memcpy(arr1, arr2, 10 * sizeof(int)); for (int i = 0; i < 10; i++) { printf("%d ", arr1[i]); } return 0; } 由于计算的是 int 类型的,所以大小要乘上整形的大小4。
结果:0 0 0 0 0 0 0 0 0 0
void* my_memcpy(void* dest, void* src, size_t count) { assert(dest); assert(src); void* ret = dest; while (count--) { *(char*)dest = *(char*)src; ++(char*)dest; ++(char*)src; } return ret; }先断言判断非空指针。由于dest和src都是无类型的指针,解引用需要先强制类型转换为char*类型的,因为这个函数要做到所有类型都是用,只能采取最小的计量值1个字节,所以就是char型的。循环中每次把一个字节的值赋值,然后++,这样就把每一个字节的内容都拷贝过去了。
2.2 memmove
格式:memmove( void *dest, const void *src, size_t count )
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。 如果源空间和目标空间出现重叠,就得使用memmove函数处理。 使用:实现:#includeint main() { int arr[20] = { 1,2,3,4,5,6,7,8,9,10 }; memmove(arr, arr+1, 4); for (int i = 0; i < 10; i++) { printf("%d ", arr[i]); } return 0; } 结果: 2 2 3 4 5 6 7 8 9 10
void* my_memmove(void* dest, void* src, size_t count) { assert(dest); assert(src); if (dest < src) { //从前向后拷贝 while (count--) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = (char*)src + 1; } } else { //从后向前拷贝 while (count--) { *((char*)dest + count) = *((char*)src + count); } } }这其中涉及到了目标空间在前面还是后面的问题: 如果在前面并且和源头空间有重叠,那么就有可能互相影响导致结果不正确。例如abcdefg,把bcd移动到abc的位置,如果从向前移动,那么c移动到b这里的时候b就改变了,本来要把b移到a就变成了c移动到a。
所以我们的思路应该是:如果目标空间在前面,那么从前往后移动。其他的都从后往前移动。(从前往后指的是从头部移还是先从尾部移)
今天的文章就到此结束啦!感谢各位的观看!
五一快乐!



