20. 字符串函数.md 6.3 KB

20 字符串函数

20.1 完整概念讲解

字符串基础

C语言中字符串是'\0'结尾的字符数组。字符串字面量(如"hello")自动在末尾添加'\0'

字符串长度 vs 数组大小:

  • strlen():返回字符数(不含'\0'
  • sizeof:返回数组总字节数(含'\0'

标准字符串函数

函数 功能 安全版本
strlen 计算字符串长度 -
strcpy 复制字符串 strncpy
strcat 连接字符串 strncat
strcmp 比较字符串 strncmp
strchr 查找字符首次出现 -
strstr 查找子串首次出现 -
strtok 分割字符串 -
snprintf 安全格式化到字符串 -

字符串转换函数

函数 功能 说明
atoi 字符串转整数 无错误检测
atof 字符串转浮点数 无错误检测
strtol 字符串转长整数 可检测错误

20.2 核心API/语法

#include <string.h>

// 字符串长度
size_t strlen(const char *s);

// 字符串复制
char *strcpy(char *dest, const char *src);
char *strncpy(char *dest, const char *src, size_t n);

// 字符串连接
char *strcat(char *dest, const char *src);
char *strncat(char *dest, const char *src, size_t n);

// 字符串比较
int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, size_t n);

// 查找
char *strchr(const char *s, int c);
char *strstr(const char *haystack, const char *needle);
char *strtok(char *str, const char *delim);

// 安全格式化
int snprintf(char *str, size_t size, const char *format, ...);

// 字符串转换
int atoi(const char *str);
double atof(const char *str);
long strtol(const char *str, char **endptr, int base);

20.3 代码示例(完整可编译,附gcc命令)

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

void demonstrate_basic(void) {
    printf("=== 基础字符串函数 ===\n");

    char src[] = "Hello, World!";
    char dest[50];

    // strlen
    printf("strlen(\"%s\") = %zu\n", src, strlen(src));
    printf("sizeof(src) = %zu\n", sizeof(src));

    // strcpy
    strcpy(dest, src);
    printf("strcpy: dest = \"%s\"\n", dest);

    // strncpy(注意:不会自动添加'\0')
    char buf[10];
    strncpy(buf, src, sizeof(buf) - 1);
    buf[sizeof(buf) - 1] = '\0';
    printf("strncpy: buf = \"%s\"\n", buf);

    // strcat
    char str1[50] = "Hello";
    char str2[] = " World";
    strcat(str1, str2);
    printf("strcat: str1 = \"%s\"\n", str1);

    // strcmp
    printf("strcmp(\"abc\", \"abc\") = %d\n", strcmp("abc", "abc"));
    printf("strcmp(\"abc\", \"abd\") = %d\n", strcmp("abc", "abd"));
    printf("strcmp(\"abd\", \"abc\") = %d\n", strcmp("abd", "abc"));
}

void demonstrate_search(void) {
    printf("\n=== 查找函数 ===\n");

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

    // strchr
    char *p = strchr(str, 'W');
    if (p) {
        printf("strchr('W'): found at index %ld, rest = \"%s\"\n",
               p - str, p);
    }

    // strstr
    p = strstr(str, "World");
    if (p) {
        printf("strstr(\"World\"): found at index %ld\n", p - str);
    }

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

void demonstrate_snprintf(void) {
    printf("\n=== snprintf安全格式化 ===\n");

    char buffer[50];
    int result;

    result = snprintf(buffer, sizeof(buffer), "Number: %d, Float: %.2f", 42, 3.14);
    printf("snprintf: \"%s\" (would write %d chars)\n", buffer, result);

    // 截断测试
    char small_buf[10];
    result = snprintf(small_buf, sizeof(small_buf), "This is a long string");
    printf("Truncated: \"%s\" (would write %d chars)\n", small_buf, result);
}

void demonstrate_conversion(void) {
    printf("\n=== 字符串转换 ===\n");

    // atoi
    printf("atoi(\"123\") = %d\n", atoi("123"));
    printf("atoi(\"abc\") = %d\n", atoi("abc"));

    // strtol(更安全,可检测错误)
    char *endptr;
    long val;

    val = strtol("123abc", &endptr, 10);
    printf("strtol(\"123abc\"): val = %ld, endptr = \"%s\"\n", val, endptr);

    val = strtol("abc", &endptr, 10);
    printf("strtol(\"abc\"): val = %ld, endptr = \"%s\"\n", val, endptr);

    // 十六进制转换
    val = strtol("0xFF", NULL, 16);
    printf("strtol(\"0xFF\", 16) = %ld\n", val);
}

int main() {
    demonstrate_basic();
    demonstrate_search();
    demonstrate_snprintf();
    demonstrate_conversion();

    return 0;
}

编译命令:

gcc -Wall -Wextra -o string_functions string_functions.c
./string_functions

20.4 注意事项与易错点

  1. strncpy不添加'\0':如果src长度>=n,dest不会以'\0'结尾,需手动添加
  2. 缓冲区溢出:strcpy/strcat不检查目标空间,使用strncpy/strncat并指定大小
  3. strtok修改原字符串:strtok会将分隔符替换为'\0'
  4. atoi无错误检测:返回0表示失败或真的为0,无法区分
  5. 字符串比较不能用====比较指针地址,应用strcmp

20.5 面试要点(3-5个Q&A)

Q1: strcpy和strncpy有什么区别? A: strcpy不检查目标空间,可能溢出。strncpy最多复制n个字符,但不保证添加'\0'。安全用法:strncpy(dest, src, n-1); dest[n-1] = '\0';

Q2: 为什么说atoi不安全? A: atoi无法区分"0"和非法输入,没有错误检测。应使用strtol,通过endptr检测转换是否成功。

Q3: strtok的线程安全性问题? A: strtok使用静态内部状态保存位置,多线程调用不安全。可使用strtok_r(POSIX)或strtok_s(C11)替代。

Q4: 如何安全地拼接多个字符串? A: 使用snprintf计算所需空间,动态分配,或使用足够大的缓冲区。避免使用strcat多次追加导致溢出。

Q5: sizeof和strlen对字符串的区别? A: sizeof返回数组总大小(含'\0'),strlen返回字符数(不含'\0')。对指针使用sizeof得到的是指针大小,不是字符串长度。