十四、字符串和字符串函数

文章目录

      • 字符串和字符串函数
        • 1. 字符串
          • 1.1 C/C++ 字符串概述
            • 1.1.1 字符串常量
            • 1.1.2 字符数组
          • 1.2 字符串函数
            • 1.2.1 strcpy 和 strncpy
            • 1.2.2 strcat 和 strncat
            • 1.2.3 strcmp 和 strncmp
            • 1.2.4 strchr 和 strrchr
            • 1.2.5 strstr 和 strlen
            • 1.2.6 memory 函数

字符串和字符串函数

1. 字符串
1.1 C/C++ 字符串概述
1.1.1 字符串常量

字符串常量

格式要求:采用英文双引号包含的所有内容,C/C++ 语言规定,自负床常量都存在一个 ‘’ 结尾

在内存的【数据区】,而且字符串本身在程序中是对应当前字符串所在内存的空间首地址,可以采用 char * 存储对应的首地址。

"ABCDEFG" 占用字节为 8 个字节!!!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    printf("字符串占用字节数: %ld
", sizeof("ABCDEFG")); // 8
    /*
    str 可以存储字符串常量在内存【数据区】的空间首地址
    同时也是当前字符串下标为 0 的元素空间首地址
    */
    char *str = "1234567890";
    printf("str : %s
", str); // 1234567890
    printf("%p
", "1234567890"); // 0x400697

    printf("&str[5] : %s
", &str[5]); // 67890

    printf("*str : %c
", *str); // 1
    printf("str[5] : %c
", str[5]); // 6
    printf(""1234567890"[5] : %c
", "1234567890"[5]); // 6

    return 0;
}
1.1.2 字符数组
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    char arr[4] = {'a', 'b', 'c', ''};
    /*
    arr 数组名数据类型为 char * 是当前字符数组空间首地址
    同时也是下标为 0 的元素空间首地址
    可以将一个符合 C/C++ 规范的字符数组当作一个字符串【变量】操作
    C/C++ 规范要求末尾必须有 
    */
    printf("arr = %s
", arr);
    printf("arr = %p
", arr);
    printf("arr[1] = %c
", arr[1]);

    // 在字符数组可以进行数据的更改
    arr[1] = 'G';
    printf("arr = %s
", arr);

    // 段错误!字符串常量无法修改
    char *str = "ABC";
    str[1] = 'G';
    printf("str = %s
", str);

    return 0;
}
1.2 字符串函数

【重要提示】

  • 字符串是一个常量,数据内容无法修改,地址内容无法修改
  • 字符串函数操作请注意【内存空间问题】
  • 字符串函数操作,注意返回值类型和返回值情况
1.2.1 strcpy 和 strncpy
char *strcpy(char *dest, const char *src);
将 src 指向的字符串内容,拷贝到 dest 字符数据空间中
要求:
    1. src 可以是字符串常量,也可以是字符数组
    2. dest 必须是可以存储字符数据的内存空间,一般是字符数组或者动态申请内存字符空间,而且空间要求足够使用
    
char *strncpy(char *dest, const char *src, size_t n);
将 src 指向的字符串内容,拷贝到 dest 字符数据空间中,最多复制 n 个字符。
要求:
	1. src 可以是字符串常量,也可以是字符数组
    2. dest 必须是可以存储字符数据的内存空间,一般是字符数组或者动态申请内存字符空间,而
        且空间要求足够使用。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    /*
    【重要提示】
        - 字符串是一个常量,数据内容无法修改,地址内容无法修改
        - 字符串函数操作请注意【内存空间问题】
        - 字符串函数操作,注意返回值类型和返回值情况
    */
    // strcpy 函数
    char dest[20] = {''}; 
    char *src = "0213456789";

    printf("dest = %s
", dest); // 空
    printf("src = %s
", src); // 0213456789
    printf("---------------------------------
");

    strcpy(dest, src);
    printf("dest = %s
", dest); // 0213456789
    printf("src = %s
", src); // 0213456789
    printf("---------------------------------
");

    // strncpy 函数
    char dest1[20] = {''};
    char *src1 = "12345";
    strncpy(dest1, src1, 20);
    
    printf("dest1 = %s
", dest1); // 12345
    printf("src1 : %s
", src1); // 12345

    return 0;
}
1.2.2 strcat 和 strncat
char *strcat(char *dest, const char *src)
把 src 所指向的字符串追加到 dest 所指向的字符串的结尾
注意:
    1. 末尾标记''
    2. 返回值数据类型为 char *,返回内容是 dest 对应的空间首地址
    3. dest 必须有对应的内存空间

char *strncat(char *dest, const char *scr, size_t n)
把 src 所指向的字符串类型追加到 dest 所指向的字符串的结尾,直到 n 字符长度为止
注意:
    1. 末尾标记''
    2. 返回值数据类型为 char *,返回内容是 dest 对应的空间首地址
    3. dest 必须有对应的内存空间
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    char *src = "ABCD";
    // 定义一个容量为 20 的字符串数组
    char dest[20] = {'E', 'F', 'G', ''};

    // strcat 操作
    char *s1 = strcat(dest, src); // 将字符串 ABCD 复制到字符数组 dest 的末尾
    printf("s1 = %s
", s1); // EFGABCD
    printf("dest = %s
", dest); // EFGABC
    printf("-----------------------------
");

    // strncat 操作
    char *s2 = strncat(dest, src, 3); // 字符串的三个元素复制到字符数组 dest 的末尾
    printf("s2 = %s
", s2); //EFGABCDABC(在 strcat 操作的基础上)
    printf("dest = %s
", dest); // EFGABCDABC

    return 0;
}
1.2.3 strcmp 和 strncmp
int strcmp(const char *str1, const char *str2)
把 str1 所指向的字符串和 str2 所指向的字符串进行比较
返回值
    1. 0 表示 str1 和 str2 两个字符串一致
    2. 1 or -1 表示两个字符串不一致
    
int strncmp(const char *str1, const char *str2, size_t n)
把 str1 所指向的字符串和 str2 所指向的字符串进行比较,最多比较前 n 个字节
返回值
    1. 0 表示 str1 和 str2 两个字符串一致
    2. 1 or -1 表示两个字符串不一致
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    // strcmp 操作
    printf("ret = %d
", strcmp("ABC", "ABC")); // 0
    printf("ret = %d
", strcmp("AbC", "ABC")); // 1
    printf("ret = %d
", strcmp("ABC", "aBc")); // -1
    printf("-------------------------------
");

    /*
    字符串数据通过 == 等值判断,比较的不是【字符串内容】
    是比较两端的地址是否一致,如果是两个内容一致的字符串
    常量比较, == 判断结果为 1(true),如果是两个字符数组
    或者字符串和字符数组内容一致,但是通过 == 等值判断
    结果为 0(false)
    */
    printf("ret : %d
", "ABC" == "ABC"); // 1
    char arr[4] = {'A', 'B', 'C', ''}; 
    printf("ret : %d
", "ABC" == arr); // 0 因为比较的是地址是否一致
    printf("-------------------------------
");

    // strncmp 操作
    printf("ret : %d
", strncmp("ABCDE", "ABCDE11111", 5)); // 0
    printf("ret : %d
", strncmp("ABCDE", "ABCDE11111", 6)); // -1

    return 0;
}
1.2.4 strchr 和 strrchr
char *strchr(const char *str, int c);
在参数 str 所指向的字符串中搜索第一次出现字符 c (一个无符号字符) 的位置
    
char *strrchr(const char *str, int c);
在参数 str 所指向的字符串中搜索最后一次出现字符 c (一个无符号字符) 的位置
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    // strchr 操作,在参数 str 所指向的字符串中搜索
    // 第一次出现字符 '8' 的位置
    char *str = "ABCD8BCD";
    char *p1 = strchr(str, '8');

    printf("p1 : %s
", p1); // 8BCD
    printf("p1 = %p
", p1); // 0x40081c 

    /*
    p1 和 str 都是 char * 指针,存储对应的数据得地址
    因为字符数据在内存中,占用字节数为 1, 两个字符地址相减,
    可以认为是下标位置,要求必须在同一个字符串地址范围以内
    */
    printf("p1 - str : %ld
", p1 - str); // 4
    printf("--------------------------------
");

    int arr[5] = {1, 2, 3, 4, 5};


    /*
    两个同数组中元素取地址相减操作
        CPU 首先计算两个地址直接得字节差,
        根据数据类型相减,最终结果是两个地址得【下标差/坐标差】
    */
    printf("&arr[4] - &arr[1] : %ld
", &arr[4] - &arr[1]); // 3
    
    // strrchr 操作,在参数 str 所指向的字符串中搜索
    // 最后一次出现字符 'C' 的位置
    char *p2 = strrchr(str, 'C');
    printf("p2 = %s
", p2); // CD
    printf("p2 = %p
", p2); // 0x40081e
    return 0;
}
1.2.5 strstr 和 strlen
char *strstr(const char *haystack, const char *needle)
在字符串 haystack 中查找第一次出现字符串 needle (不包含空结束字符) 的位置
    
size_t strlen(const char *str)
计算字符串 str 的长度,直到空结束字符,但不包括空结束字符
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    // strstr 操作 
    // 在字符串 str 中查找第一次出现
    // 字符串 target (不包含空结束字符) 的位置
    char *str = "ABCDEFG";
    char *target = "EFG";

    char *p = strstr(str, target);
    printf("p = %p
", p); // 0x4007a8 系统分配的地址 
    printf("p = %s
", p); // EFG

    // 相当于 (E 的下标位置) - (A 的下标位置)
    printf("p - str = %ld
", p - str); // 4 
    
    // 计算字符串 "ABCD" 的长度
    // 直到空结束字符,但不包括空结束字符
    printf("strlen = %ld
", strlen("ABCD")); // 4 "ABCD" 字符串的长度
    char arr[5] = {'A', 'B', 'C', 'D', ''};
    printf("strlen = %ld
", strlen(arr)); // 4
    return 0;
}
1.2.6 memory 函数

以下函数具备字符串处理功能,更多的使用场景是针对内存数据操作

void *memchr(const void *str, int c, size_t n)
在参数 str 所指向的字符串的前 n 个字节中搜索第一次出现字符 c (一个无符号字符)的位置

int memcmp(const void *str1, const void *str2, size_t n)
把 str1 和 str2 的前 n 个字节进行比较
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct student
{
    int id;
    char name[32];
    int age;
} Student;

int main(int argc, char const *argv[])
{
    /*
    void *memchr(const void *str, int c, size_t n)
    在参数 str 所指向的字符串的前 n 个字节中搜索
    第一次出现字符 c (一个无符号字符) 的位置
    */
    char *str = "ABCDABCD";
    void *p = memchr(str, 'C', 5);
    
    printf("p = %p
", p); // 0x4008e6

    /*
    int memcmp(const void *str1, const void *str2, size_t n)
    把 str1 和 str2 的前 n 个字节进行比较
    0 相同,非 0 不同
    */
    char *str1 = "ABCDE";
    char *str2 = "ABcDE";
    int ret = memcmp(str1, str2, 5);
    printf("ret = %d
", ret); // != 0 为:-32

    printf("---------------------------
");

    // 用 malloc 申请空间
    Student * stu1 = (Student *)malloc(sizeof(Student));
    memset(stu1, 0, sizeof(Student));
    Student * stu2 = (Student *)malloc(sizeof(Student));
    memset(stu2, 0, sizeof(Student));

    stu1->id = 1;
    strcpy(stu1->name, "James");
    stu1->age = 39;

    stu2->id = 1;
    strcpy(stu2->name, "James");
    stu2->age = 39;

    // stu1 和 stu2 的内存内容一样
    ret = memcmp(stu1, stu2, sizeof(Student));
    printf("ret = %d
", ret); // 0 相同为 0 

    free(stu1);
    free(stu2);
    stu1 = NULL;
    stu2= NULL;

    return 0;
}
void *memcpy(void *dest, const void *src, size_t n)
从 src 复制 n 个字符到 dest 中

void *memmove(void *dest, const void *src, size_t n)
另一个用于从 src 复制 n 个字符到 dest 的函数
/*
memcpy 和 memmove 功能 和 strncpy 一致,memmove 可以更好的保护拷贝源数据 src
【推荐】使用 memmove,不会导致存储数据丢失 
*/
void *memset(void *str, int c, size_t n)
复制字符 c (一个无符号字符) 到参数 str 所指向的字符串的前 n 个字符
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

    /*
    memove 操作
    从 src 复制 n 个字符到 dest 中,
    */
    memmove(arr, "123456789", 3);
    // 将前三位覆盖
    printf("arr = %s
", arr); // arr = 123DE
    printf("------------------------
");

    /*
    memcpy 操作
    从 src 复制 n 个字符到 dest 中
    */
    char str[20] = "Hello World";
    memcpy(&str[1], &str[5], 4);
    printf("%s
", str); // 结果:H Wor World
    printf("------------------------
");

    /*
    memset 操作
    复制字符 c (一个无符号字符) 到
    参数 str 所指向的字符串的前 n 个字符
    */
    char str2[20] = "Hello World";
    memset(str2, 'a', 5);
    printf("%s
", str2); // aaaaa World

    return 0;
}