【C】字符串函数与字符函数

字符函数与字符串函数

文章目录

  • 字符函数与字符串函数
    • 求字符串长度
      • `strlen`
        • 模拟实现
    • 长度不受限的字符串函数
      • `strpy`
        • 模拟实现
      • `strcat`
        • 模拟实现
      • `strcmp`
        • 模拟实现
    • 长度受限的字符串函数
      • `strncpy`
        • 模拟实现
      • `strncat`
        • 使用
        • 模拟实现
      • `strncmp`
        • 使用
        • 用途
        • 模拟实现
    • 字符串查找
      • `strstr`
        • 使用
        • 模拟实现
      • `strtok`
        • 示例
        • 模拟实现
    • 错误信息报告
      • `strerror`
    • 内存操作函数
      • `memcpy`
        • 模拟实现
      • `memmove`
        • 模拟实现
      • `memset`
      • `memcmp`

求字符串长度

strlen

strlen 是 C 语言的一个字符串函数,用于计算字符串的长度(不包括结尾的空字符 ‘\0’)。它的原型定义在 头文件中:

size_t strlen(const char* str);

其中,str 是一个指向以 null 结尾的字符数组(即字符串)的指针。函数返回一个 size_t 类型的值,表示字符串的长度(不包括结尾的空字符 ‘\0’)。

  • 字符串已经 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包含 ‘\0’ )。
  • 参数指向的字符串必须要以 ‘\0’ 结束。
  • 注意函数的返回值为size_t,是无符号的( 易错 )
模拟实现
#include 

size_t strlen_custom(const char* str) {
    const char* ptr = str;
    while (*ptr != '\0') {
        ptr++;
    }
    return (size_t)(ptr - str);
}

int main() {
    const char* myString = "Hello, World!";
    size_t length = strlen_custom(myString);
    printf("Length of the string: %zu\n", length);
    return 0;
}
//输出结果:13

在这个示例中,strlen_custom 函数同样接受一个指向字符数组的指针 str。通过使用指针来遍历字符数组,当指针 ptr 指向空字符时,循环结束。最后,通过计算 ptr 和原始指针 str 的差值来获取字符数组的长度。

长度不受限的字符串函数

strpy

strcpy 是 C 语言的一个字符串函数,用于将一个字符串(源字符串)拷贝到另一个字符串(目标字符串)。它的原型定义在 头文件中:

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

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

用途:

  1. 字符串复制strcpy 可以将一个字符串的内容复制到另一个字符串中,使目标字符串与源字符串相同。这在需要对字符串进行修改或者创建新的字符串副本时非常有用。
  2. 字符串拼接:通过结合使用 strcpy 和字符串的长度函数 strlen,可以实现字符串的拼接。先将目标字符串复制到新字符串中,然后将源字符串追加到新字符串的末尾。
  3. 字符串传递:在函数之间传递字符串参数时,通常使用 strcpy 将一个字符串复制到另一个指针类型的参数中。这样可以避免在函数内部修改原始字符串,并保持参数的一致性。

需要注意的是,使用 strcpy 函数时需要确保目标字符串的长度足够大,以容纳源字符串的所有字符及结尾的空字符。否则可能导致缓冲区溢出和未定义行为。可以使用 strlen 函数来检查源字符串的长度,并确保目标字符串具备足够的空间。

模拟实现
#include 

char* strcpy_custom(char* dest, const char* src) {
    char* ptr = dest;
    while (*src != '\0') {
        *ptr++ = *src++;
    }
    *ptr = '\0';
    return dest;
}

int main() {
    const char* source = "Hello, World!";
    char destination[20];
    strcpy_custom(destination, source);
    printf("Copied string: %s\n", destination);
    return 0;
}
//输出结果
Copied string: Hello, World!

在这个示例中,strcpy_custom 函数接受一个指向目标字符串的指针 dest 和一个指向源字符串的指针 src。通过使用指针来遍历源字符串,将每个字符依次拷贝到目标字符串中,并在最后添加一个空字符 ‘\0’。

需要注意的是,源字符串必须以空字符 ‘\0’ 结尾,否则可能会导致未定义的行为。此外,目标字符串的大小需要足够大,以确保能够容纳源字符串的所有字符及结尾的空字符。


strcat

strcat 是 C 语言中的一个字符串函数,用于将一个字符串(源字符串)追加到另一个字符串(目标字符串)的末尾。它的原型定义在 头文件中:

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

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

模拟实现
#include 

char* my_strcat(char* dest, const char* src) {
    // 找到目标字符串的结尾位置
    char* dest_end = dest;
    while (*dest_end != '\0') {
        dest_end++;
    }

    // 将源字符串复制到目标字符串的结尾处
    while (*src != '\0') {
        *dest_end = *src;
        src++;
        dest_end++;
    }

    // 添加结尾的空字符
    *dest_end = '\0';

    return dest;
}

int main() {
    char str1[20] = "Hello, ";
    const char* str2 = "World!";
    
    my_strcat(str1, str2);
    
    printf("Concatenated string: %s\n", str1);
    
    return 0;
}
//输出结果
Concatenated string: Hello, World!

此模拟实现中,my_strcat 函数接收两个参数:目标字符串 dest 和源字符串 src。首先,我们需要找到目标字符串 dest 的结尾位置,通过遍历字符数组找到 \0 字符。然后,将源字符串 src 的内容逐个复制到 dest 字符数组的结尾处,并在最后添加一个结尾的空字符 \0。最后,将修改后的目标字符串的指针返回。在 main 函数中,我们调用 my_strcat 函数将 str2 字符串追加到 str1 字符数组的末尾,并打印拼接后的结果。


使用 strcat 函数时需要注意以下几点:

  1. 目标字符串必须具备足够的空间来容纳源字符串的内容及结尾的空字符。否则可能导致缓冲区溢出。
  2. 源字符串必须以空字符结尾,否则会导致未定义的行为。在 C 语言中,字符串以空字符 '\0' 结尾,表示字符串的结束。
  3. 目标字符串通常应该是一个足够大的字符数组,以确保有足够的空间来容纳追加的字符串。否则,可以使用动态内存分配函数(如 malloc)来分配足够的空间。
  4. 在拼接过程中,需要确保目标字符串的结尾处有足够的空间。如果目标字符串的末尾字符不是空字符,则 strcat 函数会将源字符串直接追加在目标字符串的末尾,可能导致结果字符串不符合预期。

注意事项:

  • 在使用 strcat 进行字符串拼接时,要确保目标字符串的长度足够大,以容纳源字符串的内容及结尾的空字符。
  • 避免发生缓冲区溢出,使用函数 strncat 可以指定追加的最大字符数。
  • 注意处理字符串中的空字符 ‘\0’,确保字符串的有效性和正确性。
  • 在使用 strcat 进行字符串拼接时,要考虑字符串的顺序和位置,确保得到预期的结果。
  • 在操作字符串时,尤其是在使用字符串函数时,要注意处理边界条件和异常情况,以避免潜在的问题和漏洞。

总之,strcat 函数是进行字符串拼接的常用工具,但在使用时需要注意目标字符串的大小及合法性,以及源字符串的结尾字符等细节,以确保安全和正确性。


strcmp

当你需要比较两个字符串的内容时,可以使用 C 语言中的 strcmp 函数。strcmp 函数用于按照字典序比较两个以 null 字符(‘\0’)结尾的字符串。

strcmp 函数的定义如下:

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

strcmp 函数接受两个参数 str1str2,它们是需要比较的字符串。

strcmp 函数的返回值为整数,其含义如下:

  • 如果 str1str2 相等,则返回值为 0。
  • 如果 str1 在字典中排在 str2 之前,则返回值小于 0。
  • 如果 str1 在字典中排在 str2 之后,则返回值大于 0。
模拟实现
#include 

int my_strcmp(const char* str1, const char* str2) {
    while (*str1 && (*str1 == *str2)) {
        str1++;
        str2++;
    }

    return *(unsigned char*)str1 - *(unsigned char*)str2;
}

int main() {
    const char* str1 = "apple";
    const char* str2 = "banana";
    const char* str3 = "apple";

    int result = my_strcmp(str1, str2);
    if (result == 0) {
        printf("str1 and str2 are equal\n");
    } else if (result < 0) {
        printf("str1 comes before str2\n");
    } else {
        printf("str1 comes after str2\n");
    }

    result = my_strcmp(str1, str3);
    if (result == 0) {
        printf("str1 and str3 are equal\n");
    } else if (result < 0) {
        printf("str1 comes before str3\n");
    } else {
        printf("str1 comes after str3\n");
    }

    return 0;
}
str1 comes before str2
str1 and str3 are equal

这个模拟实现的 my_strcmp 函数通过迭代比较两个字符串中的字符,直到遇到不相等的字符或者其中一个字符串的结尾。返回值的计算方式与标准库中的 strcmp 函数相同。

输出结果与前面示例的 strcmp 函数的输出结果相同。

实际上标准库中的 strcmp 函数可能有更多的优化和错误处理,以应对更多复杂的情况。

strcmp 函数在字符串处理、排序和查找中非常有用。以下是它的一些常见用途:

  1. 对字符串进行排序:可以使用 strcmp 函数进行字符串的字典排序,比较函数需要用在排序算法中的回调函数中,来比较字符串的大小。

  2. 比较字符串的相等性:使用 strcmp 函数可以判断两个字符串是否相等,而不仅仅是比较指针或内存地址。

注意事项

  1. strcmp 函数是区分大小写的。如果你需要进行大小写不敏感的比较,可以使用 strcasecmp(忽略大小写)或 stricmp(不区分大小写)函数。

  2. 当比较字符串时,要确保字符串以 null 字符(‘\0’)结尾,否则可能导致未定义的行为。

  3. 当比较长字符串时,要确保字符串的长度不超过被比较的字符串的长度,以避免内存访问错误。

  4. strcmp 函数返回值为负数时,表示第一个字符串在字典中排在第二个字符串之前;返回值为正数时,表示第一个字符串在字典中排在第二个字符串之后。返回值的具体数值大小没有特定的意义,只有正负号和零的信息才有意义。

长度受限的字符串函数

strncpy

strncpy 是一个C语言标准库函数,用于将一个字符串的一部分拷贝到另一个字符串中

char* strncpy(char* destination, const char* source, size_t num);

其中 destination 是目标字符串的指针,source 是源字符串的指针,num 是最大拷贝的字符数。函数的返回值是指向目标字符串的指针。

strncpy 函数会将源字符串 source 的前 num 个字符拷贝到目标字符串 destination 中。如果 source 字符串长度小于 num,那么在拷贝结束后,目标字符串 destination 的剩余部分会被填充为零。如果 source 字符串长度大于等于 num,那么 destination 字符串不会以零字节结尾。

strncpy 函数的用途主要是进行字符串的部分拷贝。例如,当你需要从一个字符串中拷贝一部分内容到另一个数组或者缓冲区中时,可以使用 strncpy。它可用于复制文件路径、截取一定长度的文件名等。

需要注意以下几点:

  1. strncpy 函数在拷贝过程中不会自动添加字符串的结尾符号。如果拷贝的部分超过了源字符串的长度,目标字符串不会以零字节结尾。因此,在使用 strncpy 时,需要手动添加结尾符 '\0'

  2. 如果 num 大于源字符串的长度,目标字符串的剩余部分会被填充为零。这可能会导致目标字符串的长度超过预期。为了避免这种情况,你可以使用 strlcpy 函数。strlcpy 函数类似于 strncpy,但会确保目标字符串以零字节结尾,并在超出 num 时截断目标字符串。

模拟实现
#include 

char* my_strncpy(char* destination, const char* source, size_t num) {
    char* dest = destination;

    while (num > 0 && *source) {
        *dest++ = *source++;
        num--;
    }

    while (num > 0) {
        *dest++ = '\0';
        num--;
    }

    return destination;
}

int main() {
    char dest[10];
    const char* src = "Hello, world!";

    my_strncpy(dest, src, sizeof(dest));
    printf("Destination: %s\n", dest);

    return 0;
}

这个示例中,my_strncpy 函数模拟了 strncpy 函数的功能。它首先将源字符串的字符逐个拷贝到目标字符串中,直到达到最大拷贝字符数 num 或者源字符串的末尾。然后,如果 num 大于已拷贝字符数,它会在目标字符串中继续添加零字符,直到 num 为零。

该示例的输出结果为:Destination: Hello, wo,符合预期。然而,需要注意,实际的 strncpy 函数可能有更多优化和错误处理,以应对更多复杂的情况。所以,在实际开发中,它应该使用标准库中的函数,而不是自己实现。


strncat

strncat 是一个C语言标准库函数,用于将一个字符串的一部分追加到另一个字符串的末尾。它的函数原型如下:

char* strncat(char* destination, const char* source, size_t num);

其中 destination 是目标字符串的指针,source 是源字符串的指针,num 是最大追加的字符数。函数的返回值是指向目标字符串的指针。

strncat 函数会将源字符串 source 的前 num 个字符追加到目标字符串 destination 的末尾,并添加终止的零字节。如果源字符串 source 的长度小于 num,那么会将整个 source 字符串追加到 destination 的末尾。如果 source 字符串长度大于等于 num,那么只会将 source 字符串的前 num 个字符追加到 destination 的末尾。

strncat 函数的用途主要是将一部分字符串追加到另一个字符串的末尾。它可用于连接字符数组、构建文件名等。

需要注意以下几点:

  1. destination 必须具有足够的空间来容纳源字符串 source 的内容和终止的零字节。请确保目标字符串有足够的空间来避免发生溢出。

  2. 当目标字符串 destination 的长度已经达到前 num 个字符时,strncat 将自动在目标字符串末尾添加一个终止的零字节。

使用
#include 
#include 

int main() {
    char dest[20] = "Hello, ";
    const char* src = "world!";
    
    // 将源字符串的前5个字符追加到目标字符串的末尾
    strncat(dest, src, 5);
    
    printf("Destination: %s\n", dest);
    return 0;
}

该示例中,目标字符串 dest 初始化为 "Hello, ",源字符串 src 为 “world!”。使用 strncat 函数,将 src 的前5个字符追加到 dest 的末尾。最终输出结果为 “Hello, world”。

要确保目标字符串保持有效,需要确保 destination 具有足够的大小来容纳源字符串的内容和终止的零字节。如果目标字符串的大小不足以容纳这些字符,可能会导致缓冲区溢出,并引发未定义的行为。因此,在实际使用中,应当仔细计算目标字符串的大小,并确保其足够大。

模拟实现

好的,下面是一个简单的模拟实现 strncat 函数的示例代码:

#include 

char* myStrncat(char* destination, const char* source, size_t num) {
    char* destPtr = destination;
    
    // 将 destPtr 移动到 destination 的末尾
    while (*destPtr != '\0') {
        destPtr++;
    }
    
    // 追加源字符串的前 num 个字符到目标字符串
    while (*source != '\0' && num > 0) {
        *destPtr = *source;
        destPtr++;
        source++;
        num--;
    }
    
    // 添加终止的零字节
    *destPtr = '\0';
    
    return destination;
}

int main() {
    char dest[20] = "Hello, ";
    const char* src = "world!";
    
    // 将源字符串的前5个字符追加到目标字符串的末尾
    myStrncat(dest, src, 5);
    
    printf("Destination: %s\n", dest);
    return 0;
}

在这个示例中,我们定义了一个名为 myStrncat 的函数来模拟 strncat 函数的行为。它接受一个目标字符串 destination、一个源字符串 source 和一个表示最大追加字符数的整数 num。函数首先将 destPtr 指针指向 destination 的末尾,并将其移动到字符串的结束符位置。然后,它迭代源字符串的字符,将其追加到 destination 末尾的字符中,直到达到最大追加字符数 num 或源字符串结束。最后,函数添加一个终止的零字节,并返回目标字符串 destination 的指针。

该示例中,目标字符串 dest 初始化为 "Hello, ",源字符串 src 为 “world!”。通过调用 myStrncat 函数,将 src 的前5个字符追加到 dest 的末尾,并输出结果为 "Hello, world"


strncmp

strncmp 是 C 语言中一个用于比较两个字符串的函数。它可以按照指定的最大字符比较数,逐个字符地比较两个字符串的字母序。它的函数原型如下:

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

参数说明:

  • str1:第一个要比较的字符串。
  • str2:第二个要比较的字符串。
  • num:要比较的最大字符数。

函数返回值:

  • 如果两个字符串相等,返回值为 0。
  • 如果第一个字符串小于第二个字符串,返回值小于 0。
  • 如果第一个字符串大于第二个字符串,返回值大于 0。
使用
#include 
#include 

int main() {
    const char* str1 = "apple";
    const char* str2 = "apple";
    const char* str3 = "banana";
    
    int result1 = strncmp(str1, str2, 5);
    int result2 = strncmp(str1, str3, 5);
    
    if (result1 == 0) {
        printf("str1 and str2 are equal\n");
    } else if (result1 < 0) {
        printf("str1 is less than str2\n");
    } else {
        printf("str1 is greater than str2\n");
    }
    
    if (result2 == 0) {
        printf("str1 and str3 are equal\n");
    } else if(result2 < 0) {
        printf("str1 is less than str3\n");
    } else {
        printf("str1 is greater than str3\n");
    }
    
    return 0;
}

在这个示例中,我们使用了 strncmp 函数来比较字符串的前 5 个字符。首先,我们将 str1str2 比较,它们是相等的字符串,所以返回值为 0。然后,我们将 str1str3 比较,它们的前 5 个字符不相同,所以返回值为负数,表明 str1 小于 str3

用途
  1. 字符串排序:strncmp 可以用于对字符串进行排序,根据返回值的正负来判断字符串的大小关系,从而实现排序功能。
  2. 字符串比较:strncmp 可以比较两个字符串是否相等,根据返回值是否为 0 来判断两个字符串是否相等。

注意事项:

  1. 最大字符比较数:strncmp 可以指定最大的字符比较数,但需要注意 num 参数必须小于等于字符串的实际长度,否则可能导致访问越界的错误。
  2. 不足字符比较:如果两个字符串的长度不足 num,则 strncmp 会对比较的字符进行补齐操作。这可能会导致比较结果不准确,因此在使用时需要注意字符串的长度和比较的字符数。
  3. 字符串末尾:strncmp 在比较时会遇到字符串的末尾字符,遇到末尾字符后会终止比较。因此,对于可能以 '\0' 结尾的字符串,应确保 num 不包括 '\0' 在内,避免比较过程过早结束。
  4. 二进制数据:strncmp 是按照字母序进行比较的,对于二进制数据的比较不够准确。如果需要比较二进制数据,请使用 memcmp 函数。

注意:strncmp 函数只比较字符的字母序,而不考虑字符的大小写关系。如果你需要考虑大小写,可以使用 strncasecmp 函数来代替。

模拟实现
#include 
#include 

int myStrncmp(const char* str1, const char* str2, size_t num) {
    while (num > 0 && *str1 && *str2 && *str1 == *str2) {
        str1++;
        str2++;
        num--;
    }
    
    if (num == 0) {
        return 0;
    }
    else {
        return (*str1 - *str2);
    }
}

int main() {
    const char* str1 = "Hello";
    const char* str2 = "Hellx";
    
    // 比较前4个字符
    int result = myStrncmp(str1, str2, 4);
    
    if (result == 0) {
        printf("Strings are equal.\n");
    }
    else if (result < 0) {
        printf("String 1 is less than string 2.\n");
    }
    else {
        printf("String 1 is greater than string 2.\n");
    }
    
    return 0;
}

在这个示例中,我们定义了一个名为 myStrncmp 的函数来模拟 strncmp 函数的行为。它接受两个字符串 str1str2,以及一个表示比较字符数的整数 num。函数通过迭代两个字符串的字符,逐个比较它们是否相等,直到达到最大比较字符数 num 或者遇到不相等的字符。如果比较的字符数达到了 num,则返回0表示两个字符串相等;如果其中一个字符串遇到了不相等的字符,则返回它们的 ASCII 码之差。如果第一个字符串比第二个字符串小,则返回负数;如果第一个字符串比第二个字符串大,则返回正数。

在本示例中,比较了两个字符串 "Hello""Hellx" 的前4个字符,因为最后一个字符不相等,所以结果为正数,表示 str1 大于 str2

字符串查找

strstr

strstr 是 C 语言中一个用于查找子字符串的函数。它可以在一个字符串中搜索指定的子字符串,并返回第一次出现该子字符串的位置。它的函数原型如下:

char* strstr(const char* str1, const char* str2);

参数说明:

  • str1:要搜索的源字符串。
  • str2:要查找的子字符串。

函数返回值:

  • 如果找到子字符串,返回指向第一次出现子字符串的位置的指针。
  • 如果未找到子字符串,返回 NULL
使用
#include 
#include 

int main() {
    const char* str1 = "Hello, world!";
    const char* str2 = "world";

    char* result = strstr(str1, str2);

    if (result != NULL) {
        printf("Found: %s\n", result);
    } else {
        printf("Not found\n");
    }

    return 0;
}

在这个示例中,我们使用了 strstr 函数来在字符串 str1 中查找子字符串 str2。如果找到了子字符串,我们将打印出该子字符串的位置,否则打印 “Not found”。

需要注意的是,strstr 函数是区分大小写的,如果需要进行大小写不敏感的子字符串查找,请使用 strcasestr 函数。

模拟实现
#include 
#include 

char* my_strstr(const char* str1, const char* str2) {
    int len1 = strlen(str1);
    int len2 = strlen(str2);

    if (len2 > len1) {
        return NULL;
    }

    for (int i = 0; i <= len1 - len2; i++) {
        int j;
        for (j = 0; j < len2; j++) {
            if (str1[i+j] != str2[j]) {
                break;
            }
        }
        if (j == len2) {
            return (char*)str1 + i;
        }
    }

    return NULL;
}

int main() {
    const char* str1 = "Hello, world!";
    const char* str2 = "world";

    char* result = my_strstr(str1, str2);

    if (result != NULL) {
        printf("Found: %s\n", result);
    } else {
        printf("Not found\n");
    }

    return 0;
}

在这个示例中,我们定义了一个名为 my_strstr 的函数来模拟实现 strstr 函数的功能。它的实现原理是通过循环遍历源字符串,逐个与子字符串进行比较,如果找到了匹配的子字符串,就返回其位置的指针。

strstr 函数的主要用途是在一个字符串中查找指定的子字符串。它可以用于字符串匹配和子字符串定位等场景。比如,可以用它来判断一个字符串是否包含特定的关键字。

在使用 strstr 函数时,你需要注意以下几点:

  • 如果要查找的子字符串是空字符串(即 ""),它将永远被认为是在源字符串的起始位置,即无论源字符串是什么都会返回它的指针。
  • strstr 函数是区分大小写的,如果需要进行大小写不敏感的子字符串查找,请使用 strcasestr 函数或自行实现不区分大小写的比较。
  • 确保源字符串和子字符串都是以 '\0' 结尾的有效字符串,否则可能导致访问越界或未定义行为。

strtok

strtok 是 C 语言中用于**将字符串拆分成片段的函数。它可以将一个字符串按照指定的分隔符进行拆分,每次返回一个片段,并且在后续调用时会继续返回下一个片段。**它的函数原型如下:

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

参数说明:

  • str:要拆分的字符串,首次调用时需要传入非空字符串,后续调用可以传入 NULL
  • delim:分隔符字符串,用于指定拆分字符串的分隔符。

函数返回值:

  • 如果找到下一个片段,则返回指向该片段的指针。
  • 如果已经将字符串全部拆分完毕,则返回 NULL
示例
#include 
#include 

int main() {
    char str[] = "Hello,world,how,are,you,today";
    const char delim[] = ",";

    char* token = strtok(str, delim);
    while (token != NULL) {
        printf("%s\n", token);
        token = strtok(NULL, delim);
    }

    return 0;
}

在这个示例中,我们将字符串 str 使用逗号 , 作为分隔符进行拆分。每次调用 strtok 函数,它会返回一个拆分后的片段,并将原字符串中的分隔符替换为 '\0',这样我们就可以通过访问指针来获取拆分后的片段。在后续调用中,我们将 NULL 作为第一个参数传递,以继续拆分下一个片段,直到全部拆分完成。

需要注意的是,由于 strtok 函数是修改原始字符串的,所以拆分过程中请确保原字符串的可变性。另外,如果需要同时处理多个字符串的拆分,可以使用不同的指针来调用 strtok 函数。

模拟实现
#include 
#include 

char* my_strtok(char* str, const char* delim) {
    static char* token;

    if (str != NULL) {
        token = str;
    }
    else if (token == NULL) {
        return NULL;
    }

    token += strspn(token, delim);
    if (*token == '\0') {
        return NULL;
    }

    char* ret = token;
    token += strcspn(token, delim);
    if (*token != '\0') {
        *token++ = '\0';
    }

    return ret;
}

int main() {
    char str[] = "Hello,world,how,are,you,today";
    const char delim[] = ",";

    char* token = my_strtok(str, delim);
    while (token != NULL) {
        printf("%s\n", token);
        token = my_strtok(NULL, delim);
    }

    return 0;
}

用途:

  • strtok 函数常常用于将一个字符串拆分成多个子字符串,并用这些子字符串做相应的处理。例如,在解析 CSV 文件时,可以使用 strtok 函数将每一行的数据拆分成各个字段。
  • strtok 也可以用于将一行文本拆分成若干个单词,或者将一个字符串按照空格拆分成多个部分等等。

注意事项:

  • strtok 函数是不可重入的,也就是说,在多线程环境中尽量避免使用该函数,如果必须使用,可以考虑线程安全的版本 strtok_r
  • strtok 函数修改了原始字符串,它会将分隔符替换为 '\0'。如果需要保留原始字符串,可以事先将原始字符串复制到另一个缓冲区中。
  • 在使用 strtok 函数时,需要注意分隔符的顺序和连续出现的情况。如果分隔符连续出现,则只有一个分隔符会被处理,多余的分隔符会被忽略。
  • 如果字符串中有连续的分隔符,则会返回空字符串(即长度为 0 的字符串)。
  • strtok 函数的最后一次调用应当传递 NULL 作为第一个参数,以确保将所有的子字符串都拆分出来。

错误信息报告

strerror

strerror 是一个标准库函数,用于将错误码转换成对应的错误信息字符串。它的声明在头文件 string.h 中。

函数原型如下:

char *strerror(int errnum);

参数 errnum 是一个整数,代表错误码。函数会返回一个指向错误信息字符串的指针。

下面是一个简单的示例:

#include 
#include 
#include 

int main() {
    FILE *file = fopen("nonexistent_file.txt", "r");
    if (file == NULL) {
        int errno_copy = errno;
        printf("Error: %s\n", strerror(errno_copy));
    } else {
        // 文件打开成功,继续处理
    }
    
    return 0;
}

在上面的示例中,如果打开文件失败,errno 变量会被设置为相应的错误码,使用 strerror 函数可以将错误码转换成对应的错误信息并进行打印。

strerror 函数根据不同的平台和操作系统会返回不同的错误信息,所以它非常有用来帮助调试和理解程序中发生的错误。

需要注意的是,strerror 函数在多线程环境中不是线程安全的,如果在多线程中使用,应当使用带有线程安全保证的函数(例如 strerror_r)。

内存操作函数

memcpy

memcpy 是 C 语言中的一个标准库函数,它用于将一块内存中的数据拷贝到另一块内存中。memcpy 的声明在头文件 string.h 中。

函数原型如下:

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

参数 dest 是目标内存的起始地址,注意它的类型是 void *,可以是任意类型的指针。参数 src 是源内存的起始地址,也是 void * 类型。参数 n 表示要拷贝的字节数。

以下是一个简单的示例代码:

#include 
#include 

int main() {
    char src[] = "Hello";
    char dest[10];
    
    memcpy(dest, src, strlen(src) + 1);
    
    printf("Source: %s\n", src);
    printf("Destination: %s\n", dest);
    
    return 0;
}

在上述示例中,我们将字符串 src 中的内容拷贝到了数组 dest 中。memcpy 函数的第三个参数 n 设置了要拷贝的字节数,这里使用了 strlen(src) + 1 表示包括字符串末尾的空字符 '\0'

需要注意的是,memcpy 函数只是简单地将指定的字节数从源内存拷贝到目标内存,它不会检查目标内存是否有足够的空间可以接受拷贝的字节。因此,使用 memcpy 时应确保目标内存的大小大于或等于要拷贝的字节数,以避免发生内存越界的情况。

模拟实现
void *memcpy(void *dest, const void *src, size_t n) {
    // 将源指针和目标指针转换为 unsigned char * 类型,以字节为单位进行操作
    unsigned char *d = (unsigned char *) dest;
    const unsigned char *s = (const unsigned char *) src;
    
    // 逐个字节复制数据
    for (size_t i = 0; i < n; i++) {
        d[i] = s[i];
    }
    
    return dest;
}

上述代码中,我们使用循环逐个字节地将源内存区域的数据复制到目标内存区域。需要注意的是,我们将指针类型转换为 unsigned char *,这样可以确保以字节为单位进行复制。


memmove

memmove 是 C 语言中的一个标准库函数,它类似于 memcpy ,用于在内存中拷贝数据。memmove 的声明也在头文件 string.h 中。

函数原型如下:

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

参数 dest 是目标内存的起始地址,注意它的类型是 void *,可以是任意类型的指针。参数 src 是源内存的起始地址,也是 void * 类型。参数 n 表示要拷贝的字节数。

memcpy 不同的是,memmove 能够正确处理源内存和目标内存发生重叠的情况。当源内存和目标内存有重叠时,memmove 会使用一种更加安全的策略,确保拷贝的数据正确无误。

以下是一个简单的示例代码,演示了 memmove 的用法:

#include 
#include 

int main() {
    char str[] = "Hello, World!";
    
    // 将 "Hello" 移动到字符串的末尾
    memmove(str + 7, str, 5);
    
    printf("Result: %s\n", str);

    return 0;
}

在上述示例中,我们将字符串 str 中的前 5 个字符 "Hello" 移动到字符串的末尾,得到了 "World!Hello"

需要注意的是,memmove 函数保证当源内存和目标内存发生重叠时,拷贝的数据不会被破坏,但并不保证能正常工作的时间和空间复杂性。为了避免潜在的问题,建议在重叠的情况下使用 memmove 而不是 memcpy

模拟实现
void *memmove(void *dest, const void *src, size_t n) {
    // 将源指针和目标指针转换为 unsigned char * 类型,以字节为单位进行操作
    unsigned char *d = (unsigned char *) dest;
    const unsigned char *s = (const unsigned char *) src;
    
    // 首先检查源指针和目标指针是否重叠
    if (s < d && d < s + n) {
        // 如果有重叠则从后向前复制数据,以避免数据覆盖
        for (size_t i = n; i > 0; i--) {
            d[i-1] = s[i-1];
        }
    } else {
        // 如果没有重叠则正常复制
        for (size_t i = 0; i < n; i++) {
            d[i] = s[i];
        }
    }
    
    return dest;
}

上述代码中,我们先检查源指针和目标指针是否重叠,如果重叠,则从后向前复制数据,以确保复制过程不会出现数据覆盖的情况。如果没有重叠,则按照正常的顺序进行复制。

使用该模拟实现的 memmove 函数类似于标准库中的 memmove 函数,例如:

#include 
#include 

void *memmove(void *dest, const void *src, size_t n) {
    // 模拟实现...
}

int main() {
    char str[] = "Hello, World!";
    memmove(str + 7, str, strlen(str) + 1);
    printf("Moved string: %s\n", str);
    
    return 0;
}

上述示例中,我们使用自己实现的 memmove 函数将字符串的一部分移动到另一部分,并打印出移动后的字符串。

需要注意的是,这只是一个简单的模拟实现,可能没有对各种边界情况和错误进行充分处理。在实际开发中,建议使用标准库中的 memmove 函数,因为它已经经过充分的测试和优化。


memset

memset 是 C 语言中的一个标准库函数,它用于将一块内存中的值设置为指定的字符或字节。memset 的声明在头文件 string.h 中。

函数原型如下:

void *memset(void *dest, int value, size_t n);

参数 dest 是目标内存的起始地址,注意它的类型是 void *,可以是任意类型的指针。参数 value 是要设置的值,它以整数形式表示,但会被转换为 unsigned char 类型进行填充。参数 n 表示要设置的字节数。

以下是一个简单的示例代码:

#include 
#include 

int main() {
    char str[10] = "Hello";
    
    printf("Before memset: %s\n", str);
    
    // 使用字符 'x' 来填充字符串
    memset(str, 'x', 5);
    
    printf("After memset: %s\n", str);
    
    return 0;
}

在上述示例中,我们将字符串 str 的前 5 个字符使用字符 'x' 进行填充,得到了 ”xxxxx“

需要注意的是,memset 函数在填充内存时是以字节为单位进行的,因此对于非字符类型的数组,填充的内容可能看起来是乱码。如果需要填充的是字节 0,可以使用 memset 进行初始化,例如:

int array[5];
memset(array, 0, sizeof(array));

上述示例使用 memset 将整型数组 array 中的所有元素设置为 0


memcmp

memcmp 是 C 语言中的一个标准库函数,用于比较两块内存区域的内容。memcmp 的声明在头文件 string.h 中。

函数原型如下:

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

参数 ptr1ptr2 分别表示要比较的两个内存区域的起始地址,类型为 void *,可以是任意类型的指针。参数 n 表示要比较的字节数。

memcmp 函数会按字节逐个比较两个内存区域中的内容,并根据比较结果返回一个整数值:

  • ptr1ptr2 相等(每个字节都相等),则返回 0。
  • ptr1 大于 ptr2(首个不相等的字节中 ptr1 的值大于 ptr2 的值),则返回正整数。
  • ptr1 小于 ptr2 (首个不相等的字节中 ptr1 的值小于 ptr2 的值),则返回负整数。

以下是一个简单的示例代码:

#include 
#include 

int main() {
    char str1[] = "Hello";
    char str2[] = "World";
    
    int result = memcmp(str1, str2, 5);
    
    if (result == 0) {
        printf("The two strings are equal.\n");
    } else if (result > 0) {
        printf("str1 is greater than str2.\n");
    } else {
        printf("str1 is less than str2.\n");
    }
    
    return 0;
}

在上述示例中,我们比较了字符串 str1str2 的前 5 个字符,结果是 str1 小于 str2,因此输出为 “str1 is less than str2.”

memcmp 函数常用于比较字符串、数组或结构体等内存区域的内容是否一致。需要注意的是,memcmp 只比较字节的值,不考虑字符编码或类型的含义。

你可能感兴趣的:(C语言,c语言,开发语言)