前言:
C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串 通常放在 常量字符串 中或者 字符数组 中。 字符串常量 适用于那些对它不做修改的字符串函数.
一、长度不受限制的字符串函数
1.1字符串求长——strlen函数
1.1.1strlen函数的介绍
size_t strlen ( const char * str );
这里我们可以看到strlen函数的返回值是size_t 🌕:1.字符串已经 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包 含 ‘\0’ )。 🌕:2.参数指向的字符串必须要以 ‘\0’ 结束。 🌕:3.注意函数的返回值为size_t,是无符号的( 易错 ) 例子:👇
#include <stdio.h>
#include <string.h>
int main()
{
if (strlen("abc") - strlen("abcdef") > 0)
{
printf("hehe\n");
}
else
{
printf("haha\n");
}
return 0;
}
这里输出的结果是hehe,因为返回的是无符号整数,这两哥字符串无论怎么减都是正数。
1.1.2strlen函数的模拟实现
这里直接计算个数即可,较为简单,不做赘述。
#include<stdio.h>
#include<assert.h>
size_t my_strlen(const char* str)
{
int count = 0;
assert(str != NULL);
while (*str)
{
count++;
str++;
}
return count;
}
int main()
{
char arr[] = "pwh";
int len = my_strlen(arr);
printf("%d", len);
return 0;
}
1.2字符串拷贝——strcpy函数
1.2.1strcpy函数的介绍
🌕:strcpy把含有’\0’结束符的字符串复制到另一个地址空间,返回值的类型为char*。
char* strcpy(char * destination, const char * source );
需要注意的是:👇 1.源字符串必须以 ‘\0’ 结束。 2.会将源字符串中的 ‘\0’ 拷贝到目标空间。 3.目标空间必须足够大,以确保能存放源字符串。 4.目标空间必须可变。
为什么返回值为char*
返回strDest的原始值使函数能够支持链式表达式,增加了函数的“附加值”。同样功能的函数,如果能合理地提高的可用性,也就就更加理想。
int iLength=strlen(strcpy(strA,strB));
char * strA=strcpy(new char[10],strB);
举例:👇
char a[10],b[]={"PWH"};
strcpy(a,b);
1.2.2strcpy函数的模拟实现
该代码与strlen函数类似,只需要注意代码的严谨,源地址的不可变性,拷贝至’\0’,数组空间够大即可。且一定要是字符数组!
#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{
assert(dest != NULL);
assert(src != NULL);
char* ret = dest;
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[] = "abcdefghi";
char arr2[] = "bit";
my_strcpy(arr1, arr2);
printf("%s", arr1);
return 0;
}
1.3字符串比较——strcmp函数
1.3.1strcmp函数的介绍
int strcmp(const char *s1,const char *s2);
🌕:1.第一个字符串大于第二个字符串,则返回大于0的数字 🌕:2.第一个字符串等于第二个字符串,则返回0 🌕:3.第一个字符串小于第二个字符串,则返回小于0的数字 🔓:注意: 比较的是字符串的ASCII码值! 从第一个字符开始比较,第一个相等就从第二个字符比较,第一个大于返回正数,小于返回负数;然后从第二个开始比较。
#include<stdio.h>
#include<string.h>
int main()
{
char* p1 = "abcdef";
char* p2 = "aqwer";
int ret = strcmp(p1, p2);
printf("%d\n", ret);
return 0;
}
1.3.2strcmp函数的模拟实现
strcmp函数的实现思路为定义两个数组,且都为不可变,使用while循环 从第一个字符开始比较,如果相等则返回0,如果不相等则继续比较下个字符,字符地址+1,如果大于,返回正数,小于返回负数。
#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;
}
str1++;
str2++;
}
if (*str1 > *str2)
{
return 1;
}
else
{
return -1;
}
}
int main()
{
char* p1 = "abcdef";
char* p2 = "abqwe";
int ret = my_strcmp(p1, p2);
printf("ret=%d\n", ret);
return 0;
}
1.4字符串连接——strcat函数
1.4.1strcat函数的介绍
char *strcat(char *dest, const char *src);
🌕:把src所指向的字符串(包括“\0”)复制到dest所指向的字符串后面(删除dest原来末尾的“\0”)。要保证dest足够长,以容纳被复制进来的*src。*src中原有的字符不变。返回指向dest的指针。 举例:👇
#include <stdio.h>
#include <string.h>
int main ()
{
char src[50]="source";
char dest[50]="destination";
strcat(dest, src);
printf("最终字符串:%s", dest);
return 0;
}
1.4.1strcat函数的模拟实现
🌕:定义两个数组,首先遍历到’\0’,然后和strcpy函数相似开始拷贝即可。
#include <stdio.h>
#include <assert.h>
char* my_strcat(char *dest, const char *src)
{
assert(dest && src);
char *tmp = dest;
while (*tmp != '\0')
{
tmp++;
}
while ((*tmp++ = *src++) != '\0');
return dest;
}
int main()
{
char arr1[10] = "pwh";
char arr2[] = "hxs";
char* arr3 = my_strcat(arr1, arr2);
printf("str1=%s\n", arr1);
printf("str2=%s\n", arr2);
printf("str3=%s\n", arr3);
return 0;
}
1.5字符串查找——strstr函数
1.5.1strstr函数的介绍
🌕:返回字符串中首次出现子串的地址 举例:
int main()
{
char* p1 = "abcdefghi";
char* p2 = "defq";
char* ret = strstr(p1, p2);
if (ret == NULL)
{
printf("子串不存在\n");
}
else
{
printf("%s\n", ret);
}
return 0;
}
1.5.1strstr函数的模拟实现
思路: 尽量不要让p1,p2走,这样无法记住它的位置 当相等时,cur作为一个大前提,先在cur的位置使用s1 s2进行查找,如果没找到,cur++,从下一个字符查找。 这里定义了cur s1 s2,这样可以互不干扰,然后s1重新等于cur,从此位置++。
char* my_strstr(const char* p1, const char* p2)
{
assert(p1 != NULL);
assert(p2 != NULL);
char* s1 = p1;
char* s2 = p2;
char* cur =(char*) p1;
if (*p2 == '\0')
{
return p1;
}
while (*cur)
{
s1 = cur;
s2 = (char*)p2;
while (*s1 && *s2 && (*s1 == *s2))
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return cur;
}
cur++;
}
return NULL;
}
int main()
{
char* p1 = "abbbcdef";
char* p2 = "bbc";
char* ret = my_strstr(p1, p2);
if (ret == NULL)
{
printf("子串不存在\n");
}
else
{
printf("%s\n", ret);
}
return 0;
}
二、长度受限制的字符串函数
2.1strncpy函数
char * strncpy ( char * destination, const char * source, size_t num );
2.1.1与strcpy的区别
🌕:拷贝num个字符从源字符串到目标空间。 🌕:如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。 🌕:strncpy越界之后,拷贝完毕会把数组后面的内容全部变为0。
2.1.2strncpy函数的模拟实现
只需要在strcpy基础上加一个n的限制条件即可。
#include<stdio.h>
#include<assert.h>
char*my_strncpy(char*dest, const char*src, size_t n)
{
assert(dest != NULL);
assert(src != NULL);
char*ret = dest;
while (n)
{
*dest = *src;
src++;
dest++;
n--;
}
return ret;
}
int main()
{
char arr1[20] = "hxs";
char arr2[20]= "pwh";
int n = 0;
printf("请输入需要拷贝的字符个数:\n");
scanf("%d", &n);
char*ret = my_strncpy(arr1,arr2,n);
printf("%s\n", ret);
return 0;
}
2.2strncat函数
2.2.1与strncat的区别
🌕:strncat越界之后,追加完毕后依旧会自动添加’\0’,但和strncpy区别在于,不会把数组后面的元素变为0
2.2.1strncat函数的模拟实现
先遍历到目标字符串\0处,加一个n的限制条件即可。
#include <stdio.h>
#include <assert.h>
char* my_strncat(const char* dest, const char* src,unsigned n)
{
assert(dest && src);
char* tmp = dest;
while (*tmp != '\0')
{
tmp++;
}
while (n)
{
*tmp = *src;
tmp++;
src++;
n--;
};
return dest;
}
int main()
{
char arr1[10] = "pwh";
char arr2[10] = "hxs";
int n = 0;
printf("请输入你要拷贝的字符个数");
scanf("%d", &n);
my_strcat(arr1, arr2,n);
printf("arr1=%s\n", arr1);
return 0;
}
2.3strncmp函数
2.3.1与strcmp的区别
🌕:比较的字符数可控。
2.3.2strncmp函数的模拟实现
没什么好说的,就是很简单!这里用for循环更加方便一些。
#include<stdio.h>
#include<assert.h>
int my_strncmp(const char* str1, const char* str2,unsigned n)
{
assert(str1 && str2);
unsigned i = 0;
for (i = 0; i < n - 1 && *str1 && *str2; i++)
{
if (*str1 != *str2)
{
break;
}
str1++;
str2++;
}
return (*str1 - *str2);
}
int main()
{
char* p1 = "abcdef";
char* p2 = "abqwe";
int n = 0;
printf("请输入要检验的字符数");
scanf("%d", &n);
int ret = my_strcmp(p1, p2,n);
printf("ret=%d\n", ret);
return 0;
}
三、内存函数
内存函数可以对任意类型的值操作,而字符串函数只能对字符串操作!
3.1memcpy函数
3.1.1memcpy函数的介绍
void * memcpy ( void * destination, const void * source, size_t num );
🌕:1.函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。 🌕:2.这个函数在遇到 ‘\0’ 的时候并不会停下来。 🌕:3.如果source和destination有任何的重叠,复制的结果都是未定义的。
3.1.2memcpy函数的模拟实现
将所传入的指针强制类型转换为char*类型,这样可以对于各种类型的数据进行操作,其余过程和strncpy函数比较相似。
struct S
{
char name[20];
int age;
};
void my_memcpy(void* dest, const void* src, size_t num)
{
assert(dest);
assert(src);
void* ret = dest;
while (num--)
{
*(char*)dest = *(char*)src;
++(char*)dest;
++(char*)src;
}
return dest;
}
int main()
{
struct S arr3[] = { {"张三",20},{"李四",30} };
struct S arr4[3] = { 0 };
my_memcpy(arr4, arr3, sizeof(arr3));
printf("%d", arr4->age);
return 0;
}
3.2memove函数
3.2.1memove函数的介绍
void *memmove( void* dest, const void* src, size_t count );
memmove用于拷贝字节,如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,但复制后源内容会被更改。但是当目标区域与源区域没有重叠则和memcpy函数功能相同。
3.2.2memove函数的模拟实现
实现思路较为复杂,需要考虑两种情况,所重复的部分是在拷贝内容之前还是之后? 那么可以分为向前拷贝或者向后拷贝两种解决方法!
void my_memove(void* dest, const void* src, size_t num)
{
assert(dest);
assert(src);
void* ret = dest;
if (dest<src)
{
while (num--)
{
*(char*)dest = *(char*)src;
++(char*)dest;
++(char*)src;
}
}
else
{
while (num > 3)
{
*((char*)dest + num - 4) = *((char*)src + num - 4);
num--;
}
}
return ret;
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memove(arr+2, arr, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
本章完!
|