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

C 语言字符串函数大全

2021-11-041.2k 阅读

字符串基础与函数概述

在C语言中,字符串是一种非常重要的数据类型,虽然C语言没有专门的字符串类型,但通过字符数组以及一些相关的库函数来实现对字符串的操作。字符串函数在处理文本、文件输入输出、数据解析等众多场景中都扮演着关键角色。这些函数主要定义在<string.h>头文件中,下面将详细介绍各类字符串函数。

strlen函数 - 计算字符串长度

strlen函数用于计算字符串的长度,不包括字符串结束符'\0'。其函数原型为:

size_t strlen(const char *str);

这里str是要计算长度的字符串指针,返回值类型size_t是在<stddef.h>中定义的无符号整数类型,用于表示对象的大小或长度。

示例代码:

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

int main() {
    const char *str = "Hello, World!";
    size_t length = strlen(str);
    printf("字符串 %s 的长度是 %zu\n", str, length);
    return 0;
}

在上述代码中,定义了字符串"Hello, World!",通过strlen函数获取其长度并打印。这里需要注意的是,strlen函数是通过逐个检查字符,直到遇到'\0'为止来计算长度的。如果字符串没有正确以'\0'结尾,strlen函数的行为是未定义的,可能会导致程序崩溃或得到错误的结果。

strcpy函数 - 字符串复制

strcpy函数用于将一个字符串复制到另一个字符数组中。其函数原型为:

char *strcpy(char *dest, const char *src);

其中dest是目标字符数组的指针,src是源字符串的指针。函数返回值是指向目标字符串dest的指针。

示例代码:

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

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

在这个例子中,将source字符串复制到destination数组中。需要特别注意的是,目标数组destination必须有足够的空间来容纳源字符串及其结束符'\0',否则会导致缓冲区溢出错误,这是一种严重的安全漏洞,可能会被恶意利用来执行任意代码。

strncpy函数 - 安全的字符串复制

为了避免strcpy可能导致的缓冲区溢出问题,strncpy函数提供了一种更安全的字符串复制方式。其函数原型为:

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

dest是目标字符数组,src是源字符串,n指定要从源字符串复制的最大字符数。如果源字符串的长度小于nstrncpy会在复制完源字符串后,在目标数组中填充'\0'直到n个字符。如果源字符串长度大于等于n,目标数组将不会以'\0'结尾,除非在复制n个字符之前遇到了'\0'

示例代码:

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

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

在上述代码中,由于目标数组destination的大小为5,strncpy只复制4个字符,此时需要手动添加'\0'以确保目标数组是一个合法的字符串。虽然strncpy相对安全,但使用不当仍可能导致问题,例如忘记手动添加'\0'可能会在后续操作中引发未定义行为。

strcat函数 - 字符串拼接

strcat函数用于将一个字符串追加到另一个字符串的末尾。其函数原型为:

char *strcat(char *dest, const char *src);

dest是目标字符串,src是要追加的源字符串。函数返回指向目标字符串dest的指针。目标字符串dest必须有足够的空间来容纳源字符串和自身,否则会导致缓冲区溢出。

示例代码:

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

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

在这个例子中,将src字符串追加到dest字符串的末尾。同样,在使用strcat时要确保dest有足够的空间,否则可能会覆盖内存中的其他数据,引发程序错误或安全问题。

strncat函数 - 安全的字符串拼接

类似于strncpystrncat提供了一种更安全的字符串拼接方式。其函数原型为:

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

dest是目标字符串,src是源字符串,n是要从源字符串追加的最大字符数。strncat会在目标字符串的'\0'处开始追加源字符串,并且保证目标字符串以'\0'结尾。

示例代码:

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

int main() {
    char dest[20] = "Hello, ";
    const char *src = "World! This is a long string.";
    strncat(dest, src, 5);
    printf("拼接后的字符串: %s\n", dest);
    return 0;
}

在这个例子中,只从src字符串中追加5个字符到dest字符串。strncat通过限制追加字符数,减少了缓冲区溢出的风险,但在使用时仍需合理分配目标字符串的空间,以确保拼接操作的正确性。

strcmp函数 - 字符串比较

strcmp函数用于比较两个字符串。其函数原型为:

int strcmp(const char *str1, const char *str2);

str1str2是要比较的两个字符串。函数返回值遵循以下规则:

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

字符串比较是按字符的ASCII码值逐个进行的,直到遇到不同的字符或到达字符串结束符'\0'

示例代码:

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

int main() {
    const char *str1 = "apple";
    const char *str2 = "banana";
    int result = strcmp(str1, str2);
    if (result < 0) {
        printf("%s 小于 %s\n", str1, str2);
    } else if (result == 0) {
        printf("%s 等于 %s\n", str1, str2);
    } else {
        printf("%s 大于 %s\n", str1, str2);
    }
    return 0;
}

在上述代码中,比较str1str2两个字符串,并根据返回结果输出相应的信息。需要注意的是,strcmp区分大小写,如果需要不区分大小写的比较,可以使用stricmp(在某些系统中)或strcasecmp(POSIX系统)函数。

strncmp函数 - 比较指定长度的字符串

strncmp函数用于比较两个字符串的前n个字符。其函数原型为:

int strncmp(const char *str1, const char *str2, size_t n);

str1str2是要比较的字符串,n是比较的最大字符数。返回值规则与strcmp类似。

示例代码:

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

int main() {
    const char *str1 = "apple";
    const char *str2 = "applet";
    int result = strncmp(str1, str2, 3);
    if (result < 0) {
        printf("前3个字符中,%s 小于 %s\n", str1, str2);
    } else if (result == 0) {
        printf("前3个字符中,%s 等于 %s\n", str1, str2);
    } else {
        printf("前3个字符中,%s 大于 %s\n", str1, str2);
    }
    return 0;
}

在这个例子中,只比较str1str2的前3个字符。strncmp在需要控制比较长度的场景中非常有用,比如在搜索部分匹配的字符串时。

strchr函数 - 查找字符在字符串中的首次出现位置

strchr函数用于在字符串中查找一个字符的首次出现位置。其函数原型为:

char *strchr(const char *str, int c);

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

示例代码:

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

int main() {
    const char *str = "Hello, World!";
    char *result = strchr(str, 'o');
    if (result != NULL) {
        printf("字符 'o' 首次出现在位置: %ld\n", result - str);
    } else {
        printf("未找到字符 'o'\n");
    }
    return 0;
}

在上述代码中,查找字符'o'在字符串str中的位置,并输出其相对于字符串起始位置的偏移量。如果未找到,输出相应提示信息。

strrchr函数 - 查找字符在字符串中的最后出现位置

strrchr函数与strchr类似,但它查找的是字符在字符串中最后出现的位置。其函数原型为:

char *strrchr(const char *str, int c);

str是字符串,c是要查找的字符。返回一个指针,指向字符c在字符串str中最后出现的位置,如果未找到则返回NULL

示例代码:

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

int main() {
    const char *str = "Hello, World!";
    char *result = strrchr(str, 'o');
    if (result != NULL) {
        printf("字符 'o' 最后出现在位置: %ld\n", result - str);
    } else {
        printf("未找到字符 'o'\n");
    }
    return 0;
}

在这个例子中,查找字符'o'在字符串str中最后出现的位置,并输出其位置信息。与strchr不同,strrchr是从字符串的末尾开始向前查找。

strstr函数 - 查找子字符串在字符串中的首次出现位置

strstr函数用于在一个字符串中查找另一个子字符串的首次出现位置。其函数原型为:

char *strstr(const char *haystack, const char *needle);

haystack是要搜索的主字符串,needle是要查找的子字符串。函数返回一个指针,指向子字符串needle在主字符串haystack中首次出现的位置,如果未找到则返回NULL

示例代码:

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

int main() {
    const char *haystack = "Hello, World! This is a test.";
    const char *needle = "is";
    char *result = strstr(haystack, needle);
    if (result != NULL) {
        printf("子字符串 '%s' 首次出现在位置: %ld\n", needle, result - haystack);
    } else {
        printf("未找到子字符串 '%s'\n", needle);
    }
    return 0;
}

在上述代码中,查找子字符串"is"在主字符串haystack中的位置,并输出其相对于主字符串起始位置的偏移量。如果未找到,输出相应提示信息。

strtok函数 - 字符串分割

strtok函数用于将字符串按照指定的分隔符进行分割。其函数原型为:

char *strtok(char *str, const char *delim);

str是要分割的字符串,delim是包含分隔符的字符串。该函数的使用较为特殊,第一次调用时,str参数传入要分割的字符串,后续调用时,str参数传入NULL,函数会记住上次分割的位置并继续分割。当没有更多的分隔符时,函数返回NULL

示例代码:

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

int main() {
    char str[] = "Hello,World;This is a test";
    const char *delim = ",; ";
    char *token = strtok(str, delim);
    while (token != NULL) {
        printf("分割后的子字符串: %s\n", token);
        token = strtok(NULL, delim);
    }
    return 0;
}

在这个例子中,字符串str按照","";"" "进行分割,通过循环不断调用strtok获取分割后的子字符串并打印。需要注意的是,strtok会修改原始字符串,将分隔符替换为'\0',并且在多线程环境下使用时需要特别小心,因为它不是线程安全的。

sprintf函数 - 格式化字符串输出到字符数组

sprintf函数用于将格式化的数据输出到一个字符数组中。其函数原型为:

int sprintf(char *str, const char *format, ...);

str是目标字符数组,format是格式化字符串,与printf函数的格式化字符串类似,后面的...表示可变参数列表,根据格式化字符串的要求提供相应的参数。函数返回成功写入的字符数(不包括'\0')。

示例代码:

#include <stdio.h>

int main() {
    char buffer[50];
    int num = 123;
    const char *text = "Hello";
    int result = sprintf(buffer, "The number is %d and the text is %s", num, text);
    printf("写入的字符数: %d\n", result);
    printf("格式化后的字符串: %s\n", buffer);
    return 0;
}

在上述代码中,将整数num和字符串text按照指定的格式输出到字符数组buffer中,并打印写入的字符数和格式化后的字符串。使用sprintf时要确保目标数组有足够的空间,否则会导致缓冲区溢出。

sscanf函数 - 从字符串中按格式读取数据

sscanf函数用于从字符串中按照指定的格式读取数据。其函数原型为:

int sscanf(const char *str, const char *format, ...);

str是要读取的字符串,format是格式化字符串,与scanf函数的格式化字符串类似,后面的...表示可变参数列表,用于存储读取的数据。函数返回成功匹配和赋值的输入项数,如果到达输入字符串末尾或匹配失败则返回一个小于预期值的数。

示例代码:

#include <stdio.h>

int main() {
    const char *str = "123 Hello";
    int num;
    char text[20];
    int result = sscanf(str, "%d %s", &num, text);
    if (result == 2) {
        printf("读取的整数: %d\n", num);
        printf("读取的字符串: %s\n", text);
    } else {
        printf("读取失败\n");
    }
    return 0;
}

在这个例子中,从字符串str中按照"%d %s"的格式读取一个整数和一个字符串,并根据返回值判断读取是否成功。sscanf在解析包含特定格式数据的字符串时非常有用,但同样要注意参数的正确性和目标变量的空间足够。

memset函数 - 填充内存区域

虽然memset函数不完全是字符串函数,但常用于初始化字符串相关的内存区域。其函数原型为:

void *memset(void *ptr, int value, size_t num);

ptr是指向要填充的内存区域的指针,value是要填充的值(会被转换为unsigned char类型),num是要填充的字节数。函数返回指向ptr的指针。

示例代码:

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

int main() {
    char buffer[10];
    memset(buffer, 'A', 5);
    buffer[5] = '\0';
    printf("填充后的字符串: %s\n", buffer);
    return 0;
}

在上述代码中,将字符数组buffer的前5个字节填充为字符'A',并手动添加字符串结束符'\0',然后打印填充后的字符串。memset常用于初始化数组、结构体等内存区域,确保数据的一致性。

memcpy函数 - 内存块复制

memcpy函数用于将一块内存区域的内容复制到另一块内存区域。其函数原型为:

void *memcpy(void *dest, const void *src, size_t n);

dest是目标内存区域的指针,src是源内存区域的指针,n是要复制的字节数。函数返回指向dest的指针。

示例代码:

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

int main() {
    char source[] = "Hello";
    char destination[20];
    memcpy(destination, source, strlen(source) + 1);
    printf("复制后的字符串: %s\n", destination);
    return 0;
}

在这个例子中,将source字符串复制到destination数组中,通过strlen(source) + 1确保包括字符串结束符'\0'memcpy可以用于复制任何类型的内存数据,不限于字符串,但在复制字符串时要注意处理好结束符。与strcpy不同,memcpy不会检查目标内存区域是否以'\0'结尾,也不会自动添加'\0',这在处理非字符串数据时很有用,但在处理字符串时需要额外注意。

memcmp函数 - 比较内存块

memcmp函数用于比较两个内存块的内容。其函数原型为:

int memcmp(const void *ptr1, const void *ptr2, size_t n);

ptr1ptr2是要比较的两个内存块的指针,n是要比较的字节数。返回值规则与strcmp类似:

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

示例代码:

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

int main() {
    char str1[] = "Hello";
    char str2[] = "Hello";
    int result = memcmp(str1, str2, strlen(str1));
    if (result < 0) {
        printf("str1 小于 str2\n");
    } else if (result == 0) {
        printf("str1 等于 str2\n");
    } else {
        printf("str1 大于 str2\n");
    }
    return 0;
}

在上述代码中,比较str1str2两个字符串的前strlen(str1)个字节。memcmp在需要精确比较内存内容而不仅仅是字符串时非常有用,它不依赖于字符串结束符'\0',而是按照指定的字节数进行比较。

通过对这些C语言字符串函数的深入理解和熟练运用,开发者可以更加高效地处理字符串相关的操作,无论是文本处理、数据解析还是文件操作等场景,都能游刃有余。同时,在使用这些函数时,要始终注意内存管理和边界条件,避免出现缓冲区溢出等安全问题和程序错误。