[C高手编程] 字符串处理:长度、危险操作、格式化与字符串化

在这里插入图片描述

💖💖⚡️⚡️专栏:C高手编程-面试宝典/技术手册/高手进阶⚡️⚡️💖💖
「C高手编程」专栏融合了作者十多年的C语言开发经验,汇集了从基础到进阶的关键知识点,是不可多得的知识宝典。如果你是即将毕业的学生,面临C语言的求职面试,本专栏将帮助你扎实地掌握核心概念,轻松应对笔试与面试;如果你已有两三年的工作经验,专栏中的内容将补充你在实践中可能忽略的新技术和技巧;而对于资深的C语言程序员,这里也将是一本实用的技术备查手册,提供全面的知识回顾与更新。无论处在哪个阶段,「C高手编程」都能助你一臂之力,成为C语言领域的行家里手。

概述

本章深入探讨C语言中的字符串处理技术,包括字符串的表示、长度测量、字符串函数的使用、字符串危险操作、字符串格式化以及字符串化操作符。这些概念对于理解和编写高效安全的C语言程序至关重要。通过本章的学习,读者将能够理解这些概念的工作原理,并能在实际编程中正确地运用它们。

1. 字符串基础

1.1 定义

  • 定义:字符串是由字符组成的序列,通常以空字符\0结尾。

  • 示例代码:

    char str[] = "Hello, World!";
    

    详细说明

    • str是一个包含13个字符的数组,最后一个字符是空字符\0

1.2 字符串字面量

  • 定义:字符串字面量是直接在源代码中定义的字符串。

  • 示例代码:

    char *greeting = "Hello, World!";
    

    详细说明

    • greeting指向一个只读的字符串字面量。
    • 字符串字面量在程序运行期间不能被修改。

1.3 字符串数组

  • 定义:字符串也可以存储在字符数组中。

  • 示例代码:

    char greeting[14];
    strcpy(greeting, "Hello, World!");
    

    详细说明

    • greeting是一个包含14个字符的数组,其中13个字符用于存储字符串,第14个字符用于存储结束标志\0
    • 使用strcpy函数将字符串复制到greeting数组中。
    • 字符数组可以被修改。

1.4 字符串长度

  • 定义:字符串长度是指从第一个字符到最后一个非空字符的字符数量。

  • 示例代码:

    char str[] = "Hello, World!";
    size_t len = strlen(str); // len will be 13
    

    详细说明

    • strlen函数返回从第一个字符到最后一个非空字符的字符数量,不包括结束标志\0
    • strlen函数遍历整个字符串直到遇到结束标志\0

1.5 字符串比较

  • 定义:使用strcmp函数比较两个字符串。

  • 示例代码:

    char str1[] = "Hello";
    char str2[] = "hello";
    int result = strcmp(str1, str2); // result will be non-zero
    

    详细说明

    • strcmp函数按字典序比较两个字符串,如果两个字符串相等则返回0,如果不相等则返回非零值。
    • strcmp函数逐字符比较两个字符串,直到遇到结束标志\0或发现不同字符为止。

在这里插入图片描述

2. 字符串长度与sizeof

2.1 sizeof运算符

  • 定义sizeof运算符返回对象或类型的字节大小。

  • 示例代码:

    char str[] = "Hello, World!";
    size_t size = sizeof(str); // size will be 14
    

    详细说明

    • sizeof(str)返回整个数组的大小,包括字符串本身和结束标志\0
    • sizeof运算符在编译时计算对象或类型的大小。

2.2 sizeof与strlen的区别

  • 定义sizeof返回的是数组或对象的大小,而strlen返回的是字符串的实际长度。

  • 示例代码:

    char str[] = "Hello, World!";
    size_t size = sizeof(str); // size will be 14
    size_t len = strlen(str);  // len will be 13
    

    详细说明

    • sizeof(str)返回的是整个数组的大小,即14个字节。
    • strlen(str)返回的是字符串的实际长度,即13个字符。
    • strlen函数遍历整个字符串直到遇到结束标志\0,而sizeof运算符在编译时计算数组的大小。

2.3 常见陷阱与注意事项

  • 定义:确保正确理解和使用sizeofstrlen

  • 解决方案:正确区分sizeofstrlen的用途。

    详细说明

    • sizeof返回的是数组或对象的大小,包括结束标志\0
    • strlen返回的是字符串的实际长度,不包括结束标志\0
    • 使用sizeof时需要注意它返回的是整个数组的大小,即使数组中只有一部分被使用。
    • 在处理动态分配的字符串时,使用strlen而不是sizeof来获取字符串的实际长度。

在这里插入图片描述

3. 字符串处理函数

3.1 基础函数

3.1.1 strcpy
  • 定义strcpy函数用于复制一个字符串到另一个字符串。

  • 示例代码:

    char str1[] = "Hello";
    char str2[6];
    strcpy(str2, str1);
    

    详细说明

    • strcpy函数将str1复制到str2中。
    • 确保目的数组足够大,以避免缓冲区溢出。
    • strcpy函数复制源字符串直到遇到结束标志\0
3.1.2 strcat
  • 定义strcat函数用于连接两个字符串。

  • 示例代码:

    char str1[] = "Hello, ";
    char str2[] = "World!";
    strcat(str1, str2);
    

    详细说明

    • strcat函数将str2追加到str1的末尾。
    • 确保目的字符串有足够的空间来存储附加的字符串。
    • strcat函数从目的字符串的当前位置开始追加源字符串,直到遇到结束标志\0
3.1.3 strchr
  • 定义strchr函数用于在字符串中查找指定字符的位置。

  • 示例代码:

    char str[] = "Hello, World!";
    char *pos = strchr(str, ',');
    

    详细说明

    • strchr函数返回第一次出现指定字符的位置。
    • 如果没有找到指定字符,则返回NULL
    • strchr函数遍历整个字符串直到遇到结束标志\0

3.2 高级函数

3.2.1 strstr
  • 定义strstr函数用于在字符串中查找子字符串的位置。

  • 示例代码:

    char str[] = "Hello, World!";
    char *pos = strstr(str, "World");
    

    详细说明

    • strstr函数返回子字符串“World”在主字符串中的位置。
    • 如果没有找到子字符串,则返回NULL
    • strstr函数遍历整个字符串直到遇到结束标志\0
3.2.2 strtok
  • 定义strtok函数用于将字符串分割成令牌。

  • 示例代码:

    char str[] = "one,two,three,four,five";
    char *token = strtok(str, ",");
    while (token != NULL) {
        printf("%s\n", token);
        token = strtok(NULL, ",");
    }
    

    详细说明

    • strtok函数根据分隔符,将字符串分割成令牌。
    • 第一次调用时传入原始字符串和分隔符,之后的调用只需传入NULL即可继续分割剩余的部分。
    • strtok函数遍历整个字符串直到遇到结束标志\0

3.3 示例代码

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

int main() {
    char str1[] = "Hello, ";
    char str2[] = "World!";
    char str3[12];

    strcpy(str3, str1);
    strcat(str3, str2);

    printf("str3: %s\n", str3);

    char *pos = strchr(str3, ',');
    if (pos != NULL) {
        printf("Found comma at position: %ld\n", pos - str3);
    }

    char *sub = strstr(str3, "World");
    if (sub != NULL) {
        printf("Found 'World' at position: %ld\n", sub - str3);
    }

    return 0;
}

详细说明

  • 输出结果为str3: Hello, World!
  • 输出结果还包括逗号和子字符串“World”的位置。
  • strchr函数返回逗号的位置。
  • strstr函数返回子字符串“World”的位置。

在这里插入图片描述

4. 字符串危险操作

4.1 缓冲区溢出

  • 定义:缓冲区溢出是指向缓冲区写入超出其边界的数据。

  • 示例代码:

    char str1[10];
    strcpy(str1, "This is a very long string"); // Potential buffer overflow
    

    详细说明

    • 如果目标缓冲区的大小不足以容纳源字符串,则可能发生缓冲区溢出。
    • 缓冲区溢出可能导致程序崩溃或安全漏洞。

4.2 解决方案

  • 定义:使用安全的字符串处理函数。

  • 解决方案:使用strncpy代替strcpy,并检查是否达到目的缓冲区的末尾。

    详细说明

    • strncpy函数允许指定复制的最大字符数。
    • 使用strncpy时,需要检查是否达到目的缓冲区的末尾,并手动添加结束标志\0
    • strncpy函数复制源字符串直到达到指定的最大字符数或遇到结束标志\0

4.3 示例代码

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

int main() {
    char str1[10];
    const char *str2 = "This is a very long string";

    strncpy(str1, str2, sizeof(str1) - 1);
    str1[sizeof(str1) - 1] = '\0'; // Ensure null termination

    printf("str1: %s\n", str1);

    return 0;
}

详细说明

  • strncpy函数将最多9个字符从str2复制到str1中。
  • 手动添加结束标志\0确保字符串正确终止。
  • strncpy函数复制源字符串直到达到指定的最大字符数或遇到结束标志\0

4.4 常见陷阱与注意事项

  • 定义:确保正确理解和使用字符串处理函数。

  • 解决方案:使用安全的字符串处理函数,并检查边界条件。

    详细说明

    • 使用strncpy而不是strcpy来避免缓冲区溢出。
    • 使用strncat而不是strcat来避免缓冲区溢出。
    • 总是确保目的缓冲区有足够的空间来存储源字符串和结束标志\0
    • 避免使用未初始化的指针或数组。
    • 使用strlen来检查字符串的实际长度,以确保不会发生缓冲区溢出。

在这里插入图片描述

5. 字符串格式化

5.1 基本概念

  • 定义:字符串格式化用于生成格式化的字符串。

  • 示例代码:

    int num = 10;
    char str[20];
    sprintf(str, "Number: %d", num);
    

    详细说明

    • sprintf函数将格式化的字符串写入str数组中。
    • 确保目的数组足够大,以避免缓冲区溢出。
    • sprintf函数将格式化的字符串写入目的数组,直到遇到结束标志\0

5.2 格式化指令

  • 定义:格式化指令用于指定输出的格式。

  • 示例代码:

    double pi = 3.14159265;
    char str[50];
    snprintf(str, sizeof(str), "Pi: %.2f", pi);
    

    详细说明

    • snprintf函数将格式化的字符串写入str数组中,最多写入sizeof(str)个字符。
    • 格式化指令%.2f指定输出的浮点数保留两位小数。
    • snprintf函数提供了一个安全的版本,可以避免缓冲区溢出。
    • snprintf函数将格式化的字符串写入目的数组,直到达到指定的最大字符数或遇到结束标志\0

5.3 示例代码

#include <stdio.h>

int main() {
    int num = 10;
    double pi = 3.14159265;
    char str[50];

    snprintf(str, sizeof(str), "Number: %d, Pi: %.2f", num, pi);

    printf("%s\n", str);

    return 0;
}

详细说明

  • 输出结果为Number: 10, Pi: 3.14
  • snprintf函数将格式化的字符串写入目的数组,直到达到指定的最大字符数或遇到结束标志\0

5.4 常见陷阱与注意事项

  • 定义:确保正确理解和使用字符串格式化。

  • 解决方案:使用安全的格式化函数,并检查边界条件。

    详细说明

    • 使用snprintf而不是sprintf来避免缓冲区溢出。
    • 总是确保目的缓冲区有足够的空间来存储格式化的字符串。
    • 避免使用无效的格式化指令。
    • 使用snprintf时,确保指定的目的数组大小足够大。

在这里插入图片描述

6. 字符串化操作符

6.1 定义

  • 定义:字符串化操作符#用于将宏名转换为其字符串形式。

  • 示例代码:

    #define NAME "John Doe"
    char *str = NAME;
    char *str2 = #NAME;
    

    详细说明

    • str指向宏NAME定义的字符串。
    • str2包含宏名NAME的字符串形式。
    • 字符串化操作符仅在预处理阶段有效。

6.2 示例代码

#include <stdio.h>

#define NAME "John Doe"

int main() {
    char *str = NAME;
    char *str2 = #NAME;

    printf("String value: %s\n", str);
    printf("String name: %s\n", str2);

    return 0;
}

详细说明

  • 输出结果为String value: John DoeString name: NAME
  • 字符串化操作符将宏名转换为字符串。

6.3 常见陷阱与注意事项

  • 定义:确保正确理解和使用字符串化操作符。

  • 解决方案:正确使用字符串化操作符。

    详细说明

    • 字符串化操作符仅在宏展开时生效。
    • 不要在运行时使用字符串化操作符。
    • 使用字符串化操作符时,确保宏名正确无误。
    • 字符串化操作符仅在预处理阶段有效。

7. 综合使用

在实际编程中,字符串处理函数、字符串格式化以及字符串化操作符常常结合使用,以达到特定的效果。

7.1 复合表达式

  • 定义:通过结合使用上述概念,可以构建复杂的字符串处理逻辑。

  • 示例代码:

    char str[] = "Hello, World!";
    char *pos = strchr(str, ',');
    if (pos != NULL) {
        *pos = '\0'; // Remove the comma and everything after it
    }
    printf("Modified string: %s\n", str);
    

    详细说明

    • 如果字符串中含有逗号,则去除逗号及其后面的所有字符。
    • 使用strchr函数查找逗号的位置。
    • 使用指针操作去除逗号及其后面的所有字符。

7.2 示例代码

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

int main() {
    char str[] = "Hello, World!";
    char *pos = strchr(str, ',');

    if (pos != NULL) {
        *pos = '\0'; // Remove the comma and everything after it
    }

    printf("Modified string: %s\n", str);

    return 0;
}

详细说明

  • 输出结果为Modified string: Hello
  • 使用strchr函数查找逗号的位置。
  • 使用指针操作去除逗号及其后面的所有字符。

7.3 常见陷阱与注意事项

  • 定义:确保正确理解和使用这些概念的综合应用。

  • 解决方案:仔细检查表达式的顺序和预期行为。

    详细说明

    • 当使用字符串处理函数、字符串格式化以及字符串化操作符时,要特别注意表达式的顺序和预期行为。
    • 使用这些概念时,需要确保代码的逻辑正确无误。
    • 在复杂的表达式中,可能需要使用圆括号来明确优先级。
    • 在使用指针操作时,确保指针指向有效的内存区域。

7.4 实际应用

  • 定义:综合使用这些概念可以构建更复杂的字符串处理逻辑。

  • 示例代码:

    char str[] = "Hello, World!";
    char *pos = strchr(str, ',');
    if (pos != NULL) {
        *pos = '\0'; // Remove the comma and everything after it
    }
    printf("Modified string: %s\n", str);
    

    详细说明

    • 如果字符串中含有逗号,则去除逗号及其后面的所有字符。
    • 使用strchr函数查找逗号的位置。
    • 使用指针操作去除逗号及其后面的所有字符。

7.5 性能考量与优化技巧

  • 定义:综合使用这些概念可以构建更复杂的字符串处理逻辑。

  • 理由:使用这些概念可以帮助解决字符串处理问题,并可以构建复杂的字符串处理逻辑。

    详细说明

    • 当使用字符串处理函数、字符串格式化以及字符串化操作符时,要特别注意表达式的顺序和预期行为。
    • 使用这些概念时,需要确保代码的逻辑正确无误。
    • 在复杂的表达式中,可能需要使用圆括号来明确优先级。
    • 优化技巧包括避免不必要的字符串复制和确保使用最合适的字符串处理函数。
    • 使用strncpystrncat代替strcpystrcat以避免潜在的缓冲区溢出问题。

8. 总结

通过本章的学习,我们深入了解了C语言中的字符串处理技术,包括字符串的表示、长度测量、字符串函数的使用、字符串危险操作、字符串格式化以及字符串化操作符。我们探讨了这些概念的基本概念、使用方法以及注意事项,并提供了详细的示例代码。此外,我们还讨论了如何避免常见的陷阱和危险操作,确保代码的安全性和效率。

  • 字符串基础:字符串的表示和基本操作。
  • 字符串长度与sizeofstrlensizeof的区别。
  • 字符串处理函数:常用的字符串处理函数。
  • 字符串危险操作:缓冲区溢出和其他常见陷阱。
  • 字符串格式化:格式化字符串的方法。
  • 字符串化操作符:宏名的字符串化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值