4. 字符、字符串

4.1 字符

1. 基本概念

  • 字符和字符串是C 语言中常用的数据类型
  • 字符:一个单个的字母、数字、标点符号或者其他可打印的符号,使用单引号' ' 表示,例如:‘A’、‘a’、'-'等;
  • C 语言使用 ASCII 码表示字符,每个字符都有一个整数值和一个字符表示;

char类型用来存储字符,但是其本质上char存储的是字符的ASCII 码值,是整数;大小为1字节;

2. 字符变量和字符常量

​ 声明字符变量的方式和其他类型变量声明方式相同,如声明两个字符变量c1c2

char c1;
char c2;

​ 给变量赋值:

c1 = 'A';
c2 = 'a';
  • C语言中,用单引号''括起来的单个字符称为字符常量(character constant),编译器在读到c1 = 'A';时,会自动将其转换为对应的ASCII 码值;
  • 既然字符是可以用数值表示,那么下列与上述等价,但是一般不这么写;
c1 = 65;
c2 = 97;

4.2 字符串

1. 基本概念

  • 字符串(string)是用来存储一个或多个字符的序列,字符串使用双引号 " " 表示,C语言并没有专门存储字符串的变量类型;
  • 字符串被存在一个char类型的数组中,或者说是字符数组,最后一个字符是ASCII字符NUL(‘\0’)
  • 字符串的最后一个字符必须是 '\0',表示字符串的结尾,非打印字符。

2. 表示和使用字符串

C语言常用字符数组或者字符指针使用字符串;

① 字符数组(Character Array)
  1. 使用字符集合初始化

    char str1[5] = {'h','e','l','l','e'};    //全部赋值
    char str2[5] = {'h','e','l'};            //部分元素赋值
    char str3[] = {'h','e','l','l','e'};    //省略数组长度赋值
    

注意:

上述只有str2是字符串,若字符数组内存中没有’\0’就不是字符串,以字符串形式出现的赋值,编译器都会为该字符串自动添加一个\0作为结束符,

  1. str1不是字符串;
  2. str2未初始化的内存,自动填充'\0',得到{'h','e','l','\0','\0'},因此是字符串;
  3. str3未指定长度,编译器自动感觉数组内容确定长度,但是不会自动填充'\0',不是字符串;

总结:用字符集合的方式(不常用)赋值时,需要在末尾补充’\0',否则不是字符串;

  1. 字符串整体赋值
char str1[5] = "hello";			
char str2[3] = "hel";            
char str3[5] = "hell";    
char str4[] = "helle";    

printf("%s\n", str1);
printf("%s\n", str2);
printf("%s\n", str3);
printf("%s\n", str4);

注意:

  1. str1str2均是错误,超出了数组的长度,越界了!
  2. str3编译器会自动填充'\0',因此不会产生越界;
  3. 推荐使用str3方式;
//输出结果
hello烫烫烫泰p?#?
hel烫烫烫烫蘦ello烫烫烫泰p?#?
hell
hello
  1. 易错点

    • 字符数组不能简单的再次赋值,注意字符数组名不是简单的指向数组首地址的指针常量

      char str4[] = "helle";
      str4 = "world"; //错误!
      
② 字符指针(Character Pointer)
char *str = "hello";
//或
char str[] = "hello";
char *pstr = str;

注意:

  • 该形式是自动加上了字符串结束符,如下:

  • 字符指针可以用字符串常量初始化和赋值,也只能用字符串常量初始化和赋值;

  • 字符指针指向的字符串常量是不可修改的。

4.3 字符数组和字符指针的区别

  • 字符串用字符数组来存储,也可以用数组地址取址赋值给字符型指针(直接赋值也行);

  • 主要区别如:内存分配、初始化和赋值方式、使用场景

1. 初始化和赋值方式

① 可对字符指针变量赋值,但不能对数组名赋值
char str[6]="hello";
char* p="hello";

/*等价于
char *p;
p="hello";
*/

//不可使用数组名赋值
str[6] = "world";
② 赋值方式
  1. 使用数组定义的字符串只能通过为数组中的元素逐一赋值或通过调用复制函数的方式来赋值;
  2. 使用指针定义的字符串还可以实现直接赋值(指向已存在的数组或者动态内存进行赋值);
char str_array[6];
str_array[0] = 'h';
str_array[1] = 'e';
str_array[3] = 'l';
str_array[4] = 'l';
str_array[5] = 'o';
//或者
char str_array[6];
strcpy(str_array, "hello");


char str[] = "hello";
char *str_pointer = str;
char *str_pointer = malloc(10 * sizeof(char));
strcpy(str_pointer, "hello");

2. 内存分配

① 字符数组的栈区分配内存
  • 栈上分配内存:字符数组的内存通常在函数的栈帧上分配。栈内存是一种临时的、自动管理的内存,它在函数执行完毕后会被自动释放。
  • 拷贝副本:在初始化字符数组的时候,将静态区的字符串拷贝到栈区数组,程序执行完毕自动销毁;
  • 长度固定:在定义字符数组时,需要指定数组的大小。这个大小就决定了在栈上分配的内存大小。
② 字符指针的内存分配
  1. 指向栈上数组:字符指针可以指向栈上的字符数组,这并不改变内存分配的基本原理。
char str_stack[20]; // 栈上的字符数组
char *str_pointer = str_stack; // 字符指针指向栈上的字符数组
  1. 指向堆上动态分配的数组:字符指针也可以指向通过 malloccalloc 等函数在堆上动态分配的数组。在这种情况下,内存的生命周期由程序员手动管理,需要显式调用 free 来释放内存。
char *str_dynamic = (char *)malloc(20 * sizeof(char)); // 堆上动态分配的字符数组

存储关系如下:

3. 使用特点

  • 字符数组:通过数组名访问,可以修改数组中的内容,但长度是固定的。
  • 字符指针:可以通过指针访问字符串,可以指向字符串常量、字符数组、或者动态分配的字符串,具有较大的灵活性。

4. 使用建议

  1. 生命周期和自动管理:字符数组在栈上分配,生命周期与函数调用相关,由系统自动管理。字符指针可以指向栈上数组,也可以指向堆上动态分配的数组,生命周期由程序员管理。
  2. 长度固定性:字符数组的长度在编译时就需要确定,因为它在栈上分配固定大小的内存。字符指针更加灵活,可以指向不同长度的字符串,包括动态分配的字符串。
  3. 堆上分配的动态性:字符指针的一大优势是可以方便地指向堆上动态分配的数组,这使得它更适合处理不确定长度的字符串。

总体而言,如果字符串的长度是已知的且不会变化,字符数组是一个简单而高效的选择。

如果字符串长度不确定或者需要在运行时动态分配内存,字符指针更加灵活,选择哪种方式取决于程序的需求和设计。

4.4 字符串操作函数

​ C语言中,给我们提供了许多内置的库函数,使用时候需要包括其头文件,下列函数需要包含#include<string.h>,常见的字符串处理函数如下:

​ 更多函数用法参考:cplusplus.com

1. strlen() - 计算字符串长度

  • 函数原型: size_t strlen(const char *str);

  • 参数:str - 输入要计算长度的字符串。

  • 返回值: 字符串的字符数(即长度)。

  • 用法示例:

    const char *message = "Hello, World!";
    size_t length = strlen(message);
    printf("Length of the string: %zu\n", length);
    //注意
    
  • 注意事项:注意字符串的长度不包括null终止符。

2. strcpy() - 复制字符串

  • 函数原型: char *strcpy(char *dest, const char *src);

  • 参数: dest - 目标字符串,src - 源字符串。

  • 返回值: 目标字符串的指针。

  • 用法示例:

    char destination[20];
    const char *source = "Copy me!";
    strcpy(destination, source);
    printf("Copied string: %s\n", destination);
    
  • 注意事项: 确保目标字符串有足够的空间来存储源字符串,即目标缓冲区足够大。

3. strncpy() - 复制部分字符串

  • 函数原型: char *strncpy(char *dest, const char *src, size_t n);

  • 参数: dest - 目标字符串,src - 源字符串,n - 复制的最大字符数。

  • 返回值: 返回目标字符串的指针。

  • 用法示例:

    char destination[10];
    const char *source = "Copy me!";
    strncpy(destination, source, 5);
    destination[5] = '\0';  // 手动添加 null 终止符
    printf("Copied string: %s\n", destination);
    
  • 注意事项: 指定复制的最大字符数,手动添加 null 终止符\0

4. strcat() - 连接字符串

  • 函数原型: char *strcat(char *dest, const char *src);

  • 参数: dest - 目标字符串,src - 要追加的字符串。

  • 返回值: 目标字符串的指针。

  • 用法示例:

    char destination[20] = "Hello, ";
    const char *source = "World!";
    strcat(destination, source);
    printf("Concatenated string: %s\n", destination);
    
  • 注意事项: 确保目标缓冲区足够大

5. strncat() - 拼接部分字符串

  • 函数原型: char *strncat(char *dest, const char *src, size_t n);

  • 参数: dest - 目标字符串,src - 要追加的字符串,n - 追加的最大字符数。

  • 返回值: 目标字符串的指针。

  • 用法示例:

    char destination[20] = "Hello, ";
    const char *source = "World!";
    strncat(destination, source, 3);
    destination[10] = '\0';  // 手动添加 null 终止符
    printf("Concatenated string: %s\n", destination);
    
  • 注意事项: 指定追加的最大字符数,手动添加 null 终止符\0

6. strcmp() - 比较字符串

  • 函数原型: int strcmp(const char *str1, const char *str2);

  • 参数: 要比较的 str1 - 第一个字符串,str2 - 第二个字符串。。

  • 返回值: 负数(str1 小于 str2),零(str1 等于 str2),正数(str1 大于 str2)。

  • 用法示例:

    const char *str1 = "apple";
    const char *str2 = "banana";
    int result = strcmp(str1, str2);
    printf("Comparison result: %d\n", result);
    
  • 注意事项: 区分大小写,不同编码下结果可能不同。

7. strncmp() - 比较部分字符串

  • 函数原型: int strncmp(const char *str1, const char *str2, size_t n);

  • 参数: str1 - 第一个字符串,str2 - 第二个字符串,n - 比较的最大字符数。

  • 返回值: 负数(str1 小于 str2),零(str1 等于 str2),正数(str1 大于 str2)。

  • 用法示例:

    const char *str1 = "apple";
    const char *str2 = "banana";
    int result = strncmp(str1, str2, 3);
    printf("Comparison result: %d\n", result);
    
  • 注意事项: 指定比较的最大字符数,区分大小写。。

8. strstr() - 在字符串中查找子串

  • 函数原型: char *strstr(const char *haystack, const char *needle);

  • 参数: haystack - 要搜索的字符串,needle - 要查找的子串。

  • 返回值: 指向第一次出现的子字符串的指针,或者 NULL。

  • 用法示例:

    const char *sentence = "This is a sample sentence.";
    const char *word = "sample";
    char *result = strstr(sentence, word);
    if (result != NULL) 
    {
        printf("Substring found: %s\n", result);
    } 
    else 
    {
        printf("Substring not found.\n");
    }
    

9. strchr() - 在字符串中查找字符

  • 函数原型: char *strchr(const char *str, int c);

  • 参数: str - 要搜索的字符串,c - 要查找的字符。

  • 返回值: 返回指向第一次出现字符的指针,或者 NULL。

  • 用法示例:

    const char *sentence = "This is a sample sentence.";
    char character = 'a';
    char *result = strchr(sentence, character);
    if (result != NULL) 
    {
        printf("Character found: %s\n", result);
    } 
    else
    {
        printf("Character not found.\n");
    }
    
  • 注意事项: 区分大小写,返回的是第一次出现的位置。

10. gets() - 字符串输入

  • 作用: 从标准输入获取一行字符串。

  • 函数原型: char *gets(char *str);

  • 参数: str - 接收输入的字符串。

  • 返回值: 指向输入字符串的指针。

  • 用法示例:

    cCopy codechar input[50];
    printf("Enter a string: ");
    gets(input);
    printf("You entered: %s\n", input);
    
  • 注意事项: 不推荐使用,容易导致缓冲区溢出。

11. fgets() - 带长度限制的字符串输入

  • 作用:文件流获取一行字符串,带有长度限制。

  • 函数原型: char *fgets(char *str, int n, FILE *stream);

  • 参数: str - 接收输入的字符串,n - 最大字符数,stream - 文件流。

  • 返回值: 指向输入字符串的指针,或者 NULL(读取失败)。

  • 用法示例:

    char input[50];
    FILE *file = fopen("example.txt", "r");
    if (file != NULL) 
    {
        fgets(input, sizeof(input), file);
        printf("Read from file: %s\n", input);
        fclose(file);
    }
    
  • 注意事项: 控制读取的字符数,避免溢出。

12. puts() - 字符串输出

  • 作用: 将字符串输出到标准输出。

  • 函数原型: int puts(const char *str);

  • 参数: str - 要输出的字符串。

  • 返回值: 非负数(成功),EOF(失败)。

  • 用法示例:

    Copy codeconst char *output = "Hello, World!";
    puts(output);
    
  • 注意事项: 自动在字符串后添加换行符。

13. fputs() - 带文件流的字符串输出

  • 作用: 将字符串输出到文件流。

  • 函数原型: int fputs(const char *str, FILE *stream);

  • 参数: str - 要输出的字符串,stream - 文件流。

  • 返回值: 非负数(成功),EOF(失败)。

  • 用法示例:

    const char *output = "Hello, World!";
    FILE *file = fopen("output.txt", "w");
    if (file != NULL) 
    {
        fputs(output, file);
        fclose(file);
    }
    
  • 注意事项: 不会自动添加换行符。

14. sprintf() - 格式化字符串输出到字符数组

  • 作用: 将格式化的字符串输出到字符数组。

  • 函数原型: int sprintf(char *str, const char *format, ...);

  • 参数: str - 接收格式化字符串的字符数组,format - 格式字符串,… - 可变参数。

  • 返回值: 字符数(成功),负数(失败)。

  • 用法示例:

    char buffer[50];
    int value = 42;
    sprintf(buffer, "The answer is %d.", value);
    printf("%s\n", buffer);
    
  • 注意事项: 控制字符数组大小,避免溢出。

更多推荐