1 strlen—求字符串长度
size_t strlen(const char * str);
1、 字符串以'\0'作为结束标志,strlen函数返回的是在字符串中'\0'前面出现的字符个数(不包含'\0')
2、 参数指向的字符串必须要以'\0'结束
3、 注意函数的返回值size_t,是无符号的
#include <stdio.h>
int main()
{
const char*str1 = "abcdef";
const char*str2 = "bbb";
if(strlen(str2)-strlen(str1)>0)
{
printf("str2>str1\n");
}
else
{
printf("srt1>str2\n");
}
return 0;
}
why?明明str2的长度是3,str1的长度是6,strlen(str1)>strlen(str2)
strlen的模拟实现(3种方法)
方法1:
size_t my_strlen(const char* str)
{
assert(str);
int len = 0;
while (*str++)
{
len++;
}
return len;
}
方法2:指针-指针
size_t my_strlen(char* str)
{
assert(str);
char* p = str;
while (*p)
{
p++;
}
return p-str;
}
方法3:递归
size_t my_strlen(const char* str)
{
assert(str);
if (*str == '\0')
return 0;
else
{
return my_strlen(str + 1) + 1;
}
}
2 长度不受限制的字符串函数—strcpy
char * strcpy(char * destination, const char * source) ;
●strcpy 函数用于将 source 指针指向的 C 字符串(包括终止的空字符‘\0’)复制到 destination 指针指向的数组中,分配给 destination 的内存应足够大,以复制源字符串(包括‘\0’)。函数返回指针 destination
●源字符串必须以‘\0’结束
●会将源字符串中的‘\0’拷贝到目标空间
●目标空间必须足够大,能容纳源字符串的内容
●目标空间必须可变
●函数的返回指针destination是为了实现链式访问
printf("%s\n", strcpy(str2, str1));
strcpy模拟实现
char* my_strcpy(char* dest, const char* src)
{
char* ret = dest;
assert(dest && src);
while (*dest++ = *src++)
;
return ret;
}
3 长度不受限制的字符串函数—strcat
char * strcat(char * destination, const char * source);
●strcat 函数用于将 source 指针指向的字符串追加到 destination 指针指向的字符串后面。在 destination 中,终止的空字符'\0'被 source 的第一个字符覆盖,新字符串的结尾包括一个空字符。函数返回指针 destination 。
●源字符串必须以‘\0’结束
●目标空间也要有结束标志
●目标空间必须足够大,能容纳源字符串的内容
●目标空间必须可变
?字符串自己给自己追加,可以吗?
不可以!str1作为目标空间,追加字符串会将末尾的结束标志'\0'覆盖,源字符串永远找不到'\0',会一直追加,直到目标空间溢出,导致运行错误。
strcat函数模拟实现
char* my_strcat(char* dest, const char* src)
{
char* ret = dest;
assert(dest && src);
while (*dest)
{
dest++;
}
while (*dest++ = *src++)
;
return ret;
}
3 长度不受限制的字符串函数—strcmp
int strcmp(const char * str1, const char * str2);
●strcmp函数从每个字符串的第一个字符开始比较。如果它们相等,它就继续比较后面的字符,直到出现不同的字符或者遇到终止的空字符为止。
●标准规定:
第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字
strcmp函数模拟实现
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 == *str2 && *str1 != '\0' )
{
str1++;
str2++;
}
return *str1 - *str2;
}
4 长度受限制的字符串函数—strncpy
char * strncpy ( char * destination, const char * source, size_t num );
●将source指向的源字符串的前num个字符复制到destination指向的目标字符串中。如果在复制num个字符之前,源字符串的结尾(以空字符为标志)被找到,那么目标字符串就用零填充,直到写入总共num个字符为止。
●拷贝num个字符从源字符串到目标空间
●如果源字符串的长度小于num,则拷贝完源字符串后,在目标空间后面追加‘\0’,知道拷贝数达到num个
应用实例1 源字符串长度大于num
int main()
{
char str1[20] = "abcdef";
char str2[20] = "cbb";
printf("%s\n", strncpy(str1, str2, 3));
return 0;
}
应用实例2 源字符串长度小于num
int main()
{
char str1[10] = "abcdef";
char str2[10] = "cbb";
printf("%s\n", strncpy(str1, str2, 5));
return 0;
}
strncpy函数模拟实现
char* my_strncpy(char* dest, const char* src, size_t num)
{
char* ret = dest;
assert(dest && src);
while (num && (*dest = *src))
{
dest++;
src++;
num--;
}
if (num == 0)
return ret;
else
{
while (num)
{
*dest = '\0';
dest++;
num--;
}
return ret;
}
}
5 长度受限制的字符串函数—strncat
●将source指向的字符串的前num个字符追加到destination指向的字符串后面,并在末尾添加一个空字符'\0'。
●如果source指向的字符串的长度小于num,那么也仅追加到source指向的字符串的最后一个空字符'\0'就停止。
应用实例1 源字符串长度大于num
int main()
{
char str1[10] = "abcd\0efff";
char str2[10] = "cbb";
printf("%s\n", strncat(str1, str2, 2));
return 0;
}
应用实例2 源字符串长度小于num
int main()
{
char str1[10] = "abcd\0efff";
char str2[10] = "cbb";
printf("%s\n", strncat(str1, str2, 5));
return 0;
}
strncat函数模拟实现
char* my_strncat(char* dest, const char* src, size_t num)
{
char* ret = dest;
assert(dest && src);
while (*dest)
{
dest++;
}
while (num && (*dest = *src))
{
dest++;
src++;
num--;
}
if (num == 0)
{
*dest = '\0';
}
return ret;
}
6 长度受限制的字符串函数—strncmp
int strncmp ( const char * str1, const char * str2, size_t num );
●比较到出现不一样的字符串或者其中一个字符串结束或者num个字符全部比较完
#include <stdio.h>
#include <string.h>
int main ()
{
char str[][5] = { "R2D2" , "C3PO" , "R2A6" };
int n;
puts ("Looking for R2 astromech droids...");
for (n=0 ; n<3 ; n++)
if (strncmp (str[n],"R2xx",2) == 0)//前两个字符相等就可以
{
printf ("found %s\n",str[n]);
}
return 0;
}
strncmp函数模拟实现
int my_strncmp(const char* str1, const char* str2, size_t num)
{
while (num&&(*str1==*str2)&&(*str1))
{
str1++;
str2++;
num--;
}
if (num == 0)
{
return 0;
}
return *str1 - *str2;
}
7 字符串查找函数—strstr
char * strstr ( const char * str1 const char * str2);
●返回一个指针,指向 str1 中 str2 第一次出现的位置,如果 str2 不是 str1 的一部分,则返回一个空指针
应用实例
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] ="This is a simple string";
char * pch;
pch = strstr (str,"simple");//返回的是simple中s的地址
strncpy (pch,"sample",6);//从simple中s的地址开始,向后复制6个字符,将simple改为sample
puts (str);//This is a sample string
return 0;
}
strstr函数模拟实现
#include <stdio.h>
#include <string.h>
char* my_strstr(const char* str1, const char* str2)
{
if (*str2 == '\0')
return str1;
while (*str1)
{
int len1 = strlen(str1);
int len2 = strlen(str2);
if (len1 < len2)//如果str1剩余的字符个数小于str2,str2一定不存在于str1中,跳出循环
break;
if (*str1 == *str2)
{
char* cp = str1;
char* s1 = cp;
char* s2 = str2;
while ((*s1 == *s2) && *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return cp;
}
}
str1++;
}
return NULL;
}
8 字符串查找函数—strtok
char * strtok ( char * str, const char * sep );
●sep参数是个字符串,定义了用作分隔符的字符集合
●第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
●在第一次调用时,该函数需要一个字符串作为 str 的参数,其第一个字符用作扫描标记的起始位置。在后续的调用中,该函数需要一个空指针,并使用上一个标记结束后的位置作为新的扫描起始位置。
●为了确定一个标记的开始和结束,strtok函数首先从起始位置扫描,找到不包含在sep字符串中的第一个字符(这就是标记的开始)。然后从这个标记的开始处扫描,找到包含在sep字符串中的第一个字符,并将其用 \0 替换,这就是标记的结束。如果遇到终止空字符,扫描也会停止。最终返回一个指向标记开始的指针
●(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
●strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数返回扫描起始位置的地址,并且保存这个标记后面的位置
● strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。 如果字符串中不存在更多的标记,则返回 NULL 指针。
应用实例 1
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "- This, a sample string.";
char* pch;
printf("Splitting string \"%s\" into tokens:\n", str);
pch = strtok(str, " ,.-");//被查找字符串中包含' (空格)'和','和'.'和'-',四个字符
//首先strtok从str第一个字符开始扫描,找到第一个不是上述字符的字符
作为标记的开始,那就跳过了str第1个和第2个字符'-'和' (空格)',
将T作为标记的开始
//找到','后将其变为'\0',返回T的地址,赋值给pch,并且保存
' (空格)'(这个空格是','后面那个空格)的地址
while (pch != NULL)
{
printf("%s\n", pch);//打印从T的地址开始打印,直到'\0',这个'\0'其实是我们找到的','变成
的'\0'
pch = strtok(NULL, " ,.-");//strtok函数又' (空格)'开始扫描,找到第一个不是上述4种字符
的字符作为标记的开始,那就跳过了' (空格)',将a作为标记的
开始
//找到' (空格)'后将其变为'\0',返回a的地址,赋值给pch,并且
保存sample的s的地址
}
return 0;
}
应用实例 2
int main()
{
char arr[] = "xxdwf@tji.ecu%cn&ee";
char sep[] = "@.%&";
char* str = NULL;
char p[30];
strcpy(p,arr);//strtok函数会改变字符串,因此将arr字符串拷贝,使用p进行查找
for (str = strtok(p, sep); str != NULL;str = strtok(NULL,sep))
{
printf("%s\n", str);
}
return 0;
}
9 信息错误报告函数—strerror
char * strerror (int errnum);
●库函数在执行的时候发生错误会将一个错误码存放在errno这个变量中,errno是C语言提供的一个全局变量。
●strerror函数能够将错误码对应错误信息字符串的首字符的地址返回。
int main()
{
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d:%s\n", i, strerror(i));
}
return 0;
}
应用实例
#include <stdio.h>
#include <string.h>
#include <errno.h>//使用errno必须包含的头文件
int main()
{
//C语言中可以操作文件
//操作文件的步骤
//1、打开文件
//2、读\写
//3、关闭文件
FILE* pf = fopen("unexist.ent", "r");//fopen是打开文件函数,"unexist.ent"不写路径表示当前路径下,"r"-read
if (pf == NULL)//如果文件打开失败,则返回空指针
{
printf("%s\n", strerror(errno));//打印打开文件失败的错误信息
return 1;
}
//读\写
//...
//关闭文件
fclose(pf);
return 0;
}
10 字符函数—字符分类函数
●使用时要包含头文件<ctype.h>
函数 | 如果他的参数符合下列条件就返回真 |
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 | 任何可打印字符,包括图形字符和空白字符 |
11 字符函数—字符转换函数
●使用时要包含头文件<ctype.h>
int tolower ( int c );//转换成小写
int toupper ( int c );//转换成小写
#include<stdio.h>
#include<ctype.h>
int main()
{
char str[] = "She is my Good Friend";
int i = 0;
while (str[i])
{
if (isupper(str[i]))//判断是否为大写字母
str[i] = tolower(str[i]);//转换为小写字母
i++;
}
printf("%s\n", str);
return 0;
}
12 内存函数—memcpy
void * memcpy ( void * destination, const void * source, size_t num)
●函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置
●这个函数在遇到'\0'时不会停下来
●如果source和destination有任何的重叠,复制的结果都是未知的。
应用实例 1:函数在遇到'\0'时不会停下来,直到复制num个字节
int main()
{
char str1[10] = "abcdefg";
char str2[10] = "qwe\0r";
memcpy(str1, str2, 5);
printf("%s\n",str1);
return 0;
}
应用实例2:source 和destination有重叠
int main()
{
int a[10] = {1,2,3,4,5,6,7,8,9,10};
memcpy(a + 2, a, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", a[i]);//1 2 1 2 3 4 5 8 9 10
}
return 0;
}
memcpy是用来处理不重叠内存的拷贝,VS编译器优化了他的功能,使得结果是正确的,但最好还是用处理重叠拷贝的函数memmove函数来进行重叠空间的拷贝最好!
应用实例 3:
int main()
{
int a[6] = { 1,2,3,4,5,6};
int a2[6] = { 0 };
memcpy(a2, a, 21);
int i = 0;
for (i = 0; i < 6; i++)
{
printf("%d ", a2[i]);
}
return 0;
}
memcpy函数模拟实现
void* my_memcpy(void* dest, void* src, size_t num)
{
void* ret = (char*)dest;
assert(dest && src);
while (num--)
{
*((char*)dest)++ = *((char*)src)++;
}
return ret;
}
13 内存函数—memmove
void * memmove ( void * destination, const void * source, size_t num)
●和memcpy的差别就是,函数memmove处理的源内存块和目标内存块可以重叠
●如果源空间和目标空间出现重叠,就得使用memmove函数处理
应用实例:
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "memmove can be very useful......";
memmove(str + 20, str + 15, 11);
puts(str);
return 0;
}
memmove函数模拟实现
#include <stdio.h>
#include <string.h>
void* my_memmove(void* dest, void* src, size_t num)
{
void* ret = (char*)dest;
assert(dest && src);
if (src > dest)
{
while (num--)
{
*((char*)dest)++ = *((char*)src)++;
}
return ret;
}
else
{
while (num--)
{
*((char*)dest + num) = *((char*)src + num);
}
}
return ret;
}
完结,撒花!🌸🌸🌸