字符函数和字符串函数
0.前言
c语言中对字符和字符串的处理很频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串或者字符数组中。
字符串常量适用于那些对它不做更改的字符串函数。
1.函数介绍
1.1 strlen
size_t my_strlen(const char* str)//自己模拟实现strlen,这个函数只计算长度,不会修改字符串,所以可以使用const限制一下,使函数更加稳定。
{
int count = 0;
while (*str++)
{
count++;
}
return count;
}
int main()
{
char arr[] = "abcdefg";
printf("%d", my_strlen(arr));
return 0;
}
//另一种方法就是用两个指针指向
求字符串长度的,统计的是字符串中 \0 之前出现的字符个数。
注意函数的返回值 size_t 是无符号整型。
无符号数一定大于0.
1.2 strcpy
char* my_strcpy(char* p1,const char* p2)//自己模拟实现strlen
{
char* ret = p1;
while (*p1++ = *p2++)
{
;
}
return ret;
}
int main()
{
char arr[10] = "xxxxxxxxx";
char arr2[] = {'b', 'i', '\0', 't'};
printf("%s", my_strcpy(arr, arr2));
return 0;
}
参数是目标空间的地址和被复制字符串的地址。
把被复制字符串的数据拷贝到目标空间中
拷贝数据的时候,遇到 \0 才停止,所以被拷贝字符串必须含有 \0 。
目标空间必须足够大,确保能够放下字符串。
目标空间可变。
函数中如果有不需要变化的,可以在前面加上const增强函数的 的稳定性。
链式访问——一个函数的返回值作为另一个函数的参数。
回调函数——将一个函数的地址作为参数传入另一个函数。然后再另一个函数中被调用。
常量字符串是不能被修改的 : char* p = “abcdef”;//常量字符串abcdef
1.3 strcat
char* mt_strcat(char* p1,const char* p2)
{
char* ret = p1;
while (*p1++)//寻找目标函数的末尾的\0
{
;
}
while (*p1++ = *p2++)//开始从目标字符串结尾追加
{
;
}
return ret;
}
int main()
{
char arr[20] = "abcdefg";
char arr2[] = "word";
printf("%s\n", mt_strcat(arr, arr2));
return 0;
}
字符串追加函数
把源头的数据追加到目标后面去。
1.4 strcmp
int my_strcmp(const char* p1,const char* p2)
{
while (*p1 == *p2)
{
if (*p1 == '\0')
{
return 0;
}
p1++;
p2++;
}
return *p1 - *p2;
}
int main()
{
char arr1[] = "abcdefg";
char arr2[] = "abcd";
printf("%d", my_strcmp(arr1, arr2));
return 0;
}
对比的两个字符串都要有 \0 .
长度不受限制的字符串函数:
strcpy strcat strcmp
长度受限的字符串函数:
strncpy strncat strncmp
1.5 strncpy
char* my_strncpy(char* p1,const char* p2, int a)
{
char* ret = p1;
while (a)
{
a--;
*p1++ = *p2++;
if (*p2 == '\0')
{
while (a)
{
a--;
*p1++ = '\0';
}
return ret;
}
}
return ret;
}
int main()
{
char arr1[] = "abcdefg";
char arr2[] = "word";
printf("%s", my_strncpy(arr1, arr2, 4));
return 0;
}
1.6 strncat
在追加完之后,这个函数会主动在最后放上一个 \0.
模拟思路分析:
首先这个函数中有一个整型可以控制追加的字符个数,然后在追加完了,还会在末尾补上一个 \0。它的返回值则是目标字符串的首字母地址。
首先是通过接收的参数,利用循环找到目标字符串中\0的地址,然后再利用循环使源字符串追加到目标字符串上,同时这个循环的条件为控制追加字符个数的整型。
char* my_strncat(char* p1, char* p2, int a)
{
char* ret = p1;
while (*p1)
{
p1++;
}
while (a)
{
*p1++ = *p2++;
a--;
}
*p1 = '\0';
return ret;
}
int main()
{
char arr1[20] = "abcdefg\0xxxxxx";
char arr2[] = "word";
printf("%s", my_strncat(arr1, arr2, 3));
return 0;
}
1.7 strncmp
int my_strncmp(char* p1, char* p2, int a)
{
while (*p1 == *p2)
{
a--;
p1++;
p2++;
}
return *p1 - *p2;
}
int main()
{
char arr1[20] = "abcdefg";
char arr2[] = "abadg";
int ret = my_strncmp(arr1, arr2, 4);
if (ret > 0)
printf("arr1>arr2\n");
else if(ret<0)
printf("arr1<arr2\n");
else
printf("arr1=arr2\n");
printf("%d", ret);
return 0;
}
比较两个字符串的大小,首先是找到两个字符串的首字符的地址,然后进行比较,比较一次,控制次数的整形减一。
1.8 strstr
char* my_strstr(char* p1, char* p2)
{
char* a = p1;
char* b = p2;
//首先,开始上面的比较两个是否相同,相同一起比较下一个不相同,上面的加一
while (*p1 != '\0')
{
if (*p1 == *p2)
{
a = p1;
while (*p1++ == *p2++)
{
if (*p2 == '\0')
{
return a;
}
if (*p1 == '\0')
{
return NULL;
}
}
p1 = a + 1;
p2 = b;
}
else
{
p1++;
}
}
return NULL;
}
int main()
{
char arr1[] = { "aabcabcddfg" };
char arr2[] = { "abcddf" };
char* p = my_strstr(arr1, arr2);
if (p == NULL)
{
printf("不存在\n");
}
else
{
printf("%s\n", p);
}
return 0;
}
在一个字符串中找另一个字符串是否存在。
如果存在,返回字串所在的起始地址,不存在返回空指针。
1.9 strtok
char * strtok (char * str, constchar * sep );
- sep参数是个字符串,定义了用作分隔符的字符集合
- strtok函数会找到str中的下一个标记,并将其改成’ \0 ',返回一个指向这个标记的指针,strtok真的会改变这个字符串,所以strtok切分的字符串一般都是临时拷贝出来的可修改的字符串。
- 函数的第一个参数不为null,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
- strtok函数中的第一个函数为null,函数将在同一个字符串中被保存的位置开始,查找下一个标记
- 如果字符串中不存在更多标记,则返回NULL 指针。
int main()
{
char arr[] = "weisiyuan@hongxiaozi.com";//这里的@和.就是分隔符,就是上述sep的字符串中的字符
char arr2[200] = { 0 };
strcpy(arr2, arr);//将用到的字符串拷贝出来,因为strtok真的会更改字符串
const char* p = "@.";
char * str = NULL;
for(str = strtok(arr, p); str!= NULL; str = strtok(NULL, p) )
{// 起始, 判断, 改变
printf("%s\n", str);
}
return 0;
}
1.10 strerror 和 errno
char * strerror ( int errno)//把错误码转换成错误信息
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));
//记录错误码的变量 errno;
//#include <errno.h>
FILE* pf = fopen("test.txt", "r");
if(pf == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
// 读文件
fclose(pf);
pf = NULL;
return 0;
}
2.1 memcpy //内存拷贝
void my_memcpy( void * des, const void * source, size_t num);
void* my_memcpy(void * p1, const void* p2, size_t i)
{
void* ret = p1;
while (i--)//挨个字节递减,直到i变为0
{
*(char*)p1 = *(char*)p2;
((char*)p1)++;//先将无类型的指针转换成字符指针,再+1
((char*)p2)++;
}
return ret;
}
int main()
{
int arr[20] = { 0 };
int arr2[20] = { 1,2,3,4,0,6,7,8,9,10 };
int* a = (int *)my_memcpy(arr, arr2, 24);
int i = 0;
while (*a)
{
printf("%d", *a);
a++;
}
return 0;
}
2.2 memmove
void* memmove(void * destination , void * source, size_t num);
//重叠内存的拷贝,也就是源空间和目标空间出出现重叠,就得使用memmove函数处理。
void* my_memmove( void* str, void* dec, size_t num)//重叠内存的拷贝
{
void* aka = str;
if (str > dec)
{
while (num--)
{
*((char*)str+num) = *((char*)dec+num);
}
}
else
{
while (num--)
{
*(char*)str = *(char*)dec;
str = (char*)str + 1;
dec = (char*)dec + 1;
}
}
return aka;
}
int main()
{
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ,0};
int * p = (int*)my_memmove(arr,arr+1, 20);
int i = 0;
while (*p)
{
printf("%d ", *p);
p++;
}
return 0;
}
2.3 memset
void memset (void * dest , int c, size_t count)
以字节为单位,修改
c是你想修改的值,count代表你想修改多少个字节。
int main()
{
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9,0 };
memset(arr, 0, 20);
int* p = arr;
//20个字节,也就是5个整型,可以将前五个元素改成0,
return 0;
}
字符函数
int main()
{
char ch = 'a';
int ret = isdigit(ch);
pritnf("%d\n", ret);
return 0;
}