两种函数都需要包含头文件ctype.h

都是相同的使用方式:
int ret = islower('A');islower()是能够判断参数部分是否为小写字母,通过返回值来判断,如果是小写,返回非零,不是零,放回0
其他的函数同理,也是通过返回值确定
int tolower(int c); // 将参数传进去的大写字⺟转小写
int toupper(int c); // 将参数传进去的小写字⺟转大写
#include<ctype.h>
#include<stdio.h>
int main()
{
char arr[] = "i AM stUdent";
int i = 0;
while (arr[i] != '\0')
{
if (islower(arr[i]))
{
arr[i] = toupper(arr[i]);
}
i++;
}
printf("%s", arr);
return 0;
}以下函数都必须包含头文件<string.h>
从传递的地址开始,统计’\0’之前的个数 注意事项:返回值的类型是size_t
int main()
{
if ((strlen("abc") - strlen("abcdef"))>0)
{
printf("haha");
}
else
{
printf("hehe");
}
return 0;
}运行的结果是“haha”,结果虽然是-3,但是-3当成无符号数,前面的负号不再是符号位了而是有效位了,是一个非常大的正数 模拟实现
int main()
{
char arr[] = "abcdef";
int count = 0;
char* start = arr;
while (*start != '\0')
{
start++;
count++;
}
printf("%d", count);
return 0;
}方法2:指针相减
int main()
{
char arr[] = "abcdef";
char* start = arr;
while (*start != '\0')
{
start++;
}
printf("%d", start-arr);
return 0;
}方法3:递归
size_t my_strlen(const char * p)
{
if (*p == '\0')
{
return 0;
}
else
{
return 1 + my_strlen(p + 1);
}
}
int main()
{
char arr[] = "abcdefg";
size_t ret = my_strlen(arr);
printf("%d", ret);
return 0;
}易错点:arr1=arr2,数组名是首元素地址,是常量值,不能直接赋值,要用strcpy函数

把源头的数据拷贝到目的地空间里面去,目标空间足够大,能放得下从源头拷贝过来的数据,目标空间必须可修改(比如不能是常量字符串)

具体操作:

如何验证\0是否拿下来了?
通过调试

模仿实现:
void my_strcpy(char *p2,char *p1)
{
while (*p1 != '\0')
{
*p2 = *p1;
p1++;
p2++;
}
*p2 = *p1;
}
int main()
{
char arr1[] = "abcdef";
char arr2[] = "***********";
my_strcpy(arr2, arr1);
printf("%s", arr2);
return 0;
}char* my_strcpy(char* p2, const char* p1)
{
char* ret = p2;
while (*p2++ = *p1++)
{
;
}
return ret;
}
int main()
{
char arr1[] = "abcdef";
char arr2[] = "***********";
char* ret = my_strcpy(arr2, arr1);
printf("%s", ret);
return 0;
}注意事项:目标地址最好初始化(防止空间不够,因为未初始化的话,默认按照后面的元素来判断)
两字符串数组必须要有\0,并且目标空间可修改

模拟实现:
#include<string.h>
#include<stdio.h>
#include<assert.h>
char* my_strcat(char* p1, const char* p2)
{
assert(p1 && p2);
char* ret = p1;
while (*p1 != '\0')
{
p1++;
}
while (*p1++ = *p2++);
return ret;
}
int main()
{
char arr1[20] = "abcdef";
char arr2[] = "******";
char* ret = my_strcat(arr1, arr2);
printf("%s", ret);
return 0;
}
}可以实现自己与自己追加吗?
不能,因为找不到结束的\0,被赋值的永远走在前面,陷入死循环,越界访问,最后崩溃
一个一个字符比较,不能拿数组名和字符串(代表的也是首元素的地址)去比较

注意:在VS上返回的是0、1、-1这三种值,其他编译器只要是正数或负数就行了 模拟实现:
int my_strcmp(const char* p1, const char* p2)
{
assert(p1 && p2);
while (*p1 == *p2)
{
if (*p1 == '\0')
{
return 0;
}
p1++;
p2++;
}
return *p1 - *p2;
}
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abcaqw";
int ret =my_strcmp(arr1, arr2);
printf("%d", ret);
return 0;
}在一个字符串中查找另一个字符串是否出现过,如果出现过,返回的是另一个字符串在另一个字符串第一次出现过的位置,如果找不到(没出现),则返回空指针
int main()
{
char arr[] = "this is bit";
char* p = "Is";
char * ret = strstr(arr, p);
if (ret != NULL)
{
printf("%s", ret);
}
else
{
printf("找不到\n");
}
return 0;
}函数模拟



char* Fuc(const char* p1, const char* p2)
{
const char* s1 = NULL;
const char* s2 = NULL;
char* s3 = p1;
if (p2 == NULL)
{
return (char*)p1;
}
while (*s3 != '\0')
{
s1 = s3;
s2 = p2;
while (*s1!='\0'&&*s2!='\0'&& * s1 == *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return (char*)s3;
}
s3++;
}
return NULL;
}
int main()
{
char arr[] = "this is dert";
char* p = "is";
char *ret = Fuc(arr, p);
printf("%s", ret);
return 0;
}会拷贝\0进去吗?
该函数是长度受限制的字符串函数,让拷贝几个就拷贝几个,不会拷贝\0 验证:

参数个数太大怎么办?
strncpy()比较看重参数个数,即使数组里面的个数不够那么多数,也让\0去凑

会追加\0进去吗?
要有测试的思维,直接追加(最后都有\0,看不出来),所以让它提前追加(自己放个\0进去),因为是从第一个\0开始追加的,事实证明:会把\0追加进去,保证追加过去是一个字符串

追加的个数大于字符串本身会怎么样?会不会像strncpy一样补充\0?
不会,当把所有字符传过去(包括\0),就不会再管了
比strcmp多了一个参数,最大比较的个数,如果前几个数比较出来大小的话,就知道结果了(后面不用比较了),多了一个参数确定比较的大小


会把原始数据改掉,最好是再拷贝一份设置为函数的参数,第一次传参传的是你要切割的那个字符串,第二次传的是空指针(会从保存好的地址向后查找)
#include<stdio.h>
#include<string.h>
int main()
{
char arr[] = "3456@qq.com";
char* p = "@.";
char arr1[20] = { 0 };
strcpy(arr1, arr);
char* ret = NULL;
for (ret = strtok(arr, p); ret != NULL; ret = strtok(NULL, p))
{
printf("%s\n", ret);
}
return 0;
}
打印错误码
int main()
{
for (int i = 0; i <= 10; i++)
{
printf("%d = %s\n",i, strerror(i));
}
return 0;
}
应用场景(举例):
因为errno里面存放的是错误码,可以直接传给strerror这个函数
int main()
{
FILE* ret =fopen("test.txt", "r");
if (ret == NULL)
{
printf("%s", strerror(errno));
}
return 0;
}扩展:
perror
perror函数比strerror就更简单了
用法:
先打印传给函数的字符串,在打印冒号与空格,最后打印错误信息


当给空字符串时,就什么都不打印,只打印错误信息(给字符串就说明可以加入一些自定义的信息,如果不给,就只把错误信息打印出来)

总结:
当想直接把错误码对应的错误信息打印出来可以用perror,想获得错误码对应的错误信息就用strerror


#include<stdio.h>
#include<string.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7 };
int arr1[20] = { 0 };
memcpy(arr1, arr, 16);
for (int i = 0; i < 20; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}模拟实现:
#include<string.h>
#include<stdio.h>
#include<assert.h>
void* my_memcpy(void* p1, void* p2, int n)
{
void* ret = p1;
assert(p1 && p2);
while (n--)
{
*(char*)p1 = *(char*)p2;
p1 = (char*)p1+1;
p2 = (char*)p2+1;
}
return ret;
}
int main()
{
int arr[] = { 1,2,3,4,5,6 };
int arr1[20] = { 0 };
my_memcpy(arr1, arr, 16);
for (int i = 0; i < 20; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}注意:两数组内存不能够重叠,虽然memcpy函数可以实现内存相同的数组拷贝,但是我么们不指望它实现该功能
那谁适合于内存重叠的拷贝呢?

用法:
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
memmove(arr + 2, arr, 5 * sizeof(int));
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}模拟实现的分析思路: 如果再创造一个数组的话,不仅要把数据拷贝过来,还要把拷贝完的数组再传回去,太麻烦了,既然从前面拷贝不行就从后面拷贝,但是不是所有情况都适合从后往前拷

具体过程:

void* my_memmove(void* p1, void* p2, size_t num)
{
if (p2 > p1)
{
while (num--)
{
*(char*)p1 = *(char*)p2;
p1 = (char*)p1 + 1;
p2 = (char*)p2 + 1;
}
}
else
{
while (num--)
{
*((char*)p1 + num) = *((char*)p2 + num);
}
}
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(arr, arr+3, 5 * sizeof(int));
for (int i = 0; i<10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
基本用法:
int main()
{
char arr[] = "abcdef";
memset(arr, 'x', 3);
printf("%s", arr);
return 0;
}易错点:它是一个一个字节来设置的,他会把每一个字节都填充给想要的值,而不是说把每一个元素给改了(不是以元素为单位设置的)


注意:该函数是一个一个字节比较的 比如:比到第17个字节时就停止比较了(提前得到结果就确定大小了),与strcpy比较
