1、strlen函数
![](https://i-blog.csdnimg.cn/blog_migrate/326ee5cc6636936afa3e3f55576542c8.png)
strlen 函数的返回值是size_t,即无符号整型。这是有原因的,因为编写这个库函数的人认为,字符串的长度是不可能为负数的。这样就有可能出现以下错误:
#include<stdio.h>
#include<string.h>
int main()
{
if (strlen("abc") - strlen("abcdef") > 0)
{
printf(">\n");
}
else
{
printf("<=\n");
}
return 0;
}
//>
为什么会打印“>”呢?
因为这个函数理应返回无符号整型,把-3变为无符号整型,就是一个正数。所以我们在自己编写strlen这个函数时,可以考虑把my_strlen函数的返回值设置为int。
编写自己的strlen函数有三种方法:
//计数器
#include<assert.h>
#include<stdio.h>
int my_strlen(const char* str)
//和size_t相比,写int有好处,可以避免无符号数相减的错误答案。
//当然,size_t是考虑到字符串的长度不可能为负数
{
int count = 0;
assert(str);
while (*str != '\0')
{
count++;
str++;
}
return count;
}
//递归
//如果题目要求不创建临时变量而求字符串长度,怎么办?
//递归的方法
int my_strlen(const char* str)
{
assert(str);
if(*str != '\0')
{
return 1 + my_strlen(str + 1);
}
else
{
return 0;
}
}
//指针-指针
//找到b的地址和\0的地址,相减。
char *p = arr;
while (*p!='\0')
p++;
return p - arr;
int main()
{
char arr[] = "bit";
int len = my_strlen(arr);
printf("%d\n",len);
return 0;
}
2、strcpy 函数
![](https://i-blog.csdnimg.cn/blog_migrate/a275b330da5fd8baab807a75a6b47bcd.png)
int main()
{
char arr[] = "abcder";
char arr2[20] = {0};
strcpy(arr2,arr);
printf("%s\n",arr2);
return 0;
}
注意事项:源字符串必须以\0结束
目标字符串的空间一定要足够大,以确保能够放得下源字符串的内容
目标空间必须可修改
接下来时一个错误示范:
char* p = "abcdefghi";
char arr2[20] = "hehe";
strcpy(p,arr2);//p指向的是一个常量字符串,是不可被修改的!
那我们怎么编写自己的strcpy函数呢?
#include<assert.h>
char * my_strcpy(char* dest,const char* src)
//返回的是目标空间的起始地址
{
char* ret = dest;//把目标首地址保存起来
assert(dest && src);
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[] = "hehe\n";
char arr2[20] = { 0 };
my_strcpy(arr2, arr1);
printf("%s\n", arr2);
//printf("%s\n", my_strcpy(arr2, arr1) );
return 0;
}
3、strcat 函数
![](https://i-blog.csdnimg.cn/blog_migrate/93c5fda45c3e144c30daa426a37619e2.png)
int main()
{
char arr1[20] = "hello "; //此处还有一个空格,空格后才是'\0'
char arr2[] = "world";
strcat(arr1,arr2);
printf("%s\n", arr1);
return 0;
}
注意事项:
是从‘\0’开始追加的,覆盖掉目标空间的'\0',然后追加完成时要把源字符串的'\0'也追加过去。所以目标空间里一定要有'\0',这样才以知道从何处开始追加。源字符串也必须有'\0', 这样才知道该追加到什么地方。
目标空间必须可修改,必须足够大。
那我们怎么编写自己的strcat函数呢?
char* my_strcat(char* dest, const char* src)
{
assert(dest && src);
char* ret = dest;
//找目标空间中的'\0'
while (*dest != '\0')
dest++;
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
my_strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
那么,我们可以用strcat函数对同一个字符串进行自己追加自己的操作吗?
int main()
{
char arr1[20] = "bit";
my_strcat(arr1, arr1);
printf("%s\n", arr1);
return 0;
}
//bit\0----->bitbitbitbitbitbitbitbitbit......根本找不到'\0'
不可以!会陷入死循环!因为找不到‘\0’了!
目标字符串的‘\0’被覆盖后,源字符串的'\0'也不存在了。
4、strcmp 函数
![](https://i-blog.csdnimg.cn/blog_migrate/163fb8a20742a609430add06aaf112a9.png)
if("abcdef"=="bddsdw")
{
}
这样比较的其实是这两个字符串首字符的地址,即a和b的地址,而非内容。
所以比较两个字符串的内容时,不应该用‘==’
这个函数比较的其实是对应字符位置上的ASCLL码值大小。
![](https://i-blog.csdnimg.cn/blog_migrate/4c6473942233e53cab64b50509f98d52.png)
那我们怎么编写自己的strcmp函数呢?
#include<stdio.h>
#include<assert.h>
//int my_strcmp(const char* str1, const char* str2)
//{
// assert(str1 && str2 );
// while (*str1 == *str2)
// {
// 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)
{
if (*str1 == '\0')
return 0;
str1++;
str2++;
}
return *str1 - *str2;
}
int main()
{
char arr1[20] = "abcdef";
char arr2[20] = "abcdpp";
int ret=my_strcmp(arr1, arr2);
printf("%d\n", ret);
return 0;
}
if (strcpm(arr1, arr2) == 1)
printf(">\n");
这样可以吗?不可以!因为在不同编译器上可能返回值不一样。
if (strcpm(arr1, arr2) >0)
printf(">\n");
至少也得是这样。
以上几种函数和长度都没关系,它们叫“长度不受限制的字符串函数”。
但是这样的函数可能会出现一些不安全的情况:
int main()
{
char arr1[] = "abcdef";
char arr2[5] = {0};
strcpy(arr2, arr1);//这里arr2放不下啊!程序崩溃了
return 0;
}
5、strncpy,strncat,strncmp 函数
所以,在C语言中,又引入了一组“长度受限制的字符串函数”:
![](https://i-blog.csdnimg.cn/blog_migrate/8f84830a313afbbbdf5684722cf5216c.png)
在strncpy中,要拷贝10个字符,但是实际上只有5个字符,那么它就会自动补充‘\0’,补全10个字符。
![](https://i-blog.csdnimg.cn/blog_migrate/d2984578290d7cf5d7d7da199f8dcfab.png)
让追加几个就追加几个,但是追加完后会在最后面补充一个'\0'。
自己给自己追加的情况,用这个函数。它甚至还会在最后面放上一个'\0'。
![](https://i-blog.csdnimg.cn/blog_migrate/31f5293f5a7b601830e802cb0466162f.png)
它们和原来的字符串函数相比,多了一个参数n
6、strstr 函数
![](https://i-blog.csdnimg.cn/blog_migrate/a510adb664ba0debbe13f413a622fd90.png)
int main()
{
char arr1[20] = "abcdefbcd";//有2个bcd,返回第一次出现的位置
char arr2[20] = "bcd";
char* p=strstr(arr1, arr2);
if (p == NULL)
{
printf("找不到\0");
}
else
{
printf("%s\n", p);
}
printf("%s\n", arr1);
return 0;
}
与之相似的函数有:
strchr 函数:找第一次出现的位置:
![](https://i-blog.csdnimg.cn/blog_migrate/552777769c023354b9d74d3fd902c6a5.png)
strrchr 函数:找最后一次出现的位置:
![](https://i-blog.csdnimg.cn/blog_migrate/3d6fa41f9863ac62b3c7c14e954f4501.png)
那么,strstr这个函数怎么实现呢?
char* my_strstr(const char* str1, const char* str2)
{
char* s1 = NULL;
char* s2 = NULL;
char* cp = (char*)str1;
while (*cp)
{
s1 = cp;
s2 = (char*)str2;
while (*s1 && *s2 && *s1 == *s2 )
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return cp;
}
cp++;
}
return NULL;
}
其实,用库函数,如strncmp3个3个的匹配也可以。
在字符串中查找一个字符串,还有一种效率更高的算法:
经典算法KMP算法。
感兴趣的可以自己去了解一下。
7、strtok 函数
![](https://i-blog.csdnimg.cn/blog_migrate/4bb8559014d1055aa35ef5bc5f58a249.png)
delimiters参数是个字符串,定义了用作分隔符的字符集合
第一个参数指定一个字符串,它包含了0个或者多个由delimiters字符串中一个或者多个分隔符分割的标记。
strtok函数找到str中的下一个称记,并将其用\0结尾,返回一个指向这个标记的指针。(注: strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
strtok函数的第一个参数不为 NULL,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
strtok函数的第一个参数为 NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
如果字符串中不存在更多的标记,则返回NULL指针。
注意:要想使用NULL,必须包含头文件<stdio.h>。
//"zpengwei@yeah.net"
//@.- 分割符
//"192.168.3.212"
//"@."
int main()
{
char arr[] ="zpengwei@yeah.net";
char buf[30] ={0};//zpengwei\0yeah\Onet\0
strcpy(buf,arr);
const char* p = "@.";
char* str = strtok(buf, p);
printf("%s\n",str);
str =strtok(NULL,p);
printf("%s\n",str);
str =strtok(NULL,p);
printf("%s\n",str);
return 0;
}
//太麻烦了,能换一种方法?
//考虑循环
int main()
{
char arr[] ="192.168.3.212";
char buf[30]={0};//zpengwei\oyeah\Onet\0
strcpy(buf,arr);
const char* p = ".";
char* str = NULL;
for(str=strtok(buf,p);str!= NULL; str=strtok(NULL, p))
{
printf("%s\n",str);
}
return 0;
}
这个函数应该是有记忆功能的。所以可能涉及到静态变量static。
注:char * buf ="zpengwei@yeah.net";——可不可以?
当然不行,这是一个常量字符串,不可修改它的值。
8、strerror 函数
![](https://i-blog.csdnimg.cn/blog_migrate/343da99697874b7e2c48549d2bf2957a.png)
#include <string.h>
#include <stdio.h>
int main()
{
char* p = strerror(0);
printf("%s\n",p);
p = strerror(1);
printf("%s\n", p);
p = strerror(2);
printf("%s\n",p);
p = strerror(3);
printf("%s\n",p);
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/83c8deb0daad6dcc2b4878ae905b4008.png)
C语言的库函数在调用失败的时候,会将一个错误码放在一个叫:errno的变量中,当我们想知道调用库函数是发生了什么错误信息,就可以将:errno中的错误码翻译成错误信息。
errno函数和perror函数
![](https://i-blog.csdnimg.cn/blog_migrate/644bdc1e187d48c8fd292bb546049fd3.png)
#include <errno.h>
int main()
{
//打开文件
//打开文件的时候,如果文件的打开方式是"r"
//文件存在则打开成功,文件不存在打开失败
//打开文件失败的话,会返回NULL
FILE* pf = fopen("test.txt","r");
if(pf == NULL)
{
printf("打开文件失败,原因是:&s\n",strerror(errno))
return 1;
}
//读写文件
//...
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/388dd76e44ede377a83ed0b29a892376.png)
int main()
{
//打开文件
//打开文件的时候,如果文件的打开方式是"r"
//文件存在则打开成功,文件不存在打开失败
//打开文件失败的话,会返回NULL
FILE* pf = fopen("test.txt","r");
if(pf == NULL)
{
perror("打开文件失败");
//printf + strerror
return 1;}
//读写文件
//..
//关闭文件
fclose(pf);
pf = NULL;
return 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 | 任何可打印字符,包括图形字符和空白字符 |
注意:需要包含头文件<ctype.h>.
字符转换函数:
![](https://i-blog.csdnimg.cn/blog_migrate/d47db62e74b8e1a2454ee389db377b41.png)
![](https://i-blog.csdnimg.cn/blog_migrate/cefd91c31fa4e2426f2b02601e6a0096.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d0f853711560426a4c138372679447a8.png)
运用循环可以实现整个字符串的大小写转换。
int main()
{
char arr[128]={0};
gets(arr);
int i=0;
while(arr[i])
{
if(isupper(arr[i]))
{
arr[i]=tolower(arr[i]);
}
i++;
printf("%c ",arr[i]);
}
return 0;
}
函数原型图片截图来源于网址:https://cplusplus.com