文章目录
一、strlen的使用和模拟实现
1函数基本信息
size_t strlen(const char* str);
做用是:返回字符串中’\0’之前的字符个数,不包括’\0’
2易错情况
int main()
{
char arr1[] = "abcdef";
char arr2[] = "bbc";
if (strlen(arr2) - strlen(arr1) > 0)//解决方法:(int)strlen(arr2) - (int)strlen(arr1)
{ //3 //6
printf("arr2>arr1");//这里只能是这个结果,因为两个size_t类型的数据相减,得到的也是size_t类型的数据,所以结果永远>0
}
else
{
printf("arr2<=arr1");
}
return 0;
}
具体原因:
结果得到-3
原码:10000000 00000000 00000000 00000011
反码:11111111 11111111 11111111 11111100
补码:11111111 11111111 11111111 11111101 - 内存中存的是这个
因为是size_t类型数据,所以就没有符号位的概念了,所以结果是一个正数,而正数的原码=反码=补码
所以11111111 11111111 11111111 11111101会得到得到一个非常大的正数
3模拟实现 - 递归法
size_t my_strlen( const char* str)
{
if (*str == '\0')
{
return 0;
}
else
{
return 1 + my_strlen(str + 1);
}
}
int main()
{
char arr[] = "abcdef";
size_t len = my_strlen(arr);
printf("%zd\n", len);
return 0;
}
二、strcpy的使用和模拟实现
1.函数基本信息
char* strcpy (char* 目标位置,const char* 源头位置)
作用:将源头数据拷贝到目标位置
注:源字符串里面必须有’\0’
注:'\0’也会被拷贝
注:目标空间必须足够大,且目标空间必须可修改
2.易错情况
int main()
{
char arr1[] = "abcdef";
char arr2[20] ="xxxxxxxxxxxxxx";
char* p = "xxxxxxxxxxx";
//arr2 = arr1;//是错误的,因为数组名是地址,是常量值
strcpy(arr2, arr1);
strcpy(p, arr1);//这里在拷贝的时候会出现报错,因为p指向的是常量字符串,而常量字符串不可修改
printf("%s\n", arr2);
printf("%s\n", p);
return 0;
}
3.strcpy的模拟实现
char* my_strcpy(char* dest, const char* src)
{
assert(dest != NULL);
assert(src != NULL);
char* ret = dest;
while (*dest++ = *src++)//不仅完成了拷贝,而且完成拷贝'\0'之后停止
{
};
return ret;//返回的是目标空间的起始地址
}
int main()
{
char arr1[] = "abcdef";
char arr2[20] = "xxxxxxxxx";
//char* p = my_strcpy(arr2,arr1);
//printf("%s\n", p);
printf("%s\n", my_strcpy(arr2, arr1));//返回值的作用 - 链式访问效果
return 0;
}
三、strcat的使用和模拟实现
1.函数基本信息
strcat字符追加 - 在目标数据后面加源头数据
char* strcat(char * 目标地址,const char 源头地址)
返回的是目标空间的起始地址
注:
1.目标字符串里面必须有’\0’,且目标数组有足够的空间去追加,且目标空间必须可修改
2.源字符串必须有’\0’
2.strcat的模拟实现
char* my_strcat(char* dest, const char* src)
{
assert(dest && src);//NULL - 0
char* ret = dest;
//1.找到dest指向的字符串中的'\0'
while (*dest)//当dest指向'\0'时停止
{
dest++;
}
//2.数据的拷贝
while (*dest++ = *src++)
{
;//空语句 - while循环要做的事情做完了,但又得有下面的语句,这时候可以用空语句 - 即就写一个;
}
return ret;//返回的是目标空间的起始地址
}
int main()
{
char arr1[20] = "hello";//想在后面加上world
char arr2[20] = "world";
//my_strcat(arr1, arr2);
//printf("%s\n", arr1);
printf("%s\n", my_strcat(arr1, arr2););
return 0;
}
3.问题思考
是否可以在arr1后面追加arr1?
int main()
{
char arr1[20] = "abc";
printf("%s\n", my_strcat(arr1, arr1));
return 0;
}
答:my_strcat不行
原因:当dest指向arr1中的‘\0’时,src指向arr1中的’a’
所以会把arr1中的’\0’换成’a’ - 此时arr1 = abca’\0’‘\0’…
然后接着往后换 - 此时arr1 = abcab’\0’…
… - =abcabc…
… - =abcabca…
src要一直找到’\0’才停止,但是dest在src后面,所以会把后面的’\0’都换掉
导致src找不到’\0’而一直往下换下去
最终死循环把程序弄崩溃
总结:当目标的’\0’被换掉时,源头的’\0’也被换掉了
<string.h>库里面的strcat可以做到strcat(arr1,arr1),因为经过了特殊处理
四、strcmp的使用和模拟实现
1.函数基本信息
作用:字符串比较 - 两个字符串对应位置上的字符进行比较 - 比较的是ASCII码值
int strcmp(const char* str1,const char* str2);
str1>str2 返回>0的数
str1=str2 返回=0的数
str1<str2 返回<0的数
int main()
{
//int r = strcmp("abcdef", "abq");//r=-1
//int r = strcmp("abq", "abcdef");//r=1
//int r = strcmp("abcdef", "abcdef");//r=0
//printf("%d\n", r);
if ("abq" > "abcdef")//这个表示用两个字符串的地址比较 - 即首元素a的地址
{ //a的地址 //a的地址
}
return 0;
}
2.strcmp模拟实现
法一
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 == *str2)//如果两个字符串完全相等,那么最后都会变成'\0'
{
if (*str1 == '\0')
{
return 0;
}
str1++;
str2++;
}
if (*str1 > *str2)
{
return 1;
}
else
{
return -1;
}
}
法二
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 == *str2)//如果两个字符串完全相等,那么最后都会变成'\0'
{
if (*str1 == '\0')
{
return 0;
}
str1++;
str2++;
}
return *str1 - *str2;
}
int main()
{
if (my_strcmp("abcdef", "abq") > 0)
{
printf("abcdef > abq");
}
else if (my_strcmp("abcdef", "abq") < 0)
{
printf("abcdef < abq");
}
else
{
printf("abcdef = abq");
}
return 0;
}
五、strncpy函数的使用和模拟实现
1.函数基本信息
char* strncpy(char* 目标, const char* 源头, size_t 数量);
2个参数:
strcpy,strcat,strcmp
长度不受限制的字符串函数
3个参数:
strncpy,strncat,strncmp
长度受限制的字符串函数
相对安全
int main()
{
char arr1[] = "abcdef";
char arr2[20] = "xxxxxxxxxx";//只拷贝abc到arr2
//strncpy(arr2, arr1, 3);
strncpy(arr2, arr1, 8);//当arr1没有这么多的时候,会拿'\0'来补,直到补足数量
printf("%s\n", arr2);
return 0;
}
2.strncpy模拟实现
char* my_strcpy(char* str1, const char* str2,int num)
{
int i = 0;
while (1)
{
if (*(str2 + i) == '\0')
{
*(str1 + i) = *(str2 + i);
return str1;
}
else
{
*(str1 + i) = *(str2 + i);
}
i++;
if (i == 5)
{
return str1;
}
}
}
int main()
{
char arr1[] = "abcdef";
char arr2[20] = "xxxxxxxxx";
printf("%s\n", my_strcpy(arr2, arr1,5));
return 0;
}
六、strncat函数的使用和模拟实现
1.函数基本信息
char* strncat(char* 目标,const char* 源头,size_t 数量)
即指定追加多少个字符
int main()
{
char arr1[] = "abcdef";
char arr2[20] = "qqqq";
strncat(arr2, arr1, 4);//即追加abcd - 虽然追加4个,但是会在后面补一个'\0',相当于追加了5个
printf("%s\n", arr2);
return 0;
}
2.strncat模拟实现
char* my_strncat(char* str1, const char* str2,int num)
{
int i = 0;
while (*(str1 + i) != '\0')
{
i++;
}
char* ps = str1 + i;
int m = 0;
for (m = 0;; m++)
{
if (m == num)
{
*(str1 + i + m) = '\0';
return str1;
}
if (*(str2 + m) == '\0')
{
*(str1 + i + m) = *(str2 + m);
return str1;
}
else
{
*(str1 + i + m) = *(str2 + m);
}
}
}
int main()
{
char arr1[20] = "hello";
char arr2[20] = "world";
printf("%s\n", my_strncat(arr1, arr2,3));
return 0;
}
七、strncmp函数的使用
1.函数基本信息
int strncmp(const char* str1,const char* str2,size_t 数量)
即比较几个字符
int main()
{
char arr1[] = "abcd";
char arr2[20] = "abq";
//int r = strncmp(arr1, arr2, 3);//-1
int r = strncmp(arr1, arr2, 2);//0
printf("%d\n", r);
return 0;
}
八、strstr的使用和模拟实现
1.函数基本信息
strstr作用:在一个字符串中,找另外一个字符串 - 即找一个子字符串是否存在
const char* strstr(const char* str1,const char* str2);
即在str1中str2,找到了就返回子字符串的地址(子字符串的起始地址);没找到就返回NULL(空指针)
//注:返回的是第一次出现的地址
int main()
{
char arr1[] = "abcdefabcdef";
char arr2[] = "cde";
//char arr2[] = "cdq";
const char* ret = strstr(arr1, arr2);
if (ret == NULL)
{
printf("没找到");
}
else
{
printf("%s\n", ret);
}
return 0;
}
2.模拟实现strstr
常规写法 - 暴力查找
const char* my_strstr(const char* str1, const char* str2)
{
const char* s1 = NULL;
const char* s2 = NULL;
const char* cur = str1;
//特殊情况的处理1
assert(str1 && str2);
//特殊情况的处理2
if (*str2 == '\0')
{
return str1;
}
while (*cur)//cur指向'\0'时停止
{
s1 = cur;
s2 = str2;
while(*s1 != '\0' && *s2!= '\0' && * s1 == *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')//当*s2=='\0'的时候,说明已经完全找到str2了
{
return cur;
}
cur++;
}
//出了while,说明把str1找遍了,也没有找到str2
return NULL;
}
int main()
{
char arr1[] = "abcdefabcdef";
char arr2[] = "cde";
//char arr2[] = "cdq";
const char* ret = my_strstr(arr1, arr2);
if (ret == NULL)
{
printf("找不到");
}
else
{
printf("%s\n", ret);
}
return 0;
}
关于字符串查找有一种算法:KMP算法
特点1:高效
特点2:代码比较难,难写
九、strtok函数的使用
1.函数基本信息
作用:可以把字符串里面由分割符隔开的一段段提取出来
例如:想从"abcdef@hijklmn.opq"里面提取出abcdef,hijklmn,opq这三个部分 - 注:这里面的@和.为分割符
char* strtok(char* str,const char* sep)
2.使用方法
1.sep指向一个字符串,定义了用作分割符的字符集合 - 如上面例子中的sep指向"@."
2.str指向一个字符串,这个字符串包含0个或多个sep字符串中包含的分割符,被割下来的部分称为标记 - 即有几段就有几个标记
注1:当str指向NULL时,有0个标记;当字符串中没有sep中的分割符时,有1个标记
当strtok找到一个标记的时候,会在后面加’\0’,并返回指向这个标记的指针
如"abcdef@hijklmn.opq"找到第一个标记"zbcedf"时,会把’@‘变成’\0’即"abcdef\0hijklmn.opq",然后返回’a’的地址
找到第二个标记"hijklmn"时会把’.‘变成’\0’即"abcdef\0hijklmn\0opq"
注2:strtok会改变str字符串,所以str字符串一般是临时拷贝的字符串且可修改
3.当str指向的第一个参数不是NULL时,会找到str中的第一个标记,且strtok会保存被改为’\0’的’@'的位置,并返回’a’的地址
4.当str指向的第一个参数为NULL时,从这个被保存的位置往后找下一个标记,且strtok会保存被改为’\0’的’.'的位置,并返回’h’的地址
5.如果字符串不存在更多的标记,即找完了,返回NULL
int main()
{
char arr[] = "abcdef@hijklmn.opq";
char tmp[30] = { 0 };
const char* sep = "@.";
strcpy(tmp, arr);//tmp成了arr的临时拷贝
//切割tmp的数据就可以了
//演示 - 这个办法没问题,但是不实用
//char* p = strtok(tmp, sep);
//printf("%s\n", p);//第一个标记"abcdef"
//
//p = strtok(NULL, sep);
//printf("%s\n", p);//第二个标记"hijklmn"
//
//p = strtok(NULL, sep);
//printf("%s\n", p);//第三个标记"opq"
//
//p = strtok(NULL, sep);
//printf("%s\n", p);//想找第四个标记,但是已经找完了,所以返回NULL
//正常写法
char* p = NULL;
// 初始化部分 判断部分 调整部分
for (p = strtok(tmp, sep); p != NULL;p = strtok(NULL,sep))
{
printf("%s\n",p);
}
return 0;
}
十、strerror函数的使用
1.函数基本信息
作用:可以把参数部分(错误码)对应的错误信息的字符串地址返回来
1.在C语言的实现中,库函数的使用,调用如果失败的话,会记录错误码,错误码是一个整数,每一个错误码对应一个信息
2.这些错误码会被记录在一个C语言中的全局变量errno中 - errno的使用需要头文件<errno.h>
3.在不同系统和C语言的标准库的实现都规定了一些错误码,一般放在<errno.h>这个头文件中
4.当程序启动的时候,erron是0 - 这个错误码的意思是“没有错误”
注:由于只有一个errno,所以要及时检测,否则容易被覆盖
char* strerror(int errnum) - 注:errnum为错误码
直接翻译错误码对应信息
int main()
{
int i = 0;
for (i = 0; i < 100; i++)
{
char* p = strerror(i);
printf("%d:%s\n",i, p);
}
return 0;
}
2.函数的使用
正常使用
int main()
{
//C语言中打开文件的代码 - fopen
//这个意思是用"r"(读)的方式,打开"text.txt"文件
//以"r"读的形式去打开,若文件不存在,则打开失败
FILE* pf = fopen("test.txt", "r");//打开成功会返回一个FILE类型的指针,打开失败会返回NULL
if (pf == NULL)
{
//printf("%s\n", strerror(errno));//No such file or directory - 没有这个文件或者文件夹
perror("喵爷");//这个perror也可以打印错误信息,perror相当于printf+strerror的组合
return 1; //注:perror需要头文件<stdio.h>
}
return 0;
}
perror - 需要头文件<stdio.h>
专门打印错误信息,且可以在()里面自定义打印信息,会打印在错误信息前面
void perror(const char* str)