字符串

1. 字符串概述

字符串在开发中是非常重要数据载体,常用于数据传递和存储,具备较强的稳定性和安全性(可以进行加密操作)。针对于字符串操作涉及到拼接,拷贝,查询…

字符串的两种定义方式:

  1. char *arr = “hello”;需要注意的是必须初始化!
  2. char arr[10] = “hello”;

2. 字符串相关函数

补充知识点 字符串数据要求

在 C/C++ 中字符串数据【字符串常量/字符数组】,必须结尾是一个 ‘\0’,建议以下所有函数的两个参数对应字符串均以\0结尾。

以下所有函数针对的都是二进制,与外部数据无关

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    printf("strlen(\"ABC\") : %ld\n", strlen("ABC")); // 3 有效字符个数
    printf("sizeof(\"ABC\") : %ld\n", sizeof("ABC")); // 4 当前字符串占用内存字节数
    /*
    strlen("ABC") : 3
    sizeof("ABC") : 4
        sizeof 多出一个字节,多出的内容是一个字符串标记 '\0'
    */

    /*
    当前 arr 算不算是一个字符数组?
        算
    当前 arr 算不算一个字符串数据?
        不算!!!
    */
    char arr[5] = {'A', 'B', 'C', 'D', 'E'};
    char arr2[5] = {'A', 'B', 'C', 'D', 'E'};

    /*
    对于计算机而言,字符串数据必须是 '\0' 结尾,包括
    打印/数据占位符 %s ,需要展示字符串数据,要求到 '\0' 结束
    但是 arr 和 arr2 中不存在 '\0',且两个字符数组内存空间连续
    因为在调用过程中,未发现 '\0' ,所以打印 arr 内容,展示的
    效果是 arr + arr2  + 后续野值数据
    */
    printf("arr = %s\n", arr);

    /*
    当前 arr3 算不算是一个字符数组?
        算
    当前 arr3 算不算一个字符串数据?
        算!!!
    */
    char arr3[5] = {'A', 'B', 'C', 'D', '\0'};

    return 0;
}

2.1 strcat 和 strncat(不会复制\0)

char *strcat(char *dest, const char *src)

函数功能: 将 src 对应的字符串内容,拼接到 dest 对应的字符串数据空间中,要求 dest 必须有足够的内存空间存储目标 src 字符串数据

参数解释:

  • dest : 通常情况下是一个字符数组形式,可以是静态数组,也可以是通过动态内存(malloc calloc realloc)分配得到的连续存储字符串数据内存空间。要求必须有足够的预留内存空间存储目标 src 数据, 同时要求保证可以满足字符串数据要求
  • src:提供给当前函数作为拼接到 dest 末尾的字符串数据,可以是字符串常量,也可以是字符数组

返回值解释:

  • 返回值内容是当前参数 dest 地址。
char *strncat(char *dest, const char *src, size_t n)

函数功能: 将 src 对应的字符串内容前 n 个字符,拼接到 dest 对应的字符串数据空间中,要求 dest 必须有足够的内存空间存储目标 src 字符串数据

参数解释:

  • dest : 通常情况下是一个字符数组形式,可以是静态数组,也可以是通过动态内存(malloc calloc realloc)分配得到的连续存储字符串数据内存空间。要求必须有足够的预留内存空间存储目标 src 数据, 同时要求保证可以满足字符串数据要求
  • src:提供给当前函数作为拼接到 dest 末尾的字符串数据,可以是字符串常量,也可以是字符数组
  • n: 限制当前 src 中前 n 个字符拼接到目标 dest 之后

返回值解释:

  • 返回值内容是当前参数 dest 地址。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
char *strcat(char *dest, const char *src);
char *strncat(char *dest, const char *src, size_t n);
*/

int main(int argc, char const *argv[])
{
    /*
    使用字符串常量给予字符数组进行赋值操作
    当前 arr 字符串数据中的数据存储情况为
    {'A', 'B', 'C', 'D', '\0', '\0', '\0', '\0', '\0', '\0'}

    当前字符数组中有 6 个 '\0' 存在,如果需要保证当前字符数组数据
    依然满足字符串要求,有且最多可以填充的字符个数为 5 个
    */
    char arr[10] = "ABCD";

    /*
    strcat 操作需要明确的知晓当前操作过程中 src 对应字符串数据是否
    满足当前 dest 剩余内存空间情况。防止出现越界行为。

    strcat(arr, "ABCDEFG");   // 1 不行,超出范围
    strcat(arr, "12345");     // 2 可以!!!
    strcat(arr, "8888");      // 3 可以!!!
    strcat(arr, "987654321"); // 4 不行,超出范围
    */

    strcat(arr, "123");
    printf("arr = %s\n", arr);

    strncat(arr, "ABCDEFG", 2);
    printf("arr = %s\n", arr);

    return 0;
}

2.2 strcpy 和 strncpy(会复制\0,且复制过去从下标为0的元素覆盖)

char *strcpy(char *dest, const char *src)

函数功能: 将 src 对应的字符串内容复制到 dest 指向的内存空间中,要求 dest 必须有足够的内存空间保证字符串数据完整

tips : strcpy 会将 src 对应的完整字符串数据拷贝到 dest 中,同时会将 ‘\0’ 同时提供给 dest

参数解释:

  • dest : 通常情况下是一个字符数组形式,可以是静态数组,也可以是通过动态内存(malloc calloc realloc)分配得到的连续存储字符串数据内存空间。要求必须有足够的预留内存空间存储目标 src 数据, 同时要求保证可以满足字符串数据要求
  • src:提供给当前函数,将 src 对应字符串数据拷贝到 dest 对应内存空间中

返回值解释:

  • 返回值内容是当前参数 dest 地址。
char *strncpy(char *dest, const char *src, size_t n)

函数功能: 将 src 对应的字符串内容前 n 个字符复制到 dest 指向的内存空间中,要求 dest 必须有足够的内存空间保证字符串数据完整

tips : strncpy 仅将限制以内的字符拷贝提供给当前 dest 不会自行补充 '\0’

参数解释:

  • dest : 通常情况下是一个字符数组形式,可以是静态数组,也可以是通过动态内存(malloc calloc realloc)分配得到的连续存储字符串数据内存空间。要求必须有足够的预留内存空间存储目标 src 数据, 同时要求保证可以满足字符串数据要求
  • src:提供给当前函数,将 src 对应字符串数据拷贝到 dest 对应内存空间中
  • n: 将 src 前 n 个字符拷贝到目标 dest 空间中,同时保证字符串数据完整

返回值解释:

  • 返回值内容是当前参数 dest 地址。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
char *strcpy(char *dest, const char *src)
char *strncpy(char *dest, const char *src, size_t n)
*/

int main(int argc, char const *argv[])
{
    /*
    使用字符串常量给予字符数组进行赋值操作
    当前 arr 字符串数据中的数据存储情况为
    {'1', '2', '3', '4', '5', '6', '7', '\0', '\0', '\0'}
    */
    char arr[10] = "1234567";

    strcpy(arr, "ABC");
    /*
    以上 strcpy 执行完毕,arr 中数据存储情况为
    {'A', 'B', 'C', '\0', '5', '6', '7', '\0', '\0', '\0'}
    */

    printf("arr = %s\n", arr);       // ABC ABC4567
    printf("arr[5] = %c\n", arr[5]); // '\0' '6'
    printf("arr[4] = %c\n", arr[4]); // '\0' '5'
    arr[3] = '1';

    printf("arr = %s\n", arr); // ABC ABC4567
    /*
    以上操作完成,arr 中存储的数据情况
    {'A', 'B', 'C', '1', '5', '6', '7', '\0', '\0', '\0'}
    */
    strncpy(arr, "666", 2);
    printf("arr = %s\n", arr); // 66 66C1567
    arr[2] = '\0';

    char arr1[3] = {'C', 'D', 'E'};

    char *str1 = (char *)calloc(10, sizeof(char));
    str1[0] = '1';
    str1[1] = '2';
    str1[2] = '3';
    strcpy(str1, arr1);
    printf("str1 = %s\n", str1); //

    free(str1);

    return 0;
}

2.3 strcmp 和 strncmp

int strncmp(const char *str1, const char *str2, size_t n);

函数功能: 字符串比较函数,将 str1 和 str2 对应的字符串数据,前 n 个字符进行比较,如果一致返回 0,如果不一致,返回结果不为 0

参数解释:

  • str1: 用户提供的字符串数据
  • str2: 用户提供的字符串数据
  • n : 要求比较 str1 和 str2 前 n 个字符是否一致

返回值解释:

  • 如果两个字符串前 n 个字符串一致,返回 0,否则返回值非 0
int strcmp(const char *str1, const char *str2);

函数功能: 字符串比较函数,将 str1 和 str2 对应的字符串数据进行逐一比较,如果一致返回 0,如果不一致,返回结果不为 0

参数解释:

  • str1: 用户提供的字符串数据
  • str2: 用户提供的字符串数据

返回值解释:

  • 如果两个字符串前一致,返回 0,否则返回值非 0

2.4 strstr 和 strchr

char *strchr(const char *str, int c)

函数功能: 在当前 str 字符串中,找到目标字符 c ,如果存在,返回 c 第一次出现的地址,如果不存在返回 NULL

tips: 参数 c 是一个 int 数据类型,但是实际上提供给函数使用的有且只有整个 int 内存低 8 位,相当于有且只有一个字节数据有效

参数解释:

  • str:目标搜索字符数据的字符串数据,可以是字符串常量也可以是满足字符串要求的字符数组
  • c: 目标搜索的字符数据

返回值解释:

  • 如果找到目标字符,返回字符在当前字符串中的地址,如果未找到返回 NULL
char *strstr(const char *haystack, const char *needle)

函数功能: 在当前 haystack 字符串中,找到目标字符串 needle ,如果存在,返回 needle 第一次出现的地址,如果不存在返回 NULL

参数解释:

  • str:目标搜索字符数据的字符串数据,可以是字符串常量也可以是满足字符串要求的字符数组
  • c: 目标搜索的字符数据

返回值解释:

  • 如果找到目标字符,返回字符在当前字符串中的地址,如果未找到返回 NULL
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
char *strchr(const char *str, int c)
char *strstr(const char *haystack, const char *needle)
*/

int main(int argc, char const *argv[])
{
    char *str1 = "ABCDEFG";

    if (strchr(str1, 'a'))
    {
        printf("目标字符存在!\n");
    }
    else
    {
        printf("Source Not Found! 404!\n");
    }

    if (strstr(str1, "CB"))
    {
        printf("目标字符串存在!\n");
    }
    else
    {
        printf("Source Not Found! 404!\n");
    }

    /*
    "" C/C++ 中的空字符串,当前字符串中有且只有一个 '\0'
    */
    char arr[10] = "";

    strcpy(arr, strchr(str1, 'C'));
    printf("arr = %s\n", arr);

    return 0;
}

2.5 自学函数

菜鸟教程-string.h

char *strrchr(const char *str, int c);
char *strtok(char *str, const char *delim); // VIP 函数

3. 内存操作相关函数

以下函数是在 <string.h> 头文件中,针对于内存进行操作的函数,关注点是内存中的二进制数据情况,不需要考虑外部的数据类型。所有函数相关操作都是以字节为单位。

3.1 memchr

void *memchr(const void *p, int b, size_t n)

函数功能: 在 p 指向内存空间中,前 n 个字节搜索指定字节 b 对应数据是否存在,如果存在,返回所在内存地址位置,如果不存在,返回 NULL

参数解释:

  • p: 目标内存空间首地址,可以是静态内存地址,也可以是动态申请的内存地址
  • b: 参数类型为 int 类型,实际提供给当前函数的有效数据是当前 int 类型的低 8 位,一个字节数据。
  • n: 表示搜索范围的内存字节数

返回值解释:

  • 如果目标内存数据存在,返回对应地址,否则返回 NULL

01-memchr操作.png

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
当前操作考虑的内容是内存中的数据情况,不需要考虑内存二进制对外的
数据类型和数据形式。
参数 int b 在函数中要求的参数为 int 类型,但是在函数实际运行过程
中,有效数据仅是当前 int 类型数据的低 8 位参与代码运行。
例如:
    b 对应的实际参数为 321 函数实际解析参与代码运行的数据为 65
void *memchr(const void *p, int b, size_t n)
*/

int main(int argc, char const *argv[])
{
    int arr[5] = {321, 833, 1857, 3905, 8001};

    if (memchr(arr, 65, 20)) //
    {
        printf("目标存在!\n");
    }
    else
    {
        printf("Source Not Found!\n");
    }

    if (memchr(arr, 15, 20)) //
    {
        printf("目标存在!\n");
    }
    else
    {
        printf("Source Not Found!\n");
    }

    printf("memchr(arr, 65, 20) : %p\n", memchr(arr, 65, 20));   // 0x7fff3a17d3b0
    printf("memchr(arr, 256, 20) : %p\n", memchr(arr, 256, 20)); // 0x7fff3a17d3b2
    printf("memchr(arr, 15, 20) : %p\n", memchr(arr, 15, 20));   // 0x7fff3a17d3bd

    return 0;
}

3.2 memcmp

int memcmp(const void *p1, const void *p2, size_t n)

函数功能: 比较参数 p1 和 p2 指向内存空间中,前 n 个字节内容是否一致。如果一致返回 0,如果不一致返回其他

参数解释:

  • p1: 用户提供进行比较操作的内存空间首地址
  • p2: 用户提供进行比较操作的内存空间首地址
  • n: 比较限制的前 n 个字节

返回值解释:

  • 如果两个内存对应的数据完全一致,返回 0,否则返回其他整数
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
int memcmp(const void *p1, const void *p2, size_t n);
*/

int main(int argc, char const *argv[])
{
    int num = 1145258561;

    /*
    当前 num 是一个 int 类型,对应内存为
        0100 0100
        0100 0011
        0100 0010
        0100 0001

    字符串 "ABCD" 二进制内容为
        0000 0000 ==> '\0'
        0100 0100 ==> 'D'
        0100 0011 ==> 'C'
        0100 0010 ==> 'B'
        0100 0001 ==> 'A'

    将 num 和 字符串 "ABCD" 对应内存的前 4 个字节进行比较
        1145258561       "ABCD"
        0100 0100    0100 0100 ==> 'D'
        0100 0011    0100 0011 ==> 'C'
        0100 0010    0100 0010 ==> 'B'
        0100 0001    0100 0001 ==> 'A'
    */
    int ret = memcmp(&num, "ABCD", 4);
    printf("ret = %d\n", ret); // 0

    return 0;
}

3.3 memcpy 和 memmove

void *memcpy(void *dest, const void *src, size_t n)
void *memmove(void *dest, const void *src, size_t n)

函数功能: 以上两个函数是将 src 对应内存中的 n 个字节数据,拷贝到 dest 对应内存,要求 dest 对应内存必须具备足够的数据空间

tips : memcpy 性能好,但是安全性较低,memmove 性能较低,安全性好,如果涉及到【同源地址】拷贝操作,请使用 memmove

tips: 【同源地址】dest 和 src 来自于同一块连续内存空间,例如同一个数组 &arr[2] &arr[5],同一个动态申请内存空间

参数解释:

  • dest : 目标存储数据的内存空间首地址
  • src:提供拷贝源数据的首地址
  • n: 拷贝目标对应的字节个数

返回值解释:

  • 返回值内容是当前参数 dest 地址。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
void *memcpy(void *dest, const void *src, size_t n)
void *memmove(void *dest, const void *src, size_t n)
*/

int main(int argc, char const *argv[])
{
    char arr[10] = "ABCDEFG12";

    printf("arr = %s\n", arr);

    memmove(&arr[3], &arr[1], 5);
    printf("arr = %s\n", arr);

    memcpy(&arr[3], &arr[1], 5);
    printf("arr = %s\n", arr);

    return 0;
}

3.4 memset

void *memset(void *p, int b, size_t n)

函数功能: 将用户提供的指针 p 对应内存的 n 个字节,全部赋值为 b

参数解释:

  • p: 用户提供内存的空间首地址
  • b: 参数类型为 int 类型,实际提供给当前函数的有效数据是当前 int 类型的低 8 位,一个字节数据。
  • n 覆盖操作对应的字节个数

返回值解释:

  • 返回值内容是当前参数 p 地址。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
void *memset(void *p, int b, size_t n)
*/
int main(int argc, char const *argv[])
{
    char arr[10];

    memset(arr, 321, 10);

    for (size_t i = 0; i < 10; i++)
    {
        printf("%c\n", arr[i]);
    }
    printf("=------------------------------\n");

    int *p1 = (int *)malloc(10 * sizeof(int));
    memset(p1, 65, sizeof(int) * 10);
    // memset(p1, 0, sizeof(int) * 10); 正常操作!

    for (size_t i = 0; i < 10; i++)
    {
        printf("%d\n", p1[i]);
    }

    free(p1);

    return 0;
}
  • ssacnf
  • sprintf