目录
前言:C语言中对字符和字符串的处理是频繁的,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串中或字符数组中。字符串常量适用于那些对它不做修改的字符串函数.
求字符串长度
strlen
strlen
size_t strlen(const char*str);
字符串已经'\0'作为结束标志,strlen函数返回的是在字符串中'\0'前面出现的字符个数(不包含'\0').
参数指向的字符串必须要以'\0'结束 否则求出的是随机值
注意函数的返回值为size_t,是无符号的(易错)
学会strlen的模拟实现:
可以给模拟实现取个名字叫my_strlen,接着给一个数组 将数组名传入 传址调用
int main()
{
char arr[] = "abc";
//求到\0之前的字符个数 且必须有\0
//char arr[] = { 'a','b','c' };//这里没有\0 随机值
int len = my_strlen(arr);
printf("%d\n", len);
return 0;
}
实现my_strlen函数功能:
因为my_strlen不回改变数组内字符的内容,所以用const修饰,保护指针所指向的内容。
这里可以说一下:const char*p表示指针是变量 指向一个常量。常量指针->指向常量的指针
char const*p表示指针是常量,指向一个变量。指针常量->指针类型的常量
记忆方法:从左到右和谁连接谁不变.
同时,因为避免接受空指针,最好添加一个还能输assert
int my_strlen(const char*str)
{
int count = 0;//计数器
assert(*str != NULL);
while (*str != '\0')
{
count++;
str++;
}
return count;
}
完整代码:
#include<stdio.h>
#include<string.h>
#include<assert.h>
int my_strlen(const char*str)
{
int count = 0;
assert(*str != NULL);
while (*str != '\0')
{
count++;
str++;
}
return count;
}
int main()
{
char arr[] = "abc";
int len = my_strlen(arr);
printf("%d\n", len);
return 0;
}
注意:这里有一个坑!
int main()
{
if (strlen("abc") - strlen("abcdef") > 0)
{
printf(">\n");
}
else
{
printf("<=");
}
return 0;
}//结果是什么? > 因为strlen的返回值无符号数!坑!
用my_strlen可以算出<=
长度不受限制的字符串函数
strcpy
char*strcpy(char*destination,const char*source)
目标空间 原
Copies the C string pointed by source into the array pointed by destiantion,including the terminating null
character(and stopping at that point)
源字符串必须以‘\0’结束
会将源字符串中的‘\0’拷贝到目标空间
目标空间足够大,以确保能存放源字符串
目标空间必须可变
学会模拟实现
#include<stdio.h>
#include<string.h>
int main()
{
//char arr[20] = { 0 };//arr数组可修改
//char* str = "**********";//这个str是一个常量字符串 不可修改
char arr[20] = "###########";
//char arr[20] = "##";
//char* p = "hello world";
//arr="hello"//error arr是个地址 hello要放在空间中
strcpy(arr,"hello");//string copy
//char arr2[] = { 'a','b','c' };//没有\0
//保证目标空间要足够大
//char*p = "hello"传递过去的只是h的地址
printf("%s\n", arr);
//源字符串没有\0 会越界访问
return 0;
}
strcpy模拟实现:目的地的字符串是可以被修改的,而源头我们不希望它被修改用const,这样的好处是:如果不小心改了编译器会报错
#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{
assert(dest && src);//保证接收的不是空指针
char* tmp = dest;
while (*dest++ = *src++)
{
;
}
return tmp;
}
int main()
{
char arr1[10] = "abcdef";
char arr2[] = "123";
char *p= my_strcpy(arr1, arr2);
printf("%s", p);
return 0;
}
#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{
assert(dest && src);
char* tmp = dest;
while (*src != '\0')
{
*dest = *src;
dest++;
src++;
}
*dest = *src;
}
int main()
{
char arr1[10] = "abcdef";
char arr2[] = "123";
char* p = my_strcpy(arr1, arr2);
printf("%s", p);
return 0;
}
strcat
strcat字符串追加函数
char*strcat(char*destination,const char*source);
Appends a copy of the source string to the destination string.The terminating null character in destination is
overwritten by the first character of source,and a null-character is included at the end of the new
string formed by the destination of both in destination.
源字符串必须以‘\0’结束
目标空间必须足够的打,能够容得下源字符串的内容
目标空间必须可修改
字符串自己给自己追加,如何?
然而,对于自我追加的情况,即源字符串和目标字符串是同一个字符串时,strcat函数并不能正常工作。这是因为一个字符数组只有一个空间,当需要在一个空间进行自己的字符串追加时,一旦找到'\0'(字符串结束符)后,将下一个字符(例如'a')赋给'\0',那么此字符串就没有终止空字符了,导致程序行为未定义。因此,为了安全,通常不推荐使用strcat函数自我追加。
好的,我来尝试用更通俗的语言解释一下。
想象一下,你有一个空的玻璃杯,你想往里面倒水。当你倒进去一些水后,你会在杯子里看到水位上升。但是,如果你试图把已经在水中的水再倒回杯子里,那么水位就不会再上升了,因为杯子里的水和你要倒的水其实是同一种东西。
同样的道理,strcat函数就像是那个玻璃杯,而要追加的字符串就像是水。当你试图把一个字符串追加到另一个字符串的末尾时,就像把水倒进杯子一样,结果是可以预见的。但是,如果你试图把同一个字符串追加到它自己(也就是同一个"玻璃杯")的末尾,那么结果就变得不可预测了。因为你无法确定这个字符串在哪里结束,所以当程序试图找到一个确定的结束位置时,可能会找不到,从而导致错误。
int main()
{
char arr[] = "abcd";
strcat(arr, arr);//不能 \0被改变了 找不到结束的标志\0
printf("%s\n", arr);
return 0;
}
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[20] = "hello" ;//world
char arr2[] = "world";
strcat(arr1,arr2);//字符串追加(连接)
printf("%s\n", arr1);
return 0;
}
模拟实现:
char* my_strcat(char*dest,const char*src)
{
char* ret = dest;
//找到目标字符串中的\0
assert(dest && src);
while (*dest)
{
dest++;
}
//源数据追加过去(包含\0)
while (*dest++ = *src++)
{
;
}
return ret;//返回的目标空间的起始地址
}
int main()
{
char arr1[20] = "hello ";//world
char arr2[] = "world";
my_strcat(arr1, arr2);//字符串追加(连接)
printf("%s\n", arr1);
return 0;
}
注意strcat和strcpy的区别:strcat追加字符串 strcpy拷贝字符串;特别注意strcat从\0位置开始追加,会追加所有内容包含world后隐藏的\0
strcpy只能单纯的拷贝字符串,arr1数组中原有的内容会被覆盖 而且strcpy会从arr2拷贝所有的内容到arr1中包含自身的\0
strcmp
strcmp
int strcmp(const char*str1,const char*str2);
This function starts comparing the first character of each string.If they are equal to each other,it continues
with the following pairs untill the charaters differ or until a terminating null-character is reached.
标准规定:
第一个字符串大于第二个字符串则返回大于0的数字
第一个字符串等于第二个字符串,贼返回0
第一个字符串小于第二个字符串,则返回小于0的数字
那么如何判断两个字符串?
一个一个比过去 abcd..排序
#include<string.h>
#include<stdio.h>
int main()
{
char* p = "abcdef";
char* q = "abbb";
int ret=strcmp(p, q);
if (ret > 0)
{
printf("p>q");
}
else if (ret < 0)
{
printf("p<q");
}
else
{
printf("q=p");
}
return 0;
}
#include<Stdio.h>
#include<string.h>
#include<assert.h>
int my_strcmp(const char*s1,const char*s2)
{
assert(s1 && s2);
while (*s1 == *s2)
{
if (*s1 == '\0'||*s2=='\0')
{
return 0;
}
s1++;
s2++;
}
if (*s1 > *s2)
{
return 1;
}
else
{
return -1;
}
return *s1 - *s2;
}
int main()
{
char* p = "abcdef";
char* q = "abbb";
int ret=my_strcmp(p, q);
if (ret > 0)
{
printf("p>q");
}
else if (ret < 0)
{
printf("p<q");
}
else
{
printf("q=p");
}
return 0;
}
//strcpy - 复制
//strcat - 追加
//strcmp - 比较
//-------------长度不受限制
长度受限制的字符串函数介绍
strncpy
strncat
strncmp
字符串查找
strstr
int main()
{
char arr1[] = "abcdefabcdef";
char arr2[] = "bcd";
//在arr1中查找arr2数组
//找到返回b地址 否则返回空指针
char*ret=strstr(arr1,arr2);
if (ret == NULL)
{
printf("没找到");
}
else
{
printf("找到了:%d", ret);
}
return 0;
}
模拟实现:
#include<stdio.h>
#include<string.h>
char* my_strstr( const char* str1,const char* str2)
{
if (*str2 == '\0')
{
return (char*)str1;
}
// abbbcdef
//bbc
assert(str1 && str2);
char* s1 = NULL;
char* s2 = NULL;
char* cp = str1;
while (*cp)
{
s1 = cp;
s2 = str2;
while (*s1&&*s2&&*s1 == *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return (char*)cp;
}
cp++;
}
return NULL;
}
int main()
{
char arr1[] = "abcdefabcdef";
char arr2[] = "bcd";
//在arr1中查找arr2数组
//找到返回b地址 否则返回空指针
char* ret = my_strstr(arr1, arr2);
if (ret == NULL)
{
printf("没找到");
}
else
{
printf("找到了:%d", ret);
}
return 0;
}
// KMP算法 - 字符串中找字符串的算法
为什么要设置三个变量s1 ,s2分别遍历和比对 pc用来记录或者说返回 确保说能够找到
也就是s1和s2不相等的时候s2回去str2的位置,pc向前走然后s1在pc的基础上向后走就算p1==p2了p1和p2依然要向后走直到s2找到\0如果pc走到了\0都找不到那么就是找不到了
#include<stdio.h>
#include<assert.h>
const char* my_strstr(const char* str1, const char* str2)
{
assert(str1 && str2);
const char* cp = str1;
//如果*cp指向了\0就直接结束循环,\0就算ASCII中的0
while (*cp)
{
const char* s1 = cp;
const char* s2 = str2;
//s1 s2都是不等于\0如果等于就结束循环
while (*s1 == *s2 && *s1 & *s2)
{
//相等之后向后寻找
s1++;
s2++;
}
if (*s2 == '\0')
{
//找到了就返回刚刚pf保存的地址
return cp;
}
//这里说明没有相等 继续往后找
cp++;
}
return NULL;
//*cp指向\0都找不到就直接返回空
}
int main()
{
char arr1[] = "yu jia yang";
char arr2[] = "yang";
char* ret = my_strstr(arr1, arr2);
if (ret == NULL)
{
printf("没有找到");
}
else
{
printf("%s", ret);
}
return 0;
}
strtok
strtok 切割字符串函数
char *strtok(char*str,const char*sep)
sep参数是个字符串,定义了用作分隔符的字符集合
第一个参数指定了一个字符串,他包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
strtok函数找到str中的下一个标记,并将其用\0结尾,返回一个指向这个标记的指针。
(注:strtok函数会改变被操作符的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改
//strtok函数的第一个参数不为null 函数将找到str中的第一个标记 strtok函数将保存它在字符串中的位置
strtok函数的第一个参数不为null,函数将在同一个字符串中被保存的位置开始,查找下一个标记
如果字符串中不存在更多的标记,则返回null指针
#include<stdio.h>
#include<String.h>
int main()
{
char arr[] = "nihao@world.hello";
char* p = "@.";
char tmp[20] = { 0 };
strcpy(tmp, arr);
char* ret = NULL;
for (ret = strtok(tmp, p); ret != NULL; ret = strtok(NULL,p))
{
printf(" % s\n", ret);
}
return 0;
}
错误信息报告
strerror
strerror
char *strerror(int errnum);
返回错误码,所对应的错误信息
include<errno.h>必须包含的头文件
使用库函数的时候
调用库函数失败的时,都会设置错误码
// 全局的错误码
int errno;
#include<errno.h>
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));
printf("%s\n", strerror(5));*/
FILE* pf = fopen("test.txt", "r");
if (pf = NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
//...
fclose();
return 0;
}
//相关函数 perror - 更好!
//
//打印错误信息
//strerror 将错误码转化为错误信息 但是打不打印完全靠自己
//perror -
//字符函数
有兴趣自行了解啦!
还有很多的库函数呢
#include<stdio.h>
#include<ctype.h>
int main()
{
char ch = '#';
char ch = 'A';
//判断大写字母
//if (ch >= 'A' && ch <= 'Z')
// {
// }
// 这也是一种判断大小写的方法
//isdigit 如果是数字字符返回非0的值,如果不是数字字符,返回0
//int ret = isdigit(ch);
//int ret = islower(ch);//判断是不是小写字母
printf("%d\n", ret);//
return 0;
}
//字符转换
int main()
{
char arr[20] = { 0 };
scanf("%s", arr);
int i = 0;
while (arr[i] != '\0')
{
if (isupper(arr[i]))//如果里面有大写字母
{
arr[i]=tolower(arr[i]);//转化为小写
}
printf("%c ", arr[i]);
i++;
}
return 0;
}
//int tolower(int c)
//int toupper(int c)
//1.字符分类函数
//2.字符转换函数
内存操作函数
memcpy
内存操作函数
memcpy - 应该拷贝不重叠的内存
memmove函数可以处理内存重叠的情况
//内存函数
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[20] = { 0 };
//strcpy() 是拷贝字符串的 遇到\0才停止
// 01 00 00 00 02 00 00 00...
//char*strcpy(char*dest,char*src)
return 0;
}
注意:数据长度
如果复制的数据类型是char 那么数据长度就等于元素个数。注意(结构体等)
#include<stdio.h>
#include<string.h>
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[20] = { 0 };
//这个函数的参数类型是void*
memcpy(arr2, arr1, 20);//5个元素每个元素四个字节 也就是20个字节
return 0;
}
char a[10]="yujiayang";
void*p=memcpy(a+3,a,2);
这里的意思是是讲从a开始的两个字节的数据也就是yu 复制到a+3开始的内存中也就是i那个位置
运行后变成yujyuyang (自己思考一下其他类型)
//memcpy的模拟实现
#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;//为什么类型转化 因为void*是任意类型
dest=(char*)dest+1;
src = (char*)src + 1;//++优先级高
//单目操作符从右向左
}
return ret;//返回目标空间
}
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[20] = { 0 };
//这个函数的参数类型是void*
int ret =my_memcpy(arr2, arr1, 20);//5个元素每个元素四个字节 也就是20个字节
//int ret = my_memcpy(arr1+2,arr1,20);
// 1 2 1 2 3 4 5 8 9 10 //有重叠的部分会出问题
// 1 2 1 2 1 2
return 0;
}
memmove
//操作符贪心法
//c语语言生动解剖
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
// 1 2 1 2 3 4 5 8 9 10
memmove(arr1 + 2, arr1, 20);//可以处理内存不重叠的情况
return 0;
}
//整数 小端存储
//01 00 00 00 | 02 00 00 00| 03 00 00 00 |04 00 00 00| 05 00 00 00|06 00 00 00|07 00 00 00|...0a 00 00 00
//一个一个字节的拷贝过去
//memcpy-只要实现了不重叠拷贝就可以了
//而VS的实现既可以拷贝不重叠也可以拷贝重叠内存
//模拟实现
#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--)//20 19
{
*((char*)dest + num) = *((char*)src + num);
}
}
}
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
// 1 2 1 2 3 4 5 8 9 10
my_memmove(arr1 + 2, arr1, 20);
return 0;
}//如果要打印直接遍历数组打印即可
memset
//memcmp - 内存比较
#include<string.h>
int main()
{
float arr1[] = { 1.0,2.0,3.0,4.0 };
float arr2[] = { 1.0,3.0 };
int ret = memcmp(arr1, arr2, 8);
//memcpy - strcmp 相等返回0
printf("%d\n", ret);
return 0;
}
memcmp
//memset - 内存设置
int main()
{
int arr[10] = { 0 };
memset(arr,1,20);//以字节为单位设置内存的
//01 00 00 00
//01 01 01 01
return 0;
}