• 【《C Primer Plus》读书笔记】第11章:字符串和字符串函数


    11.1 字符串

    在 C 语言中,字符串实际上是使用空字符 \0 结尾的一维字符数组。因此,\0 是用于标记字符串的结束。

    空字符(Null character)又称结束符,缩写 NUL,是一个数值为 0 的控制字符,\0 是转义字符,意思是告诉编译器,这不是字符 0,而是空字符。

    下面的声明和初始化创建了一个UESTC 字符串。由于在数组的末尾存储了空字符 \0,所以字符数组的大小比单词 UESTC 的字符数多一个。

    char site[6] = {'U', 'E', 'S', 'T', 'C', '\0'};
    
    • 1

    依据数组初始化规则,您可以把上面的语句写成以下语句:

    char site[] = "UESTC";
    
    • 1

    字符串字面量(字符串常量)

    用双引号括起来的内容称为字符串字面量,也叫做字符串常量

    其实,不需要把 null 字符放在字符串常量的末尾。C 编译器会在初始化数组时,自动把 \0 放在字符串的末尾。

    以下是 C/C++ 中定义的字符串的内存表示:

    在这里插入图片描述

    10.2 数组与指针

    我们可以以数组形式声明字符串:

    char ar[] = "I like C++";
    
    • 1

    也可以用指针形式声明字符串:

    const char *p = "I like C++";
    
    • 1

    以上面的声明为例,数组形式在计算机的内存中分配为一个内含11个元素的数组(每个元素对应一个字符,还加上末尾的空字符’\0’),每个元素被初始化为字符串常量对应的字符。

    通常,字符串都作为可执行文件的一部分存储在数据段中,当把程序载入内存时,也载入了程序中的字符串。字符串存储在静态存储区中。但是,程序开始运行时才会为该数组分配内存。此时,才将字符串拷贝到数组中。

    注意,此时字符串有2个副本,一个是在静态内存中的字符串常量,另一个是存储在数组 ar 里的字符串。此后,编译器便把数组名 ar 识别为该数组首元素地址的别名。ar 是地址常量,不能被修改。

    指针形式也使得编译器在计算机的内存中分配为一个内含11个元素的数组来存储字符串。另外,一旦开始执行程序,编译器会为指针变量 p 留出一个存储位置,并把字符串的地址存储在指针变量中。该变量最初指向该字符串的首个字符,但是它的值可以改变。

    字符串常量被视为 const 数据。由于 p 指向这个 const 数据,所以应该把 p 声明为指向 const 数据的指针。这意味着不能用 p 来改变它指向的数据,但 p 本身可以改变。如果把一个字符串常量拷贝给一个数组,就可以随意改变数据,除非数组声明为 const。

    总而言之,初始化数组把静态存储区中的字符串拷贝给数组,而初始化指针只把字符串的地址拷贝给指针。

    实例:

    #include 
    #include 
    #define MSG "I like C++"
    int main(void)
    {
        char ar[] = MSG;
        const char *p = MSG;
        printf("%p\n", "I like C++");
        printf("%p\n", MSG);
        printf("%p\n", ar);
        printf("%p\n", p);
        printf("%p\n", "I like C++");
        system("pause");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    运行结果:

    在这里插入图片描述

    该程序说明,p 和 MSG 的地址相同,而 ar 的地址不同。

    还说明,编译器会把多次使用的相同字符串常量存储在同一处。

    10.3 gets()

    C 库函数 char *gets(char *str) 从标准输入 stdin 读取一行,并把它存储在 str 所指向的字符串中。当读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。然后丢弃换行符,存储其余字符,并在这些字符的末尾添加一个空字符使其成为一个 C 字符串。

    下面是 gets() 函数的声明:

    char *gets(char *str)
    
    • 1

    参数 str 是指向一个字符数组的指针,该数组存储了 C 字符串。

    如果成功,该函数返回 str。如果发生错误或者到达文件末尾时还未读取任何字符,则返回 NULL。

    实例:

    #include 
    #include 
    int main(void)
    {
        char str[50];
    
        printf("请输入一个字符串:");
        gets(str);
        printf("您输入的字符串是:%s\n", str);
    
        system("pause");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    运行结果:

    在这里插入图片描述

    不安全的gets()
    gets() 函数唯一的参数是 str,它无法检查数组是否装得下输入行。如果输入的字符串过长,会导致缓冲区溢出,即多余的字符超出了指定的目标空间。

    10.4 puts()

    C 库函数 int puts(const char *str) 把一个字符串写入到标准输出 stdout,直到空字符,但不包括空字符。

    puts() 会在待输出字符串末尾添加一个换行符。

    下面是 puts() 函数的声明。

    int puts(const char *str)
    
    • 1

    参数 str 是要被写入的 C 字符串。

    如果成功,该函数返回一个非负值为字符串长度(包括末尾的 \0),如果发生错误则返回 EOF。

    实例:

    #include 
    #include 
    #include 
    int main(void)
    {
        char str1[15];
        char str2[15];
    
        strcpy(str1, "RUNOOB1");
        strcpy(str2, "RUNOOB2");
    
        puts(str1);
        puts(str2);
    
        system("pause");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    运行结果:

    在这里插入图片描述

    10.5 fgets()

    C 库函数 char *fgets(char *str, int n, FILE *stream) 从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。

    如果 fgets() 读到了一个换行符,会把它存储在字符串中。这一点与 gets() 不同。

    下面是 fgets() 函数的声明:

    char *fgets(char *str, int n, FILE *stream)
    
    • 1

    参数:

    • str – 这是指向一个字符数组的指针,该数组存储了要读取的字符串。
    • n – 这是要读取的最大字符数(包括最后的空字符)。通常是使用以 str 传递的数组长度。
    • stream – 这是指向 FILE 对象的指针,该 FILE 对象标识了要从中读取字符的流。如果读入从键盘中输入的数据,则以 stdin(标准输入)作为参数,该标识符定义在 stdio.h 中。

    返回值:

    如果成功,该函数返回相同的 str 参数。如果到达文件末尾或者没有读取到任何字符,str 的内容保持不变,并返回一个空指针。

    如果发生错误,返回一个空指针。

    10.6 fputs()

    C 库函数 int fputs(const char *str, FILE *stream) 把字符串写入到指定的流 stream 中,但不包括空字符。

    fputs() 不在字符串末尾添加换行符。这一点与 puts() 不同。

    下面是 fputs() 函数的声明:

    int fputs(const char *str, FILE *stream)
    
    • 1

    参数:

    • str – 这是一个数组,包含了要写入的以空字符终止的字符序列。
    • stream – 这是指向 FILE 对象的指针,该 FILE 对象标识了要被写入字符串的流。

    返回值:
    该函数返回一个非负值,如果发生错误则返回 EOF。

    10.7 gets_s()

    C11 新增的 gets_s() 函数和 fgets() 类似,用一个参数限制读入的字符数。

    与 fgets() 不同的是,gets_s() 只从标准输入里读取数据。如果 gets_s() 读到了一个换行符,会丢弃它而不是存储它。

    如果 gets_s() 读到最大字符数都没有读到换行符,首先会把目标数组的首字符设为空字符,读取并丢弃随后的输入直至遇到换行符或文件结尾,然后返回空指针。

    10.8 s_gets()

    如果fgets()返回NULL,说明读到文件结尾或出现读取错误,s_gets()函数跳过了这个过程。

    效果:如果字符串中出现换行符,就要空字符替换它。

    10.9 字符串函数

    C 中有大量操作字符串的函数。ANSI C 把这些函数的原型放在 string.h 头文件中。

    strlen()

    C 库函数 size_t strlen(const char *str) 计算字符串 str 的长度,直到空结束字符,但不包括空结束字符。

    下面是 strcat() 函数的声明:

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

    下面是 strlen() 函数的声明:

    size_t strlen(const char *str)
    
    • 1

    参数:

    • str – 要计算长度的字符串。

    返回值:
    该函数返回字符串的长度。

    strcat()

    C 库函数 char *strcat(char *dest, const char *src) 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾。

    下面是 strcat() 函数的声明:

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

    参数:

    • dest – 指向目标数组,该数组包含了一个 C 字符串,且足够容纳追加后的字符串。
    • src – 指向要追加的字符串,该字符串不会覆盖目标字符串。

    返回值:
    该函数返回一个指向最终的目标字符串 dest 的指针。

    strncat()

    C 库函数 char *strncat(char *dest, const char *src, size_t n) 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾,直到 n 字符长度为止。

    下面是 strncat() 函数的声明:

    char *strncat(char *dest, const char *src, size_t n)
    
    • 1

    参数:

    • dest – 指向目标数组,该数组包含了一个 C 字符串,且足够容纳追加后的字符串,包括额外的空字符。
    • src – 要追加的字符串。
    • n – 要追加的最大字符数。

    返回值:
    该函数返回一个指向最终的目标字符串 dest 的指针。

    strcmp()

    C 库函数 int strcmp(const char *str1, const char *str2) 把 str1 所指向的字符串和 str2 所指向的字符串进行比较。

    下面是 strcmp() 函数的声明:

    int strcmp(const char *str1, const char *str2)
    
    • 1

    参数:

    • str1 – 要进行比较的第一个字符串。
    • str2 – 要进行比较的第二个字符串。

    该函数返回值如下:

    • 如果返回值小于 0,则表示 str1 小于 str2。
    • 如果返回值大于 0,则表示 str1 大于 str2。
    • 如果返回值等于 0,则表示 str1 等于 str2。

    实际上,strcmp() 的返回值是 str1 和 str2 第一个相异字符之间的差值。但我们一般只关心比较的结果是正、负、还是0。

    strcpy()

    C 库函数 char *strcpy(char *dest, const char *src) 把 src 所指向的字符串复制到 dest。

    需要注意的是如果目标数组 dest 不够大,而源字符串的长度又太长,可能会造成缓冲溢出的情况。

    下面是 strcpy() 函数的声明:

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

    参数:

    • dest – 指向用于存储复制内容的目标数组。
    • src – 要复制的字符串。

    返回值:
    该函数返回一个指向最终的目标字符串 dest 的指针。

    strncpy()

    C 库函数 char *strncpy(char *dest, const char *src, size_t n) 把 src 所指向的字符串复制到 dest,最多复制 n 个字符。当 src 的长度小于 n 时,dest 的剩余部分将用空字节填充。

    下面是 strncpy() 函数的声明:

    char *strncpy(char *dest, const char *src, size_t n)
    
    • 1

    参数:

    • dest – 指向用于存储复制内容的目标数组。
    • src – 要复制的字符串。
    • n – 要从源中复制的字符数。

    返回值:
    该函数返回最终复制的字符串。

    strchr()

    C 库函数 char *strchr(const char *str, int c) 在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置。

    下面是 strchr() 函数的声明:

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

    参数:

    • str – 要被检索的 C 字符串。
    • c – 在 str 中要搜索的字符。

    返回值:
    该函数返回在字符串 str 中第一次出现字符 c 的位置,如果未找到该字符则返回 NULL。

    strstr()

    C 库函数 char *strstr(const char *haystack, const char *needle) 在字符串 haystack 中查找第一次出现字符串 needle 的位置,不包含终止符 ‘\0’。

    下面是 strstr() 函数的声明:

    char *strstr(const char *haystack, const char *needle)
    
    • 1

    参数:

    • haystack – 要被检索的 C 字符串。
    • needle – 在 haystack 字符串内要搜索的小字符串。

    返回值:
    该函数返回在 haystack 中第一次出现 needle 字符串的位置,如果未找到则返回 null。

  • 相关阅读:
    【c++11线程库的使用】
    Web中的Bias(更新中)
    JavaEE-多线程-Volatile关键字
    Centos7构建LNMP平台
    Linux友人帐之日志与备份
    Podman Desktop安装与使用-Windows10
    2023年11月5日网规考试备忘
    2、HTML——标题分组、居中、引用标签、水平线标签下划线标签、删除标签、<font>标签、图像标签
    C++中const和constexpr的多文件链接问题
    Java多线程:BlockingQueue实现原理(Condition原理)
  • 原文地址:https://blog.csdn.net/ProgramNovice/article/details/126917959