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 的子串,则返回 str2str1 中首次出现的地址。如果 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 不同于 strcmpmemcmp 遇到 \\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语言的主要内容,如果未能解决你的问题,请参考以下文章

隔壁老王都熟练使用函数式接口了,你还在等什么?(快来收藏)

隔壁老王都熟练使用函数式接口了,你还在等什么?(快来收藏)

string.h是啥头文件

常用头文件及函数

c语言string.h中常用的字符串操作函数

c语言string.h中常用的字符串操作函数