字符串函数的详解
函数介绍
一:求字符串长度
1:strlen
(1):strlen的用法
size_t strlen(const char* str);
- 字符串以
''
为结束标志,strlen函数返回的是在字符串中 ‘’ 前面出现的字符个数(不包含’’ )。 - 参数指向的字符串必须要以 ‘’ 结束。
- 注意函数的返回值为size_t,是无符号的整数( 易错 )
#include<stdio.h>
#include<string.h>
int main()
{
const char* str1 = "abcdef";
const char* str2 = "bbb";
if (strlen(str2) - strlen(str1) > 0)
{
printf("str2>str1n");
}
else
{
printf("str1>str2n");
}
return 0;
}
//看起来打印结果是str1>str2,但实际上是str2>str1
//因为strlen是无符号size_t整型,|3-6|=3>0
(2):用函数的方法来实现strlen
#include<stdio.h>
#include<string.h>
#include<assert.h>
size_t my_strlen(const char* str)
{
assert(str);//防止空指针
const char* start = str;
const char* end = str;
while (*end != '')
{
end++;
}
return end - start;
}
//指针-指针方式
size_t my_strlen(const char* s)
{
char* p = s;
while (*p != '')
p++;
return p - s;
}
//计数器方式
int my_strlen(const char* str)
{
assert(str);//断言
int count = 0;
while (*str)
{
count++;
str++;
}
return count;
}
int main()
{
char arr[] = "abcdef";
int len = my_strlen(arr);
printf("%dn", len);
return 0;
}
二:长度不受限制的字符串函数
1:strcpy
(1):strcpy的用法
char* strcpy(char * destination, const char * source );
- 语法原理:
- 把source指向地址的空间的数据,拷贝到destination所指向的空间里面去.
- 特点:
- 源字符串必须以 ‘’ 结束。
- 会将源字符串中的 ‘’ 拷贝到目标空间。
- 目标空间必须足够大,以确保能存放源字符串。
- 目标空间必须可变。
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[10] = "xxxxxxxxx";
char arr2[] = { 'b', 'i', 't', '' };
strcpy(arr1, arr2);
printf("%sn", arr1);//bit
return 0;
}
//结果:bit
//注:因为arr1数组的容量比arr2大,因此如果strcpy(arr2,arr1)的话
//会造成越界访问 导致代码出现错误
(2):用函数的方法来实现strcpy
#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{
assert(dest && src);
char* ret = dest;
while ((*dest++ = *src++))
{
;
}
return ret;
}
int main()
{
char arr1[10] = "abcdef";
char arr2[] = "bit";
my_strcpy(arr1, arr2);
printf("%s", arr1);
return 0;
}
//结果:bit
2:strcat
(1):strcat的用法
char* strcat(char* destination, const char* source);
-
语法原理:
- 把source指向地址空间的数据,拼接到destination所指向的空间末尾处,即把第1个字符串(source)拼接到第1个字符串末尾.
-
特点:
- 源字符串必须以 ‘’ 结束。
- 目标空间必须有足够的大,能容纳下源字符串的内容。
- 目标空间必须可修改。
- 字符串自己给自己追加,已经被覆盖掉了,这个程序会崩溃
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[20] = "你好";
char arr2[] = "hello world";
strcat(arr1, arr2);
printf("%sn", arr1);
return 0;
}
//结果:你好hello world
(2):用函数的方法来实现strcat
#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strcat(char* dest, const char* src)
{
assert(dest);
assert(src);
//1.先找目标空间中的
char* cur = dest;
while (*cur != '')
{
cur++;
}
//2.拷贝源头数据到之后的空间
while (*cur++ = *src++)
{
;
}
return dest;//返回目标空间起始地址
}
int main()
{
char arr1[20] = "hello ";//[]里如果不加数会导致数组越界访问
char arr2[] = "world";
printf("%sn", my_strcat(arr1, arr2));
return 0;
}
3:strcmp
(1):strcmp的用法
int strcmp(const char* str1, const char* str2);
- 语法原理:
- 这个函数会从两个字符串的第一个字符开始
对应比较
,哪一个字符的ASCII码大,哪一个 字符串大,如果两个字符一样,就继续比较下一个字符。
- 这个函数会从两个字符串的第一个字符开始
- 特点
- 第一个字符串大于第二个字符串,则返回
大于0
的数字 - 第一个字符串等于第二个字符串,则返回
0
- 第一个字符串小于第二个字符串,则返回
小于0
的数字
- 第一个字符串大于第二个字符串,则返回
#include<stdio.h>
#include<string.h>
int main()
{
//char arr1[] = "abcdef";
//char arr2[] = "abq";//返回了一个小于0的数字(-1)
char arr1[] = "abcd";
char arr2[] = "abc";//返回了一个大于0的数字(1)
//char arr1[] = "abc";
//char arr2[] = "abc";//返回0
// //char arr1[] = { 'a', 'b', 'c' };//err
// //char arr2[] = { 'a', 'b', 'c' };//err
int ret = strcmp(arr1, arr2);
if (ret < 0)
printf("arr1<arr2n");
else if (ret > 0)
printf("arr1>arr2n");
else
printf("arr1==arr2n");
printf("%dn", ret);
return 0;
}
(2):用函数的方法来实现strcmp
#include<stdio.h>
#include<string.h>
#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 == *str2)
{
if (*str1 == '')
{
return 0;
}
str1++;
str2++;
}
//方法一:
/*if (*str1 > *str2)
{
return 1;
}
else
return -1;*/
//方法二:
return *str1 - *str2;
}
int main()
{
char arr1[] = "abcd";
char arr2[] = "abc";
int ret = my_strcmp(arr1, arr2);
printf("%dn", ret);
return 0;
}
三:长度受限制的字符串函数介绍
1:strncpy
(1):strncpy的使用
char* strncpy(char* destination, const char* source, size_t num);
- 语法原理:
- 拷贝num个字符从源字符串到目标空间。
如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加’’,直到num个。
- 拷贝num个字符从源字符串到目标空间。
- 特点:
- 返回的指针指向被改变的字符串的首字符地址。
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[10] = "abcedf";
char arr2[10] = "xxxxxxxx";
strncpy(arr2, arr1, 8);
//8位看上去多了,但实际多了的位数后面自动添加'',直到补齐8位为止,
//剩余还有2个空间是本身自带的''
printf("%sn", arr2);
return 0;
}
//结果:abcedf('''''''')
(2):用函数的办法来实现strncpy
#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strncpy(char* dest, const char* src, size_t num)
{
assert(dest && src);
char* ret = dest;
while (num-- && *src != '')
{
*dest++ = *src++;
}
return ret;
}
int main()
{
char arr1[] = "abcdef";
char arr2[] = "xxxxxxxx";
int ret = my_strncpy(arr2, arr1, 5);
printf("%sn", arr2);
return 0;
}
2:strncat
(1):strncat的使用
char* strncat(char* destination, const char* source, size_t num);
- 语法原理
- 可以对字符串加长限定数目的字符
- 特点
- 返回的指针指向被改变的字符串的首字符地址
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[20] = "abcdefqqqq";
char arr2[] = "xyz";
strncat(arr1, arr2, 2);
printf("%sn", arr1);
}
//结果:abcdefxy('')(自己添加的'')
(2):用函数的办法来实现strncat
#include<stdio.h>
#include<assert.h>
char* my_strncat(char* dest, const char* src, size_t num)
{
assert(dest && src);
char* ret = dest;
while (*dest != '')
{
dest++;
}
while (num-- && *src != '')
{
*dest++ = *src++;
}
return ret;
}
int main()
{
char arr1[20] = "abc";
char arr2[] = "xyz";
printf("%sn", my_strncat(arr1, arr2, 2));
return 0;
}
3:strncmp
(1):strncmp的使用
int strncmp(const char* str1, const char* str2, size_t num)
- 语法原理:
- 这个函数会从两个字符串的第一个字符开始比较,哪一个字符的ASCII码大,哪一个字符串大,如果两个字符一样,就继续比较下一个字符。
- 特点:
- 这个函数可以比较限定数目的字符,由num来决定几个字符比较
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[12] = "abcdef";
char arr2[20] = "abcfg";
int ret = strncmp(arr1, arr2, 3);
printf("%dn", ret);
if (ret > 0)
{
printf("arr1>arr2");
}
else if (ret == 0)
{
printf("arr1=arr2");
}
else
{
printf("arr1<arr2");
}
return 0;
}
//结果:0 arr1=arr2
(2):用函数的办法来实现strncmp
#include<stdio.h>
#include<assert.h>
int my_strncmp(const char* str1, const char* str2, size_t num)
{
assert(str1 && str2);
while (num-- && *str1 == *str2 && *str1 != '')
{
if (num > 1)
{
str1++;
str2++;
}
}
return *str1 - *str2;
}
int main()
{
char arr1[12] = "abcdef";
char arr2[20] = "abcfg";
int ret = my_strncmp(arr1, arr2, 3);
if (ret > 0)
{
printf("arr1>arr2");
}
else if (ret == 0)
{
printf("arr1=arr2");
}
else
{
printf("arr1<arr2");
}
return 0;
}
四:字符串查找
1:strstr
(1):strstr的使用
char* strstr(const char* str1, const char* str2);
- 语法原理:
- 查找一个字符串中是否存在另一个子字符串
例如:abcdef中查找bcd就是存在的,打印结果从’b’开始一直到’’为止。而查找acd就是不存在的。
- 查找一个字符串中是否存在另一个子字符串
- 特点:
- 返回的指针为查找到的位置的地址,(也就是第一次子串出现的位置),如果不存在,则返回NULL.
比如说上面的例子中我们找到了bcd那么返回的指针就指abcdef中的那个字符’b’
- 返回的指针为查找到的位置的地址,(也就是第一次子串出现的位置),如果不存在,则返回NULL.
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "abcdef";
char arr2[] = "bcd";
char* p = strstr(arr1, arr2);
if (p == NULL)
printf("不存在n");
else
printf("%sn", p);
}
//结果:bcdef('')
(2):用函数的办法来实现strstr
#include<stdio.h>
#include<assert.h>
char* my_strstr(const char* str1, const char* str2)
{
assert(str1 && str2);
char* s1 = (char*)str1;
char* s2 = (char*)str2;
char* cp = (char*)str1;//cp保存首字符的地址
while (*cp)
{
s1 = cp;
while (*s1 != '' && *s2 != '' && *s1 == *s2)
{
s1++;
s2++;
}
if (*s2 == '')
{
return cp;
}
s2 = (char*)str2;
cp++;//cp++可以得到原起始位置的下一个位置
}
return NULL;
}
int main()
{
char arr1[] = "abcdef";
char arr2[] = "bcd";
char* p = my_strstr(arr1, arr2);
if (p == NULL)
printf("不存在n");
else
printf("%sn", p);
}
2:strtok
(1):strtok的使用
char* strtok(char* str, const char* sep);
- 语法原理
- 第一个数组储存
待拆分字符串
- 第二个数组储存
所有的分割符号
- 函数会在待拆分字符串中从左到右找到一个分隔符号并将其赋值为
- 返回的指针指向分割的那一部分字符串的开头,同时函数会记住原来分隔符的位置,我们第一个参数传
NULL
即可继续分割(NULL的时候才会用上记忆功能) - 例如:
我们的数组一设置为:10535715@qq.com
数组二设置为:.@
第一次分割:10535715qq.com
返回的地址指向1,内部存储的指针指向
再次传递NULL,第二次分割:10535715qqcom
返回的地址指向q(第一个q
),内部存储的指针指向下一个
以此类推,直到字符串末尾
- 第一个数组储存
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "10535715@qq.com";
const char* p = "@.";
//方法一:方便理解
//首元素地址非空
//char* str = strtok(arr1, p);
//printf("%sn", str);//10535715
//第一个开始算起,strtok函数的第一个参数为NULL,直到遇到.将其变成
//str = strtok(NULL, p);
//printf("%sn", str);//qq
//第二个开始算起,第一个参数还是NULL
//str = strtok(NULL, p);//com
//printf("%sn", str);
//方法二:
char* str = NULL;
for (str = strtok(arr1, p); str != NULL; str = strtok(NULL, p))
{
printf("%sn", str);
}
return 0;
}
五:错误信息报告
1:strerror
(1):strerror的使用
char * strerror ( int errnum );
- 语法原理:
- 把错误码转换成错误信息
在C程序中存在一个全局变量errno,要想使用这个变量需要引用头文件errno.h
当程序出现问题的时候这个变量就会存储一个对应这个错误的数字
但是只有这个数字我们不知道这个错误是什么
而这个函数可以根据这个errno的值找到对应的错误,返回错误内容的字符串
- 把错误码转换成错误信息
#include<stdio.h>
#include<string.h>
#include<errno.h>
int main()
{
printf("%sn", strerror(0));//0,1,2,3,4分别为错误码(c语言中库函数报错的时候的错误码)
printf("%sn", strerror(1));
printf("%sn", strerror(2));
printf("%sn", strerror(3));
printf("%sn", strerror(4));
return 0;
}
//结果:
//No error
//Operation not permitted
//No such file or directory
//No such process
//Interrupted function call
//如果错误信息是字符串的话,返回的是每行首字符的地址
#include<stdio.h>
#include<string.h>
#include<errno.h>
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("");//打印的依然是errno变量中错误码对应的错误信息
//perror=printf+strerror
//printf("%sn", strerror(errno));
return 1;
}
//读文件
fclose(pf);
pf = NULL;
return 0;
}
//No such file or directory
六:字符分类与字符转换函数
1:字符分类函数
可以判断字符是否为所需要的那一种类型的字符
下面是这部分的一些函数:(头文件为ctype.h)
- iscntrl 任何控制字符
- isspace 空白字符:空格‘ ’,换页‘f’,换行’n’,回车‘r’,制表符’t’或者垂直制表符’v’
- isdigit 十进制数字 0~9(如果 c 是一个数字,则该函数返回非零值,否则返回 0)
- isxdigit 十六进制数字,包括所有十进制数字,小写字母a-f,大写字母A~F
- islower 小写字母a~z
- isupper 大写字母A~Z
- isalpha 字母a-z或A~Z
- isalnum 字母或者数字,a-z, A-Z, 0~9
- ispunct 标点符号,任何不属于数字或者字母的图形字符(可打印)
- isgraph 任何图形字符
- isprint 任何可打印字符,包括图形字符和空白字符
2:字符转换函数
- 可以判断字符是否可以转化为所需要的那一种类型的字符,是则进行相应转换,不是则直接打印。
- 对于字符串也可以对每一个字符进行相应操作。
int tolower (int c); 大写字母转成小写字母(ch+32),不是大写字母的字符直接返回原字符。
int toupper (int c);小写字母转成大写字母(ch-32),不是小写字母的字符直接返回原字符。
七:内存操作函数
1:memcpy
(1):memcpy的使用
void * memcpy ( void * destination, const void * source, size_t num );
-
语法原理:
- 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
-
特点:
- 这个函数在遇到 ‘’ 的时候并不会停下来。
- 如果source和destination有任何的重叠,复制的结果都是未定义的。所以memcpy
不能重叠拷贝
#include<stdio.h>
#include<string.h>
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
memcpy(arr2, arr1, 8);//把arr1中的前8个字节的数拷贝到arr2里面去
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}
//结果:1 2 0 0 0 0 0 0 0 0
memcpy与strncpy很类似,但memcpy没规定参数
(2):用函数的办法来实现memcpy
#include<stdio.h>
#include <assert.h>
#include<string.h>
void* my_memcpy(void* dest, void* src, size_t num)
{
void* ret = dest;//定义起始位置ret
assert(dest);
assert(src);
while(num--)
{
*(char*)dest = *(char*)src;//void*指针无法解引用
dest = (char*)dest + 1;//有些编译器不能dest++和src++
src = (char*)src + 1;
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
my_memcpy(arr2, arr1, 20);//20为数组arr1中5个int元素的大小
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr2[i]);//1 2 3 4 5 0 0 0 0 0
}
return 0;
}
2:memmove
(1):memmove的使用
void * memmove ( void * destination, const void * source, size_t num );
- 语法原理:
- 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
- 如果源空间和目标空间出现重叠,就得使用memmove函数处理。
#include<stdio.h>
#include<string.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
memmove(arr + 2, arr, 20);//在arr数组的首元素+2的位置处拷贝arr数组中前5个元素
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
//结果:1 2 1 2 3 4 5 8 9 10
(2):用函数的办法来实现memmove
#include<stdio.h>
#include<string.h>
#include<assert.h>
void* my_memmove(void* dest, const void* src, size_t num)
{
char* s1 = (char*)dest;
char* s2 = (char*)src;
if (s1 <= s2)
{
while (num--)//dest小于src时,从前向后拷贝
{
*s1 = *s2;
s1++;
s2++;
}
}
else
{
while (num--)
{
*(s1 + num) = *(s2 + num);//dest大于src时,从后向前拷贝
}
}
return dest;
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(arr + 2, arr, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);//1 2 1 2 3 4 5 8 9 10
}
return 0;
}
3:memset
(1):memset的使用
void * memset ( void * ptr, int value, size_t num );
- 语法原理:
- 将ptr所指向的内存块的首个num字节(起始地址开始的字节数)设置成一个特定的值(也可解释为unsigned char)
- value值需要被设置,该值需要int传递,但函数利用该值是unsigned char类型来填充内存块
#include<stdio.h>
#include<string.h>
int main()
{
int arr[] = { 1,2,3,4,5 };
memset(arr, 0, 8);//用0代替数组arr中的前2个元素(8个字节)
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", arr[i]);//0 0 3 4 5
}
return 0;
}
(2):用函数的办法来实现memset
#include<stdio.h>
#include<assert.h>
void* my_memset(void* ptr, int value, size_t num)
{
void* ret = ptr;
assert(ptr);
while (num--)
{
*(char*)ptr = value;
ptr = (char*)ptr + 1;
}
return ret;
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memset(arr, 0, 4);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);//0 2 3 4 5 6 7 8 9 10
}
return 0;
}
4:memcmp
(1):memcmp的使用
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
- 语法原理:
- 比较从ptr1和ptr2指针开始的num个字节
- 特点:
- 如果ptr1里面字节的数据大于ptr2,那就返回>0
- 如果ptr1里面字节的数据等于ptr2,那就返回 = 0
- 如果ptr1里面字节的数据小于ptr2,那就返回<0
#include<stdio.h>
#include<string.h>
int main()
{
int arr1[] = { 1,2,3,4 };//小端存储:01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
int arr2[] = { 1,2,3,0 };// 01 00 00 00 02 00 00 00 03 00 00 00 00 00 00 00
int ret = memcmp(arr1, arr2, 12);//按照内存在第十二个字节数进行比较->00比00->返回0
printf("%dn", ret);
return 0;
}#include<stdio.h>
#include<assert.h>
int my_memcmp(const void* str1, const void* str2, size_t count)
{
assert(str1);
assert(str2);
if (!count)
{
return 0;
}
while (--count && *(char*)str1 == *(char*)str2)
{
str1 = (char*)str1 + 1;
str2 = (char*)str2 + 1;
}
return ((unsigned char*)str1 - (unsigned char*)str2);
}
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 0 };
int ret = my_memcmp(arr2, arr1, 12);//返回了一个大于0的数
printf("%dn", ret);
return 0;
}
memcmp和strncmp很类似,但memcmp没规定参数。
(2):用函数的办法来实现memcmp
#include<stdio.h>
#include<assert.h>
int my_memcmp(const void* str1, const void* str2, size_t count)
{
assert(str1);
assert(str2);
if (!count)
{
return 0;
}
while (--count && *(char*)str1 == *(char*)str2)
{
str1 = (char*)str1 + 1;
str2 = (char*)str2 + 1;
}
return ((unsigned char*)str1 - (unsigned char*)str2);
}
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 0 };
int ret = my_memcmp(arr2, arr1, 12);//返回了一个大于0的数
printf("%dn", ret);
return 0;
}