MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

C 语言字符串函数深入解析与实践指南

2022-04-113.9k 阅读

C 语言字符串基础回顾

在深入探讨 C 语言字符串函数之前,让我们先简要回顾一下 C 语言中字符串的基本概念。在 C 语言里,字符串实际上是以空字符 '\0' 结尾的字符数组。例如:

char str1[] = "Hello";
char str2[6] = {'H', 'e', 'l', 'l', 'o', '\0'};

这两种方式都定义了一个包含字符串 “Hello” 的字符数组。str1 中,编译器会自动在结尾添加 '\0',而 str2 则显式地包含了结束符。

字符串函数分类

C 语言标准库提供了丰富的字符串处理函数,大致可以分为以下几类:

  1. 字符串输入输出函数:如 printfscanf 家族函数,它们用于格式化输入输出字符串。
  2. 字符串操作函数:包括字符串的复制、连接、比较等操作。
  3. 字符串查找函数:用于在字符串中查找特定字符或子字符串。
  4. 字符串转换函数:例如将字符串转换为数值类型,或者进行大小写转换。

字符串输入输出函数

  1. printf 函数 printf 函数是 C 语言中最常用的输出函数之一,它能够格式化输出各种类型的数据,包括字符串。其基本语法为:
int printf(const char *format, ...);

其中 format 是格式化字符串,后面的省略号 ... 表示可变参数列表。例如:

#include <stdio.h>

int main() {
    char str[] = "world";
    printf("Hello, %s!\n", str);
    return 0;
}

在这个例子中,%s 是格式说明符,用于输出字符串。printf 函数会在遇到 %s 时,从可变参数列表中取出对应的字符串并输出。

  1. scanf 函数 scanf 函数用于从标准输入读取格式化的数据,读取字符串的格式如下:
int scanf(const char *format, ...);

例如:

#include <stdio.h>

int main() {
    char str[50];
    printf("请输入一个字符串: ");
    scanf("%s", str);
    printf("你输入的字符串是: %s\n", str);
    return 0;
}

这里 %s 用于读取字符串。需要注意的是,scanf 读取字符串时,遇到空白字符(空格、制表符、换行符等)就会停止读取。如果要读取包含空格的字符串,可以使用 fgets 函数。

  1. fgets 函数 fgets 函数用于从指定的流(如标准输入 stdin)中读取一行字符串。其原型为:
char *fgets(char *str, int num, FILE *stream);

str 是用于存储读取到的字符串的缓冲区,num 是要读取的最大字符数(包括 '\0'),stream 是要读取的流。例如:

#include <stdio.h>

int main() {
    char str[50];
    printf("请输入一个字符串: ");
    fgets(str, 50, stdin);
    printf("你输入的字符串是: %s", str);
    return 0;
}

fgets 会读取直到遇到换行符 '\n' 或者读取了 num - 1 个字符,然后在字符串末尾添加 '\0'。如果读取到换行符,换行符会被包含在字符串中。

字符串操作函数

  1. 字符串复制函数
    • strcpy 函数 strcpy 函数用于将一个字符串复制到另一个字符串中。其原型为:
char *strcpy(char *dest, const char *src);

dest 是目标字符串的指针,src 是源字符串的指针。例如:

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

int main() {
    char src[] = "Hello";
    char dest[20];
    strcpy(dest, src);
    printf("复制后的字符串: %s\n", dest);
    return 0;
}

需要注意的是,dest 必须有足够的空间来容纳 src 字符串及其结束符 '\0',否则会导致缓冲区溢出错误。

- **`strncpy` 函数**

strncpy 函数是 strcpy 的安全版本,它最多复制 n 个字符。其原型为:

char *strncpy(char *dest, const char *src, size_t n);

例如:

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

int main() {
    char src[] = "Hello, world";
    char dest[6];
    strncpy(dest, src, 5);
    dest[5] = '\0';  // 手动添加结束符
    printf("复制后的字符串: %s\n", dest);
    return 0;
}

在这个例子中,strncpy 最多复制 src 中的 5 个字符到 dest 中。由于 strncpy 不会自动在 dest 末尾添加 '\0',所以需要手动添加,以确保 dest 是一个合法的字符串。

  1. 字符串连接函数
    • strcat 函数 strcat 函数用于将一个字符串连接到另一个字符串的末尾。其原型为:
char *strcat(char *dest, const char *src);

dest 是目标字符串的指针,src 是要连接的源字符串的指针。例如:

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

int main() {
    char dest[20] = "Hello, ";
    char src[] = "world";
    strcat(dest, src);
    printf("连接后的字符串: %s\n", dest);
    return 0;
}

同样,dest 必须有足够的空间来容纳 dest 原有的内容、src 字符串及其结束符 '\0',否则会导致缓冲区溢出。

- **`strncat` 函数**

strncat 函数是 strcat 的安全版本,它最多连接 n 个字符。其原型为:

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

例如:

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

int main() {
    char dest[20] = "Hello, ";
    char src[] = "world, how are you?";
    strncat(dest, src, 5);
    printf("连接后的字符串: %s\n", dest);
    return 0;
}

strncat 会在 dest 末尾连接 src 中最多 n 个字符,并自动在结果字符串末尾添加 '\0'

  1. 字符串比较函数
    • strcmp 函数 strcmp 函数用于比较两个字符串。其原型为:
int strcmp(const char *s1, const char *s2);

它会按字符逐个比较 s1s2,返回值如下:

  • 如果 s1 等于 s2,返回 0。
  • 如果 s1 小于 s2,返回一个负整数。
  • 如果 s1 大于 s2,返回一个正整数。

例如:

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

int main() {
    char s1[] = "apple";
    char s2[] = "banana";
    int result = strcmp(s1, s2);
    if (result == 0) {
        printf("两个字符串相等\n");
    } else if (result < 0) {
        printf("s1 小于 s2\n");
    } else {
        printf("s1 大于 s2\n");
    }
    return 0;
}
- **`strncmp` 函数**

strncmp 函数是 strcmp 的变体,它最多比较 n 个字符。其原型为:

int strncmp(const char *s1, const char *s2, size_t n);

例如:

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

int main() {
    char s1[] = "apple";
    char s2[] = "applet";
    int result = strncmp(s1, s2, 4);
    if (result == 0) {
        printf("前 4 个字符相等\n");
    } else if (result < 0) {
        printf("s1 的前 4 个字符小于 s2 的前 4 个字符\n");
    } else {
        printf("s1 的前 4 个字符大于 s2 的前 4 个字符\n");
    }
    return 0;
}

字符串查找函数

  1. strchr 函数 strchr 函数用于在字符串中查找指定字符首次出现的位置。其原型为:
char *strchr(const char *str, int c);

str 是要查找的字符串,c 是要查找的字符(会被转换为 char 类型)。它返回一个指针,指向 strc 首次出现的位置,如果未找到则返回 NULL。例如:

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

int main() {
    char str[] = "Hello, world";
    char *ptr = strchr(str, 'o');
    if (ptr!= NULL) {
        printf("字符 'o' 首次出现的位置: %ld\n", ptr - str);
    } else {
        printf("未找到字符 'o'\n");
    }
    return 0;
}
  1. strrchr 函数 strrchr 函数与 strchr 类似,但它查找的是指定字符最后一次出现的位置。其原型为:
char *strrchr(const char *str, int c);

例如:

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

int main() {
    char str[] = "Hello, world";
    char *ptr = strrchr(str, 'o');
    if (ptr!= NULL) {
        printf("字符 'o' 最后一次出现的位置: %ld\n", ptr - str);
    } else {
        printf("未找到字符 'o'\n");
    }
    return 0;
}
  1. strstr 函数 strstr 函数用于在一个字符串中查找另一个子字符串首次出现的位置。其原型为:
char *strstr(const char *haystack, const char *needle);

haystack 是要查找的主字符串,needle 是要查找的子字符串。如果找到,返回一个指针指向 haystackneedle 首次出现的位置,否则返回 NULL。例如:

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

int main() {
    char haystack[] = "Hello, world";
    char needle[] = "world";
    char *ptr = strstr(haystack, needle);
    if (ptr!= NULL) {
        printf("子字符串 'world' 首次出现的位置: %ld\n", ptr - haystack);
    } else {
        printf("未找到子字符串 'world'\n");
    }
    return 0;
}

字符串转换函数

  1. 数值转换函数
    • atoi 函数 atoi 函数用于将字符串转换为整数。其原型为:
int atoi(const char *str);

它会跳过字符串开头的空白字符,然后将后续的数字字符转换为整数。例如:

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

int main() {
    char str[] = "123";
    int num = atoi(str);
    printf("转换后的整数: %d\n", num);
    return 0;
}

如果字符串不能正确转换为整数(例如开头不是数字字符),atoi 返回 0。

- **`atof` 函数**

atof 函数用于将字符串转换为浮点数。其原型为:

double atof(const char *str);

例如:

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

int main() {
    char str[] = "3.14";
    double num = atof(str);
    printf("转换后的浮点数: %lf\n", num);
    return 0;
}
  1. 大小写转换函数
    • toupper 函数 toupper 函数用于将单个字符转换为大写形式。其原型为:
int toupper(int c);

例如:

#include <stdio.h>
#include <ctype.h>

int main() {
    char c = 'a';
    char upperC = toupper(c);
    printf("转换后的大写字符: %c\n", upperC);
    return 0;
}
- **`tolower` 函数**

tolower 函数用于将单个字符转换为小写形式。其原型为:

int tolower(int c);

例如:

#include <stdio.h>
#include <ctype.h>

int main() {
    char c = 'A';
    char lowerC = tolower(c);
    printf("转换后的小写字符: %c\n", lowerC);
    return 0;
}

如果要对整个字符串进行大小写转换,可以通过遍历字符串并逐个调用 touppertolower 函数来实现。例如:

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

void toUpperCase(char *str) {
    for (int i = 0; i < strlen(str); i++) {
        str[i] = toupper(str[i]);
    }
}

void toLowerCase(char *str) {
    for (int i = 0; i < strlen(str); i++) {
        str[i] = tolower(str[i]);
    }
}

int main() {
    char str1[] = "Hello";
    char str2[] = "WORLD";
    toUpperCase(str1);
    toLowerCase(str2);
    printf("转换为大写: %s\n", str1);
    printf("转换为小写: %s\n", str2);
    return 0;
}

实践案例

  1. 字符串加密 假设我们要实现一个简单的字符串加密程序,将字符串中的每个字符替换为其 ASCII 码值加 3 后的字符。可以利用字符串操作函数和字符处理函数来实现。
#include <stdio.h>
#include <string.h>

void encrypt(char *str) {
    for (int i = 0; i < strlen(str); i++) {
        str[i] = str[i] + 3;
    }
}

void decrypt(char *str) {
    for (int i = 0; i < strlen(str); i++) {
        str[i] = str[i] - 3;
    }
}

int main() {
    char message[100];
    printf("请输入要加密的消息: ");
    fgets(message, 100, stdin);
    // 去除 fgets 读取的换行符
    message[strcspn(message, "\n")] = '\0';

    encrypt(message);
    printf("加密后的消息: %s\n", message);

    decrypt(message);
    printf("解密后的消息: %s\n", message);

    return 0;
}
  1. 字符串统计 编写一个程序,统计字符串中字母、数字和其他字符的个数。
#include <stdio.h>
#include <string.h>
#include <ctype.h>

void countChars(const char *str, int *letterCount, int *digitCount, int *otherCount) {
    *letterCount = 0;
    *digitCount = 0;
    *otherCount = 0;
    for (int i = 0; i < strlen(str); i++) {
        if (isalpha(str[i])) {
            (*letterCount)++;
        } else if (isdigit(str[i])) {
            (*digitCount)++;
        } else {
            (*otherCount)++;
        }
    }
}

int main() {
    char str[100];
    printf("请输入一个字符串: ");
    fgets(str, 100, stdin);
    // 去除 fgets 读取的换行符
    str[strcspn(str, "\n")] = '\0';

    int letterCount, digitCount, otherCount;
    countChars(str, &letterCount, &digitCount, &otherCount);

    printf("字母个数: %d\n", letterCount);
    printf("数字个数: %d\n", digitCount);
    printf("其他字符个数: %d\n", otherCount);

    return 0;
}

注意事项

  1. 缓冲区溢出 在使用字符串复制、连接等函数时,务必确保目标缓冲区有足够的空间,以避免缓冲区溢出错误。使用 strncpystrncat 等安全版本的函数可以降低这种风险,但需要注意手动处理字符串结束符的情况。
  2. 空指针检查 在调用字符串函数时,要确保传递的指针不为 NULL。例如,在使用 strchrstrstr 等查找函数时,如果传入 NULL 指针,可能会导致程序崩溃。
  3. 函数的可重入性 在多线程环境下,有些字符串函数(如 strtok)不是可重入的,使用时需要特别小心。可重入函数可以在多个线程同时调用时保证安全,而非可重入函数可能会导致数据竞争和未定义行为。

通过深入理解和熟练运用 C 语言的字符串函数,开发者可以更加高效地处理各种字符串相关的任务,无论是开发系统软件、应用程序还是进行数据处理和文本分析等工作。希望本文的内容能够帮助读者更好地掌握 C 语言字符串函数的使用技巧和注意事项,提升编程能力。