目录
2) strchr - 在字符串中找一个字符 第一次出现 的位置
3)strrchr - 在字符串中找一个字符 最后一次出现 的位置
1)strerror - 返回错误码,所对应的错误信息(不打印)
2. perror - 打印报错码的信息(结合了 printf + strerror 的功能)
1)字符分类函数:判断是否为这类字符,是则返回一个非0的数字,否则返回0
2、memmove - 源空间和目标空间出现重叠时的memcpy
一、字符串查找
1) strstr - 在字符串中查找字符串的位置
char * strstr ( const char *str1, const char * str2);
在 字符串1 中,找 字符串2 第一次出现的位置 。
如果找到就返回该位置的指针。找不到就返回空指针。
1.1使用
int main()
{
char arr1[] = "abcdebcdf";
char arr2[] = "bcd";
char* p = strstr(arr1, arr2);
if (p == NULL)
{
printf("找不到\n");
}
else
{
printf("%s\n", p);//bcdebcdf
//返回的是,arr2字符串第一次出现的地址,所以打印字符串%s
//得到bcdebcdf
}
return 0;
}
1.2模拟实现strstr
char* my_strstr(const char* str1, const char* str2)
{
char* s1 = NULL;
char* s2 = NULL;
char* cp = str1;
while (*cp)
{
s1 = cp;
s2 = (char*)str2;
while (*s1 && *s2 && *s1 == *s2)
{
s1++;
s2++;
}
if (*s2=='\0')
{
return cp;
}
//*s1 != *s2 不相等,则cp++
cp++;
}
return NULL; //当遍历完arr1都没有找到arr2 的字符串,则返回空指针(找不到)
}
int main()
{
char arr1[] = "abcdebcdf";
char arr2[] = "bcd";
int* p = my_strstr(arr1, arr2);
if (p == NULL)
{
printf("找不到\n");
}
else
{
printf("%s\n", p);//bcdebcdf
//返回的是,arr2字符串第一次出现的地址,所以打印字符串%s
//得到bcdebcdf
}
return 0;
}
2) strchr - 在字符串中找一个字符 第一次出现 的位置
与strstr相似的函数
在字符串中找一个字符 第一次 出现的位置
int main()
{
char arr1[] = "abcdebcdf";
int* p = strchr(arr1, 'd'); //在字符串 abcdebcdf 中字符 d,第一次出现的位置
if (p == NULL)
{
printf("找不到\n");
}
else
{
printf("%s\n", p);//debcdf
}
return 0;
}
3)strrchr - 在字符串中找一个字符 最后一次出现 的位置
在字符串中找一个字符 最后一次 出现的位置
int main()
{
char arr1[] = "abcdebcdf";
int* p = strrchr(arr1, 'c'); //在字符串 abcdebcdf 中字符 d,最后一次 出现的位置
if (p == NULL)
{
printf("找不到\n");
}
else
{
printf("%s\n", p);//cdf
}
return 0;
}
2、strtok - 分隔字符串
* char* strtok(char* str, const char* sep);
- sep参数是个字符串,定义了用作分隔符的字符集合
- 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
- strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
- strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
- strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
- 如果字符串中不存在更多的标记,则返回 NULL 指针。
1)普通使用
分割akai@163.com(随便起的邮箱名,无实际意义)
int main()
{
char arr1[] = "akai@163.com";
char arr2[20] = { 0 };
strcpy(arr2, arr1);//因为strtok函数会改变原字符串,所以我们要拷贝一个,来进行分割操作
const char* sep = "@.";//创建指向分隔符的指针
//第一次字符串起始是arr2
char* str= strtok(arr2, sep);//strtok返回的是记录下来的指针位置,所以要用一个指针变量来接收
printf("%s\n", str);//akai
//第二次分割的字符串起始是NULL
str = strtok(NULL, sep);//因为第一次分割后,第一个分割符被替换成了NULL,且该位置被保存
printf("%s\n", str);//163
//之后的分割同理第二次
str = strtok(NULL, sep);
printf("%s\n", str);//com
return 0;
}
2)循环使用
如果分割的部分过多,那么一次一次打印就过于麻烦,于是有了这样的循环切割
分割 192.168.1.1 或者 akai@163.com
int main()
{
//char arr1[] = "192.168.1.1";
char arr1[] = "akai@163.com";
char arr2[20] = { 0 };
strcpy(arr2, arr1);
//const char* sep = ".";//分隔符
const char* sep = "@.";
char* str = NULL;
for (str = strtok(arr2, sep); str != NULL; str = strtok(NULL, sep))
{
printf("%s\n", str);
}
return 0;
}
二、错误信息报告
1)strerror - 返回错误码,所对应的错误信息(不打印)
char * strerror ( int errnum );
返回值为错误信息的指针
1.1错误代码所指内容
int main()
{
//每个错误码都对应着一个错误信息,但是需要通过指针来访问
//因为strerror的返回值是指针
printf("%s\n", strerror(0));//No error
printf("%s\n", strerror(1));//Operation not permitted
printf("%s\n", strerror(2));//No such file or directory
printf("%s\n", strerror(3));//No such process
printf("%s\n", strerror(4));//Interrupted function call
return 0;
}
1.2常用例子
#include <errno.h>
int main()
{
//打开文件
//打开文件的时候,如果文件的打开方式是"r"
//文件存在则打开成功,文件不存在打开失败
//打开文件失败的话,会返回NULL
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
//因为正在编辑的这个test.c文件的路径下,没有test.txt,所以打不开
printf("打开文件失败,原因是:%s\n", strerror(errno));//这样就能看到这个保存信息了
return 1;
}
//读写文件
//...
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
2. perror - 打印报错码的信息(结合了 printf + strerror 的功能)
与strerror 很相似的函数
但是这个perror更省事,省去了额外的printf打印
int main()
{
//打开文件
//打开文件的时候,如果文件的打开方式是"r"
//文件存在则打开成功,文件不存在打开失败
//打开文件失败的话,会返回NULL
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("打开文件失败");//这个更方便
//相当于 printf + strerror
//printf("打开文件失败,原因是:%s\n", strerror(errno));
return 1;
}
//读写文件
//...
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
三、字符操作
1)字符分类函数:判断是否为这类字符,是则返回一个非0的数字,否则返回0
iscntrl 任何控制字符
isspace 空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t'或者垂直制表符'\v'
isdigit 十进制数字 0~9 字符
isxdigit 十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F
islower 小写字母a~z
isupper 大写字母A~Z
isalpha 字母a~z或A~Z
isalnum 字母或者数字,a~z,A~Z,0~9
ispunct 标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph 任何图形字符
isprint 任何可打印字符,包括图形字符和空白字符
int main()
{
//
//printf("%d\n", iscntrl(6));
//isspace 空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t'或者垂直制表符'\v'
//char ch = '\f';
//printf("%d\n", isspace(ch));
//isdigit 十进制数字 0~9 字符
//char ch = '7';
//printf("%d\n", isdigit(ch));
//isxdigit 十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F
//char ch = 'f';
//printf("%d\n", isxdigit(ch));
//islower
//char ch = 'k';
//printf("%d\n", islower(ch));
//isupper
//char ch = 'L';
//printf("%d\n", isupper(ch));
//isalpha 字母a~z或A~Z (包含isupper、islower)
//char ch = 'c';
//printf("%d\n", isalpha(ch));
//ispunct 标点符号(punctuation) 任何不属于数字或者字母的图形字符
//char ch = '?';
//printf("%d\n", ispunct(ch));
只能判断英文的非数字或者字母的图形字符(例如?),中文的会报错(例如:?)
//isgraph 任何图形字符
//char ch = '1';// ~ ` A a 1 空格
//printf("%d\n", isgraph(ch));
//isprint 任何可打印字符,包括图形字符和空白字符
//char ch = 'a';// ~ ` A a 1 ()
//printf("%d\n", isprint(ch));
return 0;
}
2)字符转换:大小写字母转换
int tolower ( int c );
int toupper ( int c )toupper 将英文字符转为大写
tolower 将英文字符转为小写
int main()
{
char arr1[20] = "HellO BiT";
char arr2[20] = "NihaO BiT";
int i = 0;
while(arr1[i])
{
printf("%c",toupper(arr1[i]));
i++;
}
printf("\n");
i = 0;
while (arr1[i])
{
printf("%c", tolower(arr2[i]));
i++;
}
return 0;
}
四、内存操作函数 (任何数据类型都可以操作)
以下内存操作函数,是通过调用函数方式,调试观察现象主函数如下:
int main()
{
//test_memcpy();
//test_my_memcpy();
//test_memmove();
//test_my_memmove();
//test_memcmp();
test_memset();
return 0;
}
1、memcpy - 内存数据拷贝函数
函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
这个函数在遇到 '\0' 的时候并不会停下来。
如果source和destination有任何的重叠,复制的结果都是未定义的
1)使用
test_memcpy()//调试观察
{
//mempcy函数返回的是目标空间的起始地址
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[20] = { 0 };
memcpy(arr2, arr1, 20);//拷贝前五个整型,4*5=20字节
float arr3[] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
float arr4[8] = { 0 };
//把arr1中的前3个数据拷贝到arr2中
memcpy(arr4, arr3, 12);//3*4=12
}
2)模拟实现memcpy
#include<assert.h>
void* my_memcpy(void* dest, const void* src,size_t num)//拷贝时,源字符串是不能被改变的,加上const
{
assert(dest && src);//断言,是否为有效指针
void* ret = dest;//记录目的字符串的起始值,方便最后的返回
while (num--)
{
*(char*)dest = *(char*)src;//先对指针强制类型转换为char*,一个字节一个字节拷贝
dest = (char*)dest+1;//指针+1,也要强制类型转换
src = (char*)src+1;
}
return ret;
}
void test_my_memcpy() //调试查看
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[8] = { 0 };
//把arr1中的前5个数据拷贝到arr2中
my_memcpy(arr2, arr1, 20);
}
2、memmove - 源空间和目标空间出现重叠时的memcpy
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
如果源空间和目标空间出现重叠,就得使用memmove函数处理
C语言:memcpy拷贝不重叠的内存
重叠的就交给memmove
能力:memmove > memcpy
1)使用
test_memmove()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
// 想通过my_memcpy(arr1+2, arr1, 20);//无法实现
// 实现 1 2 1 2 3 4 5 8 9 10
//当操作同一组数据,且源数据和目的数据有交集时,可以通过memmove来实现
memmove(arr1 + 2, arr1, 20);
//my_memcpy(arr1+2, arr1, 20);
//本应该报错的,但是因为vs的优化,使得memcpy与memmove作用相同
// 但是不能保证其他编译器也可以这样正常运行,所以建议用memmove
//memcpy(arr1, arr1 + 2, 20);
//memcpy(arr1 + 2, arr1, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
}
2)模拟实现memmove
void* my_memmove(void* dest,const void* src,size_t num)
{
assert(dest && src);
void* ret = dest;
//当dest<src时,应该从前往后移动
if(dest<src)
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
//当dest>src时,应该从后往前移动
else
{
while (num--)//num=20
{
//num=19 - 此时正好指向最后一个数据的最后一个字节
//从这个位置开始,往前移动
*((char*)dest+num) = *((char*)src+num);
}
}
return ret;
}
test_my_memmove()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(arr1 + 2, arr1, 20);
//my_memmove(arr1, arr1+2, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
}
3、memcmp - 内存数据比较
int memcmp ( const void * ptr1,const void * ptr2,size_t num );
比较多少个字节的字符,
>,返回一个>0的数
相同,返回0
<,返回一个<0的数
test_memcmp()
{
int arr1[] = { 1,2,3,4 };//01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
int arr2[] = { 1,2,3,5 };//01 00 00 00 02 00 00 00 03 00 00 00 05 00 00 00
int ret = memcmp(arr1, arr2,12);//比较前3个数据,是相同的;返回0
printf("%d\n", ret);
ret = memcmp(arr1, arr2, 13);//比较前3个数据+1个字节,刚好小端存储的第13个字节为04 05
//4<5,返回一个小于 0 的数
printf("%d\n", ret);
}
4、memset - 内存数据设置
test_memset()
{
char arr[] = "hello_bit";
memset(arr,6, 5);//将前5个块,设置为6
//6 6 6 6 6 _ b i t
错误示范
//int arr[10] = { 0 };
01 01 01 01
00000001000000010000000100000001 - 16843009
//memset(arr, 1, sizeof(arr));//这种写法无法将数据的每个元素设置为1
//int i = 0;
//for (i = 0; i < 10; i++)
//{
// printf("%x ", arr[i]);
//}
}