https://legacy.cplusplus.com/
我们使用上面这个网站,搜索strlen
,就能了解strlen
的使用。
上面说strlen
是用来求字符串长度的,而它统计的是\0之前出现的字符的个数,也就是说字符串的结束标志是\0。
那举个列子来看一下:
int main()
{
char arr[] = "abcdef";
//a b c d e f \0
size_t len = strlen(arr);
printf("%zd\n", len);
return 0;
}
这里计算出的结果就是arr中字符串的长度,如下显示:
但值得注意的是:
1.strlen
函数要正确获得字符串长度的话,字符串中必须得有**\0**。
2. 要注意strlen
的返回值类型是size_t
。
我们知道strlen
是用来求字符串的长度的,那我们实现就只需要遍历字符串,统计一下出现在\0,之前的字符个数就行了。
使用count
计数器,统计字符串的长度。
不过在计算字符串长度之前要先断言一下字符串是否为空assert(str != NULL)
。
#include
#include
#include
size_t my_strlen(const char * str)
{
size_t count = 0;
assert(str != NULL);
while (*str != '\0')
{
count++;
str++;
}
return count;
}
int main()
{
char arr[] = "abc";
size_t len = my_strlen(arr);
printf("%zd\n", len);
return 0;
}
我们在前面提到过指针-指针也就是地址-地址,得到的是指针和指针之间元素的个数。
#include
#include
#include
size_t my_strlen(const char* str)
{
const char* start = str;
while (*str != '\0')
{
str++;
}
return str - start;
}
int main()
{
char arr[] = "abc";
size_t len = my_strlen(arr);
printf("%zd\n", len);
return 0;
}
当我们不使用使用临时变量时,怎么来模拟实现strlen
函数呢?
此时就需要用到递归的思想:大事化小
当没有访问到\0时,我们每次判断是否访问到\0,就进行加1,然后继续调用my_strlen。
第一次1+my_strlen(“bc”);
第二次1+1+my_strlen(“c”);
第三次1+1+1+my_strlen(“”);
第四次就进不去,返回了0,最后1+1+1+0 = 3。
#include
#include
#include
size_t my_strlen(const char* str)
{
if (*str != '\0')
return 1 + my_strlen(str + 1);
else
return 0;
}
int main()
{
char arr[] = "abc";
size_t len = my_strlen(arr);
printf("%zd\n", len);
return 0;
}
我们会发现在使用strcpy
需要包含#include
。
strcpy
函数的功能:拷贝字符串。
我们先来看一个示例:
#include
#include
#include
int main()
{
char arr1[] = "hello abc";
char arr2[4] = "xxx";
strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
我们发现此时代码报错了,这是为什么呢?
发现要将arr1中的拷贝到arr2中,发现arr2的空间根本不够存放arr1,此时才会报错。
所以我们得注意:
我们再看一个例子:
#include
#include
#include
int main()
{
char arr1[4] = {'a', 'b', 'c', '\0'};
char arr2[10] = "xxx";
strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
此时编译器就没有出现错误了
那我们直接来模拟实现一下strcpy
。
要模拟实现strcpy
,就需要两个指针来对其进行访问,arr1为源头指针src,arr2为目的指针dest。我们通过对arr1的遍历,将arr1中拷贝\0 前的字符都传给arr2。
#include
#include
#include
void my_strcpy(char* dest, char* src)
{
//拷贝\0 前的字符
while (*src != '\0')
{
*dest = *src;
dest++;
src++;
}
//拷贝\0
*dest = *src;
}
int main()
{
char arr1[] = "abcdef";
char arr2[20] = { 0 };
my_strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
把后置++直接加到while()
判断中
#include
#include
#include
void my_strcpy(char* dest, char* src)
{
while (*dest++ = *src++)
{
;
}
}
int main()
{
char arr1[] = "abcdef";
char arr2[20] = { 0 };
my_strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
那还能再修改吗?
当然。
这里加了对src和dest的断言。
void my_strcpy(char* dest, char* src)
{
//NULL
assert(dest);
assert(src);
while (*dest++ = *src++)
{
;
}
}
int main()
{
char arr1[] = "abcdef";
char arr2[20] = { 0 };
my_strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
看版本4: dest指向的空间是需要改变的,但是src指向的空间是不期望被给变的。
void my_strcpy(char* dest, const char* src)
{
//NULL
assert(dest);
assert(src);
while (*dest++ = *src++)
{
;
}
}
int main()
{
char arr1[] = "abcdef";
char arr2[20] = { 0 };
my_strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
还有一种版本5:
char* my_strcpy(char* dest, const char* src)
{
char* ret = dest;
//NULL
assert(dest);
assert(src);
while (*dest++ = *src++)
{
;
}
return ret;//目标空间的起始地址返回
}
我们同样使用上面那个网站,我们发现strcat
函数返回的是目标空间的起始地址。
strcat
用于字符串追加,那既然是追加,我们先看一个例子。
#include
#include
#include
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
将arr2中的字符串追加到arr1中:
但是同样有需要注意的地方:
这对于我们自己用代码实现strcat
的功能是很重要的。
要模拟实现strcat
就要找到找到目标空间中的\0,然后再进行拷贝。
通过while
循环遍历找到找到目标空间中的\0,然后将源空间中的赋值到目标空间。
下面我们用代码实现一下
char* my_strcat(char* dest, const char* src)
{
char* ret = dest;
assert(dest && src);
while (*dest != '\0')
{
dest++;
}
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[20] = "hello ";//hello world
char* p = "world";
printf("%s\n", my_strcat(arr1, p));
return 0;
}
我们同样使用上面的网站,搜索strcmp
,那一起来看看怎么使用它。
strcmp
是用于比较两个字符串的,不过它比较的是两个两个字符串中对应位置上的字符,而且是按字典序比较。
像这样对应的相比较,如果两个有相同字符,那就继续往后比较。
有三种情况,像上图那种,字符串2中q比字符串1中c大,返回的就是一个小于0的数字。
第二种,字符串2比字符串小,返回的就是一个大于0的数字。
第三种,字符串2和字符串相等,返回的就是0。
实现一下上面的代码来看看结果
#include
#include
int main()
{
int ret = strcmp("abcdef", "abq");
if (ret > 0)
printf(">\n");
else if (ret == 0)
printf("==\n");
else
printf("<\n");
printf("%d\n", ret);
return 0;
}
结果和分析的是一样的。
但这里它返回的值一定是-1吗?
并不是,我们所说的是小于0的数字都可以。
那我们自己来模拟实现一下strcmp
。
首先传的两个字符串不能为空,再逐一遍历两个字符串相同位置的字符进行比较。
要注意的是,我们不改变这两个字符串的内容,所以我们加了const修饰(const char* s1, const char* s2)
。
当两个字符串里面的字符有不同的时候结束循环,再来判断里面字符的字典序号。
#include
#include
#include
int my_strcmp(const char* s1, const char* s2)
{
assert(s1 != NULL);
assert(s2 != NULL);
while (*s1 == *s2)
{
if (*s1 == '\0')
return 0;
s1++;
s2++;
}
if (*s1 > *s2)
return 1;
else
return -1;
}
int main()
{
int ret = my_strcmp("abcdef", "abq");
if (ret < 0)
printf("<\n");
return 0;
}
strncpy
与strcpy
对比发现,strncpy
多了传入的数量,长度受到了限制。
#include
#include
int main()
{
char arr1[10] = "xxxxxxxxx";
char arr2[] = "abc";
strncpy(arr1, arr2, 5);
printf("%s\n", arr1);
return 0;
}
将arr2中的5个字节传给arr1,这里的5是以字节为单位。
#include
#include
int main()
{
char arr1[20] = "abc";
char arr2[] = "def";
strncat(arr1, arr2, 5);
printf("%s\n", arr1);
return 0;
}
#include
#include
int main()
{
char arr1[20] = "abcdef";
char arr2[] = "abcqwer";
int ret = strncmp(arr1, arr2, 4);
printf("%d\n", ret);
return 0;
}
比较两个字符串中的4个字节,发现q比c的字典序大,返回一个小于0的数
这个函数是用来干什么的呢?
strstr
函数的功能:如果str2在str1中出现,那就返回第一次出现的位置;
如果没有出现,就返回NULL
。
就图中而言返回的位置就是str1的d的位置,所以我们来看看相关代码结果如何。
#include
#include
int main()
{
char arr1[] = "abcdefabcdef";
char arr2[] = "def";
/*char* ret = my_strstr(arr1, arr2);*/
char* ret = strstr(arr1, arr2);
if (ret != NULL)
printf("%s\n", ret);
else
printf("找不到\n");
return 0;
}
str2如果子串是空字符串,直接返回str1。
要先保证str1和str2不为空。
因为找不到在匹配的字符串要返回起始地址,所以不在原有的str1和str2上进行,而使用cp先记录下 str1。用s1和s2来继续匹配,当s2遍历完str2时,并没有发现有与str1中相匹配时,就重新返回str2,方便下一次的匹配。
来用代码实现一下
#include
#include
const char* my_strstr(const char* str1, const char * str2)
{
assert(str1);
assert(str2);
const char* cp = str1;
const char* s1 = NULL;
const char* s2 = NULL;
//如果子串是空字符串,直接返回str1
if (*str2 == '\0')
return str1;
while (*cp)
{
s1 = cp;
s2 = str2;
while (*s1 == *s2 && *s1 && *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')
return cp;
cp++;
}
return NULL;
}
int main()
{
char arr1[] = "abcdefabcdef";
char arr2[] = "def";
char* ret = my_strstr(arr1, arr2);
if (ret != NULL)
printf("%s\n", ret);
else
printf("找不到\n");
return 0;
}
strtok
函数其实并不常用,那它又有什么作用呢?
其实strtok
函数就是将给定一个分割符,以分割符为界,将其他部分分开。
那它遇到分割符之后怎么做呢?
在图片的方法中介绍的它把分割符置为NULL
,然后又继续往后查找。
我们看一下它的代码:
#include
#include
int main()
{
char arr[] = "192.168.101.25";//. 分割符
char buf[60] = { 0 };
strcpy(buf, arr);
char* p = ".";
char* r = NULL;
for (r = strtok(buf, p); r != NULL; r = strtok(NULL, p))
{
printf("%s\n", r);
}
return 0;
}
errno
这个变量中errno
是一个C语言的全局变量#include
#include
#include
int main()
{
for (int i = 0; i < 10; i++)
{
char* ret = strerror(i);
printf("%d : %s\n", i, ret);
}
return 0;
}
perror
与strerror
同样都是返回错误的
举个例子看看perror
与strerror
打开文件 ,在读写文件之前,需要打开文件。
如果要打开成功,需要文件是存在的,如果文件不存在,则打开失败,fopen会返回NULL
。
#include
#include
#include
int main()
{
FILE* pf = fopen("add.txt", "r");
if (pf == NULL)
{
perror("fopen");
printf("fopen: %s\n", strerror(errno));
return 1;
}
else
{
printf("打开文件成功\n");
//...
}
return 0;
}
它们给出的都是文件不存在:
perror
与strerro
r不同的地方在于
perror
会直接在屏幕是输出错误的地方,而strerror
则不会,如果需要在屏幕上输出就要用到printf
。
有不足的地方请多多指正,让我们共同进步。