string.h和stype.h常用函数讲解,隔壁老王看了直呼eazy!C语言
Posted 王亦优 foxny
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了string.h和stype.h常用函数讲解,隔壁老王看了直呼eazy!C语言相关的知识,希望对你有一定的参考价值。
📌 本文作者: Foxny
📃 更新记录: 2021.7.20
❌ 勘误记录: 无
📜 本文声明: 由于作者水平有限,本文有错误和不准确之处在所难免,本人也很想知道这些错误,恳望读者批评指正!
前言
📚 在C语言中对字符和字符串的处理是很常见的,但是C语言本身是并没有字符串类型的,字符串通常放在 常量字符串 中或 字符数组 中。字符串常量 适用于那些对它不做修改的字符串函数。
一、求字符串长度
0x00 strlen 函数
📜 头文件: string.h
🔍 链接:http://www.cplusplus.com/reference/cstring/strlen/?kw=strlen
📚 说明:字符串以 \\0 作为结束标志,strlen 返回的是在字符串中 \\0 前面出现的字符个数
📌 注意事项:
① 参数指向的字符串必须以 \\0 结束
② 函数的返回值为 size_t ,无符号(unsigned)
💬 代码演示:
#include <stdio.h>
#include <string.h>
int main()
{
int len = strlen("abcdef");
printf("%d\\n", len);
return 0;
}
🚩 6
二、长度不受限制的字符串函数
0x00 strcpy 函数
📜 头文件: string.h
🔍 链接:http://www.cplusplus.com/reference/cstring/strcpy/
📚 说明:字符串拷贝,将含有 \\0 的字符串复制到另一个地址空间,返回值的类型为 char*
📌 注意事项:
① 源字符串 src 必须以 \\0 结束
② 会将源字符串 src 中的 \\0 一同拷贝到目标空间 dest
③ 目标空间必须足够大,以确保能够存放源字符串 dest (下面讲 strncmp 的时候演示)
④ 目标空间必须可变,即目标空间 dest 不可以被 const 声明
💬 代码演示:
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "abcdefghi";
char arr2[] = "123";
printf("拷贝前:%s\\n", arr1);
strcpy(arr1, arr2); // 字符串拷贝(目标空间,源字符串)
printf("拷贝后:%s\\n", arr1);
return 0;
}
🚩 运行结果如下:
0x02 strcat 函数
📜 头文件: string.h
🔍 链接: http://www.cplusplus.com/reference/cstring/strcat/
📚 说明:将 src 所指向的字符串复制到 dest 所指向的字符串后面(删除 *dest 原来末尾的 \\0 )
📌 注意事项:
① 源字符串 src 必须以 \\0 结束
② 会将源字符串 src 中的 \\0 一同拷贝到目标空间 dest ,并删除 *dest 原来末尾的 \\0
③ 目标空间必须足够大,以确保能够存放源字符串 dest
④ 目标空间必须可变,即目标空间 dest 不可以被 const 声明
💬 代码演示:
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[30] = "hello";
char arr2[] = "world";
strcat(arr1, arr2);
printf("%s\\n", arr1);
return 0;
}
🚩 hello world
0x03 strcmp 函数
📜 头文件: string.h
🔍 链接: http://www.cplusplus.com/reference/cstring/strcmp/
📚 说明:用于比较两个字符串并根据比较结果返回整数, 两个字符串自左向右逐个字符相比,按照 ASCII值 大小相比较,从第一对字符开始比,如果相等则比下一对,直到出现不同的字符或遇 \\0 才停止。对比规则如下:
💬 代码演示:
#include <stdio.h>
#include <string.h>
int main()
{
char *p1 = "abcdef";
char *p2 = "aqwer";
int ret = strcmp(p1, p2); // p1和p2比
// a==a, 对比下一对,b<q,所以p2大
printf("%d\\n", ret);
return 0;
}
🚩 -1( 返回负数,所以 p1< p2 )
🔑 解析:
📌 注意事项:根据编译器的不同,返回的结果也不同
在 VS2013 中,大于返回 1,等于返回 0,小于返回 -1。但在 Linux-gcc 中,大于返回正数,等于返回0,小于返回负数。因此,我们需要注意判断部分的写法:
// 不推荐 ❌
if(strcmp(p1, p2) == 1) {
printf("p1 > p2");
} else if(strcmp(p1, p2 == 0)) {
printf("p1 == p2");
} else if(strcmp(p1, p2) == -1) {
printf("p1 < p2");
}
// 推荐 ✅
if(strcmp(p1, p2) > 0) {
printf("p1 > p2");
} else if(strcmp(p1, p2 == 0)) {
printf("p1 == p2");
} else if(strcmp(p1, p2) < -1) {
printf("p1 < p2");
}
三、长度受限制的函数字符串
0x00 strncpy 函数
📜 头文件: string.h
🔍 链接: http://www.cplusplus.com/reference/cstring/strncpy/
📚 说明:从源字符串中拷贝 n 个字符到目标空间
📌 注意事项:
① 如果源字符串的长度小于 n,则拷贝完源字符串之后,在目标的后面追加 0,填充至 n 个
② dest 和 src 不应该重叠(重叠时可以用更安全的 memmove 替代)
③ 目标空间必须足够大,以确保能够存放源字符串 dest
④ 目标空间必须可变,即目标空间 dest 不可以被 const 声明
💬 代码演示:
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[5] = "abc";
char arr2[] = "hello world";
strncpy(arr1, arr2, 4); // 从arr2中拷贝4个到arr1
printf("%s\\n", arr1);
return 0;
}
🚩 hell
❌ 目标空间不够大会导致报错:
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[5] = "abc"; // 大小为5的数组
char arr2[] = "hello world";
strncpy(arr1, arr2, 6); // 要求拷贝6个字节
printf("%s\\n", arr1);
return 0;
}
🚩 运行结果如下:
0x01 strncat 函数
📜 头文件: string.h
🔍 链接: http://www.cplusplus.com/reference/cstring/strncat/
📚 说明:追加 n 个字符到目标空间
📌 注意事项:如果源字符串的长度小于 n,则只复制 \\0 之前的内容。
💬 代码演示:
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[30] = "hello";
char arr2[] = "world";
strncat(arr1, arr2, 3); // 从arr2中取3个追加到arr1中
printf("%s\\n", arr1);
return 0;
}
🚩 hellowor
0x02 strncmp 函数
🔍 链接: http://www.cplusplus.com/reference/cstring/strncmp/
📚 说明:比较到出现另个字符不一样或者一个字符串结束或者 n 个字符全部比较完。
( 除了增了了个 n,其他和 strcmp 一样 )
💬 代码演示:
#include <stdio.h>
#include <string.h>
int main()
{
const char* p1 = "abczdef";
const char* p2 = "abcqwer";
// int ret = strcmp(p1, p2);
int ret = strncmp(p1, p2, 1);
int ret2 = strncmp(p1, p2, 4);
printf("%d %d\\n", ret, ret1);
return 0;
}
🚩 0 1
四、字符串查找
0x00 strstr 函数
📜 头文件: string.h
🔍 链接: http://www.cplusplus.com/reference/cstring/strstr/
📚 说明:返回字符串中首次出现子串的地址。若 str2 是 str1 的子串,则返回 str2 在 str1 中首次出现的地址。如果 str2 不是 str1 的子串,则返回 NULL 。
💬 代码演示:是子串,返回首次出现的地址
#include <stdio.h>
#include <string.h>
int main()
{
char* p1 = "abcdef";
char* p2 = "def";
char* ret = strstr(p1, p2); // 判断p2是否是p1的子串
printf("%s\\n", ret);
return 0;
}
🚩 def ( p2 是 p1 的子串,所以返回 def )
💬 代码演示:不是子串,返回 NULL
#include <stdio.h>
#include <string.h>
int main()
{
char* p1 = "abcdef";
char* p2 = "zzz";
char* ret = strstr(p1, p2);
printf("%s\\n", ret);
return 0;
}
🚩 (null)
💬 我们用 if 判断来添加描述,更好地呈现:
#include <stdio.h>
#include <string.h>
int main()
{
char* p1 = "abcdef";
char* p2 = "def";
char* ret = strstr(p1, p2);
if ( ret == NULL ) {
printf("子串不存在\\n");
} else {
printf("%s\\n", ret);
}
return 0;
}
0x01 strtok 函数
📜 头文件: string.h
🔍 链接: http://www.cplusplus.com/reference/cstring/strtok/
📚 说明:
📌 注意事项:strtok 会破坏原字符串,分割后原字符串保留第一个分割符前的字符
💬 代码演示:分割ip
#include <stdio.h>
#include <string.h>
int main()
{
//192.168.0.1
//192 168 0 1 - strtok
char ip[] = "192.168.0.1";
// const char* sep = ".";
// char* ret = strtok(ip, sep);
char* ret = strtok(ip, ".");
printf("%s\\n", ret);
ret = strtok(NULL, ".");
printf("%s\\n", ret);
ret = strtok(NULL, ".");
printf("%s\\n", ret);
ret = strtok(NULL, ".");
printf("%s\\n", ret);
return 0;
}
🚩 运行结果如下:
💬 代码演示:分割邮箱
#include <stdio.h>
#include <string.h>
int main()
{
//1300300100@qq.com
//1300300100 qq com
char arr[] = "1300300100@qq.com";
printf("原字符串: %s\\n", arr);
const char* sep = "@."; // 创建sep
char arr1[30];
char* ret = NULL;
strcpy(arr1, arr); // 将数据拷贝一份,保留arr数组的内容
// 分行打印切割内容
for (ret = strtok(arr, sep); ret != NULL; ret = strtok(NULL, sep)) {
printf("%s\\n", ret);
}
printf("保留的原内容:%s\\n", arr1); // 保存的arr数组的内容
printf("分割后原字符串被破坏: %s\\n", arr); // 分割后原字符串保留第一个分割符前的字符
return 0;
}
🚩 运行结果如下:
0x02 strerror 函数
📜 头文件: string.h
🔍 链接: http://www.cplusplus.com/reference/cstring/strerror/
📚 说明:返回错误码,返回错误码所对应的错误信息
💬 代码演示:
#include <string.h>
#include <stdio.h>
#include <errno.h>
int main()
{
// 错误码 错误信息
// 0 - No error
// 1 - Operation not permitted
// 2 - No such file or directory
// ...
//errno 是一个全局的错误码变量
//当c语言的库函数在执行过程中,发生了错误,
//就会把对应的错误码赋值到errno中
char* str = strerror(errno);
printf("%s\\n", str);
return 0;
}
🚩 No error
📚 关于 errno:查看 errno 的详细介绍 [百度百科]
errno 是记录系统的最后一次错误代码。代码是一个 int 型的值,在 errno.h 中定义
💬 文件操作的时候可以使用(后面会讲文件操作)
FILE* pf = fopen("test.txt", "r");
if ( pf == NULL ) {
printf("%s\\n", strerror(errno));
} else {
printf("*****open file success*****\\n")
}
五、字符操作
0x00 字符分类
📜 头文件: stype.h
💬 代码演示:islower
#include <stdio.h>
#include <ctype.h>
int main()
{
char ch1 = 'a';
int ret = islower(ch1); // 判断ch1是否为小写
printf("%d\\n", ret);
char ch2 = 'B';
int res = islower(ch2); // 判断ch2是否为小写
printf("%d\\n", res);
return 0;
}
🚩 运行结果如下:
0x01 字符转换
📜 需引入头文件 stype.h
💬 代码演示:tolower
int main()
{
char ch = tolower('Q'); // 大写转小写
putchar(ch);
return 0;
}
🚩 q
💬 代码演示:toupper
int main()
{
char ch = toupper('q'); // 小写转大写
putchar(ch);
return 0;
}
🚩 Q
💬 代码演示:字符串内容全部大写转小写( 利用 while 循环 )
#include <stdio.h>
#include <ctype.h>
int main()
{
char arr[] = "I Am A Student";
int i = 0;
while(arr[i]) {
if ( isupper(arr[i]) ) {
arr[i] = tolower(arr[i]);
}
i++;
}
printf("%s\\n", arr);
return 0;
}
🚩 i am a student
💬 模拟实现 Python 中的 swapcase 函数 ( 字符串大小写互换 )
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
void swapcase(char arr[])
{
assert(arr != NULL); // 断言防止传空
int i = 0;
while (arr[i] != '\\0') {
if (islower(arr[i])) { //是小写吗?
arr[i] = toupper(arr[i]); //如果是,让它变成大写
} else { //不是小写
arr[i] = tolower(arr[i]); //把它变成小写
}
i++;
}
}
int main()
{
char arr[] = "AaBbCcDdEeFf";
swapcase(arr);
printf("%s\\n", arr);
return 0;
}
🚩 aAbBcCdDeEfF
六、字符操作函数
0x00 memcpy 函数
📜 头文件: string.h
🔍 链接: http://www.cplusplus.com/reference/cstring/memcpy/
📚 说明:从源内存地址 src 的起始位置开始拷贝 n 个字节到目标内存地址 dest 中
📌 注意事项:
① memcpy 没有刹车,这个函数遇到 \\0 并不会停下来
② 如果 src 和 dest 有任何的重叠,复制的结果都是未定义的
💬 代码演示:
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[] = {1, 2, 3, 4, 5};
int arr2[5] = {0};
memcpy(arr2, arr1, sizeof(arr1));
// 打印 arr2 的内容
int i = 0;
for(i=0; i<5; i++) {
printf("%d ", arr2[i]);
}
return 0;
}
🚩 1 2 3 4 5
💬 代码演示:拷贝结构体
#include <stdio.h>
#include <string.h>
struct S
{
char name[20];
int age;
};
int main()
{
struct S arr3[] = { {"张三", 20}, {"李四", 30} };
struct S arr4[3] = { 0 };
memcpy(arr4, arr3, sizeof(arr3));
return 0;
}
🔑 调试一下看看是否拷贝成功:
0x02 memmove 函数
📜 头文件: string.h
🔍 链接: http://www.cplusplus.com/reference/cstring/memcpy/
📚 说明:用于拷贝字节,如果目标区域和源区域有重叠时,memmove 能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,但复制后源内容会被更改。
📌 注意事项:
① 和 memcpy 的差别就是 memmove 函数处理的源内存块和目标内存块时可以重叠的
② 如果原空间和目标空间出现重叠,应使用 memmove 函数处理
C语言标准要求:
memcpy 用来处理不重叠的内存拷贝,而 memmove 用来处理重叠内存的拷贝。
💬 代码演示:
#include <stdio.h>
#include <string.h>
int main()
{
int arr[] = {1,2,3,4,5,6,7,8,9,10};
int i = 0;
memmove(arr+2, arr, 20);
for(i=0; i<10; i++) {
printf("%d ", arr[i]);
}
return 0;
}
🚩 运行结果如下:
0x03 memcmp 函数
📜 头文件: string.h
🔍 链接: http://www.cplusplus.com/reference/cstring/memcmp/
📚 说明:比较 ptr1 和 ptr2 指针开始的 n 个字节,按字节比较
📌 注意事项:memcmp 不同于 strcmp,memcmp 遇到 \\0 不会停止比较
💬 代码演示:
#include <stdio.h>
#include <string.h>
int main()
{
float arr1[] = {1.0, 2.0, 3.0, 4.0};
float arr2[] = {1.0, 3.0};
int ret = memcmp(arr1, arr2, 8); // arr1是否比arr2大,比较8个字节
printf("%d\\n", ret);
return 0;
}
🚩 -1 ( 说明 arr1 小于 arr2 )
0x04 memset 函数
📜 头文件: string.h
🔍 链接: http://www.cplusplus.com/reference/cstring/memset/
📚 说明:将某一块内存中的内容全部设置为指定的值,通常为新申请内存做初始化工作。
📌 注意事项:memset 是以字节为单位设置内存的
💬 代码演示:把整型数组将前 20 个字节全部设置为 1
#include <stdio.h>
#include <string.h>
int main()
{
// 40
int arr[10] = {0};
memset(arr, 1, 20); // 将前20个字节全部设置为1
return 0;
}
本章完
以上是关于string.h和stype.h常用函数讲解,隔壁老王看了直呼eazy!C语言的主要内容,如果未能解决你的问题,请参考以下文章