目录
求字符串长度
认识strlen
stelen是一个用于求取字符串长度的函数,它返回的是一个字符串'\0'之前的字符个数使用strlen需要包含<string.h>这个头文件。
函数声明:
cpp
size_t strlen ( const char * str );
注意:
- strlen所求的字符串当中必须包含'\0',否则函数将会一直寻找'\0'。
- strlen返回值为size_t,是一个无符号的整数。
一个错误案例:
cpp
int main()
{
const char* str1 = "abcdef";
const char* str2 = "bbb";
if (strlen(str2) - strlen(str1) > 0)
printf("str2>str1\n");
else
printf("str1>str2\n");
}
输出结果:

解释:
strlen(str1)=6,strlen(str2)=3,3-6=-3,
-3 的 原 码 为:10000000 00000000 00000000 00000011
在内存中存储的补码为:11111111 11111111 11111111 11111101
由于这是两个无符号数相加减,所得到的数也将其当作无符号数处理,其原码就是补码,所以内存中存储的补码11111111 11111111 11111111 11111101实际上也是原码。翻译过来为4,294,967,293。
自主实现strlen
cpp
size_t my_strlen(const char* str)
{
int count = 0;
while (*str != '\0')
{
count++;
str++;
}
return count;
}
int main()
{
size_t sz = my_strlen("abc");
printf("%u\n", sz);
return 0;
}
字符串拷贝
认识strcpy
strcpy是为一个字符串(目标字符串)拷贝另一个字符串(源字符串)的'\0'及之前内容过来。
函数声明:
cpp
char* strcpy(char * destination, const char * source );
注意:
- 源字符串必须以'\0'结尾。
- 源字符串的'\0'也会被拷贝。
- 目标空间必须足够大,以确保能放下源字符串的所有内容。
- 目标空间必须可变。
一个错误案例:
cpp
int main()
{
char arr1[20] = "xxxxxxxxxxxxxxx";
char arr2[] = { 'a', 'b', 'c', 'd', 'e' };
strcpy(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
其中,arr2中并没有'\0'
通过逐过程调试观察:




可以看到,在没有'\0'时,strcpy并不知道复制到哪里时停止,程序运行出现错误。
打印结果:

加上'\0'后:
cpp
int main()
{
char arr1[20] = "xxxxxxxxxxxxxxx";
char arr2[] = { 'a', 'b', 'c', 'd', 'e', '\0'};
strcpy(arr1, arr2);
printf("%s\n", arr1);
return 0;
}

'\0'也被拷贝到arr1中。

自主实现strcpy
cpp
char* my_strcpy(char* dest, const char* src)
{
char* ret = dest;
assert(dest != NULL);
assert(src != NULL);
while (*src != '\0')
{
*dest = *src;
src++;
dest++;
}
*dest = *src;// 拷贝\0
return ret;
}
int main()
{
char arr1[20] = "hello world";
char arr2[] = "xxxxx";
my_strcpy(arr1, arr2);
printf("%s", my_strcpy(arr1, arr2));
return 0;
}
简化代码:
cpp
char* my_strcpy(char* dest, const char* src)
{
char* ret = dest;
assert(dest != NULL);
assert(src != NULL);
while (*dest++ = *src++);
return ret;
}
int main()
{
char arr1[20] = "hello world";
char arr2[] = "xxxxx";
my_strcpy(arr1, arr2);
printf("%s", my_strcpy(arr1, arr2));
return 0;
}
strncpy
将源的第一个数字字符复制到目标。如果在复制num个字符之前找到源C字符串的末尾(由空字符表示),则目标将填充零,直到总共写入num个字符。
函数声明:
cpp
char * strncpy ( char * destination, const char * source, size_t num );
注意:
- 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标后边追加0,直到num个
- 如果目标原本存有字符串,则在拷贝只会改变其前面num个字符,不会在拷贝完num个后自动追加'\0'
使用案例:
cpp
int main()
{
char arr1[20] = { '1', '2', '\0', '4', '5', '6', '7'};
char arr2[] = "abcdefg";
printf("%s", strncpy(arr1, arr2, 8));
return 0;
}

cpp
int main()
{
char arr1[20] = { '1', '2', '\0', '4', '5', '6', '7'};
char arr2[] = "abc";
printf("%s", strncpy(arr1, arr2, 8));
return 0;
}

cpp
int main()
{
char arr1[20] = "xxxxxxxxxxx";
char arr2[] = "abcdefg";
printf("%s", strncpy(arr1, arr2, 3));
return 0;
}

字符串拼接
认识strcat
将源字符串的副本附加到目标字符串,目标被源的第一个字符覆盖,并在末尾包含一个空字符,由目的地中的两个字符串连接而成的新字符串。
也就是将一个字符串添加到另一个字符串的字符'\0'之前,组成一个新的字符串。
注意:
- 源字符串必须'/0'结束
- 目标字符串必须足够大,能容纳下源字符串的内容
- 目标空间必须可修改
使用案例:
cpp
int main()
{
char arr1[20] = "hello world";
char arr2[] = "xxxxx";
strcat(arr1, arr2);
printf("%s", arr1);
return 0;
}
监视:

运行结果:

自主实现sracat
cpp
char* my_strcat(char* dest, const char* src)
{
assert(dest);
assert(src);
char* ret = dest;
// 1.找目标空间中的\0
while (*dest)
{
dest++;
}
while (*dest++ = *src++);
return ret;
}
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
printf("%s", my_strcat(arr1, arr2));
return 0;
}
strncat
将源的第一个数字字符附加到目标,再加上一个终止空字符。如果源代码中C字符串的长度小于num,则只复制直到终止空字符的内容。
函数声明:
cpp
char * strncat ( char * destination, const char * source, size_t num );
注意:
- 该函数为目标字符串拼接完字符串后,会主动添加一个主动字符'\0'
- 源字符串长度不足num个,会将整个源字符串复制完后停止
- 目标字符串的容器必须足够大
使用案例:
cpp
int main()
{
char arr1[20] = "1234567";
char arr2[] = "abcdefg";
printf("%s", strncat(arr1, arr2, 3));
return 0;
}

字符串大小比较
认识strcmp
此函数用于比较两个字符串的大小,开始比较每个字符串的第一个字符。如果它们彼此相等,则继续使用以下成对字符,直到字符不同或达到终止空字符。
标准规定:
- 第一个字符串大于第二个字符串,则返回大于0的数字
- 第一个字符串等于第二个字符串,则返回0
- 第一个字符串小于第二个字符串,则返回小于0的数字
cpp
strcmp("abcde", "abq");
前面两个字符ascall码值相等,到第三个字符时,c小于q,所以第一个字符串小于第二个字符串,返回一个小于0的数字。

cpp
strcmp("abzde", "abq");
前面两个字符ascall码值相等,到第三个字符时,z大于q,所以第一个字符串大于第二个字符串,返回一个大于0的数字。

cpp
strcmp("abqde", "abq");
前面三个字符ascall码值相等,到第四个字符时,字符串二达到终止字符,所以第一个字符串大于第二个字符串,返回一个大于0的数字。

cpp
strcmp("abq", "abq");
两个字符串相等,返回0。

自主实现strcmp
cpp
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()
{
int ret = my_strcmp("abq", "abq");
printf("%d", ret);
return 0;
}
注意:
字符串一大于字符串二返回1,字符串一小于字符串二返回-1只是在VS编译器环境下是这样,不同编译器下实现的效果是有差异的。标准只规定了大于返回大于0的数字,小于返回小于0的数字,没有规定具体的返回值。
strncmp
比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。
函数声明:
cpp
int strncmp ( const char * str1, const char * str2, size_t num );
使用案例:
cpp
int main()
{
char arr1[] = "abcabcd";
char arr2[] = "abcdefg";
printf("%d", strncmp(arr1, arr2, 3));
return 0;
}
arr1和arr2并不相同,但是只比较前三个字符串便会得到两个字符串相等:

cpp
int main()
{
char arr1[] = "abcaxyz";
char arr2[] = "abcdefg";
printf("%d", strncmp(arr1, arr2, 5));
return 0;
}
可以看到arr1到第五个字符开始就明显大于arr2,但strncmp比较到第四个出现不同字符时就会停止返回结果了。

字符串中寻找子字符串
认识strstr
返回一个指向str1中str2第一次出现的指针,如果str2不是str1的一部分,则返回一个空指针。
案例:
cpp
int main()
{
char arr1[] = "abcdefabcdef";
char arr2[] = "def";
char* ret = strstr(arr1, arr2);
printf("%s", ret);
return 0;
}
strstr返回的是子字符串在字符串中第一次出现的指针。
结果:

注意:
当字符串中没有子字符串时,strstr会返回一个NULL;
自主实现strstr
cpp
char* my_strstr(char* str1, char* str2)
{
char* cp = str1;
char* s1 = cp;
char* s2 = str2;
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);
printf("%s", ret);
return 0;
}
根据符号截取字符串
认识strtok
strtok可以根据数组中定义的分隔符去截取字符串。
函数声明:
cpp
char * strtok ( char * str, const char * sep );
- sep参数是个字符串,定义了用作分隔符的字符集合
- 第一个参数指定一个字符串,它包含了0个或多个由sep字符串中一个或多个分隔符分割的标记
- strtok函数找到str中的下一个标记,并将其用\0结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
- strtok函数的第一个参数不为NULL,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
- strtok函数的第一个参数为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
- 如果字符串中不存在更多的标记,则返回NULL指针。
使用案例:
cpp
int main()
{
char arr[] = "sixlegs@yeah.net";
char copy[30];
strcpy(copy, arr);
char sep[] = "@.";
char * ret = strtok(copy, sep);
printf("%s\n", ret);
ret = strtok(NULL, sep);
printf("%s\n", ret);
ret = strtok(NULL, sep);
printf("%s\n", ret);
return 0;
}
输出结果:

但是以上用法有一个局限性,因为arr是我们自己定义的,我们知道他被两个分隔符分成了三段,所以在想要根据自定义的分隔符分割未知的字符串时便有了以下写法。
cpp
int main()
{
char arr[] = "sixlegs@yeah.net";
char copy[30];
strcpy(copy, arr);
char sep[] = "@.";
char* ret = NULL;
for (ret = strtok(copy, sep); ret != NULL; ret = strtok(NULL, sep))
{
printf("%s\n", ret);
}
return 0;
}

将错误码转换成错误信息
认识strerror
库函数在执行的时候,发生了错误,会将一个错误码存放errno这个变量中,errno是C语言提供的一个全局变量。
当我们在访问网页时,如果网页发生了错误,返回了一个404的错误码,这时候要想知道发生了什么错误,就得将这个错误码翻译成错误信息。而strerror的功能就是返回错误码所对应的错误信息。
cpp
int main()
{
for (int i = 0; i < 10; i++)
{
printf("%s\n", strerror(i));
}
return 0;
}

cpp
int main()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
printf("fopen: %s\n", strerror(errno));
// perror 不需要手动打印
perror("fopen");
return 1;
}
fclose(pf);
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 | 任何可打印字符,包括图形字符和空白字符 |
字符转换
使用字符分类函数将输入的大写字母转换成小写字母:
cpp
#include <ctype.h>
int main()
{
char arr[20] = { 0 };
gets_s(arr);// 遇到空格继续读
char* p = arr;
while (*p)
{
if (isupper(*p))
{
*p = tolower(*p);
}
p++;
}
printf("%s\n", arr);
return 0;
}