【C语言】字符函数和字符串函数详解

news2024/9/24 5:26:07

🦄个人主页:小米里的大麦-CSDN博客

🎏所属专栏:https://blog.csdn.net/huangcancan666/category_12718530.html

🎁代码托管:黄灿灿 (huang-cancan-xbc) - Gitee.com

⚙️操作环境:Visual Studio 2022

目录

一、引言

内容一览

一、strlen

1. 函数介绍

2. 函数原型

参数

返回值

3. 注意事项

4. 代码示例

5. 模拟实现(strlen模拟(完整版).c · 黄灿灿/C语言 - Gitee.com)

6. 性能注意事项

二、strcpy

1. 函数介绍

2. 函数原型

参数

返回值

3. 注意事项

4. 代码示例

5. 模拟实现(strcpy实现.c · 黄灿灿/C语言 - Gitee.com)

6. 性能注意事项

7. 更多使用strncpy (strncpy - C++ Reference)

三、strcat

1. 函数介绍

2. 函数原型

参数

返回值

3. 注意事项

4. 代码示例

5. 性能注意事项

6. 更多使用strncat(strncat - C++ Reference)

四、strcmp

1. 函数介绍

2. 函数原型

参数

返回值

字典顺序

3. 代码示例

4. 特殊情况

5. 性能注意事项

6. 其他比较函数

五、strstr

1. 函数介绍

2. 函数原型

参数

返回值

3. 注意事项

4. 代码示例

5. 性能注意事项

6. 其他搜索函数

六、strtok

1. 函数介绍

2. 函数原型

参数

返回值

3. 使用方法

4. 代码示例

5. 注意事项

6. strtok_r 函数

七、strerror(【C语言】处理文件错误:详解 strerror, perror, 和 fopen-CSDN博客)

1. 函数介绍

2. 函数原型

参数

返回值

3. 注意事项

4. strerror_r 函数

函数原型

参数

返回值

八、memcpy

1. 函数介绍

2. 函数原型

参数说明

返回值

3. 代码示例

4. 注意事项

5. 性能考虑

九、memmove

1. 函数介绍

2. 函数原型

参数说明

返回值

使用场景

3. 代码示例

4. 注意事项

十、memset

1. 函数介绍

2. 函数原型:

参数说明:

返回值:

3. 代码示例

4. 注意事项:

十一、memcmp

1. 函数介绍

2. 函数原型

参数

返回值

3. 注意事项

4. 代码示例

5. 性能注意事项

6. 其他比较函数

十二、总结

共勉


一、引言

在编程的世界里,字符与字符串是构建软件应用的基本元素之一。无论是简单的命令行工具还是复杂的图形用户界面程序,都需要处理文本数据。特别是在 C 语言中,虽然它是一种强大且灵活的语言,但其本身并未提供专门的字符串类型。相反,C 语言中的字符串通常被表示为字符数组或字符串常量,这为开发者带来了更大的自由度,同时也增加了处理字符串时的复杂性和潜在的错误。

本篇文章旨在深入探讨 C 语言中用于处理字符和字符串的各种库函数,包括它们的用法、注意事项以及一些实用技巧。我们将从基本概念入手,逐步深入到具体的函数实现细节,并通过实际例子来说明如何高效地利用这些函数来简化字符串操作任务。无论你是刚刚接触 C 语言的新手,还是希望提升自己技能水平的有经验的开发者,都能从中获得有价值的见解。

内容一览

求字符串长度

  • strlen

长度不受限制的字符串函数

  • strcpy
  • strcat
  • strcmp

长度受限制的字符串函数介绍

  • strncpy
  • strncat
  • strncmp

字符串查找

  • strstr
  • strtok

错误信息报告

  • strerror

字符操作

内存操作函数

  • memcpy
  • memmove
  • memset
  • memcmp

一、strlen

1. 函数介绍

strlen - C++ Reference的介绍:

strlen 函数是 C 语言标准库中的一个函数,用于计算字符串的长度。这里的“字符串”是指以空字符 \0 结尾的一系列字符。strlen 函数不包括这个终止符('\0')在内计算长度。

2. 函数原型

strlen 函数的声明可以在 <string.h> 头文件中找到:

#include <string.h>

具体的原型如下:

size_t strlen(const char *str);

参数

str: 指向一个以空字符结尾的字符串的指针。

返回值

  • 返回从 str 开始直到遇到第一个空字符(\0)之前的字符个数,即字符串的长度。

3. 注意事项

  • 空字符串如果 str 指向的是一个空字符串(只包含一个 \0),strlen 将返回 0。
  • 未终止的字符串如果 str 指向的缓冲区没有以 \0 结尾,则 strlen 会一直读取直到遇到内存中下一个 \0 或者访问到非法内存区域,这可能导致程序崩溃。
  • 不可修改参数传递给 strlen 的字符串不会被修改。

4. 代码示例

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

int main() {
    char str[] = "Hello, world!";
    size_t length;

    length = strlen(str);

    printf("The length of the string is: %zu\n", length);

    return 0;
}

在这个例子中,strlen 计算 "Hello, world!" 的长度为 13(不包括终止符 \0)。
输出将会是:The length of the string is: 13
#include <string.h>
#include <stdio.h>

int main() {
    char str[] = "Hello, World!";
    size_t len = strlen(str);
    printf("ַ%zu\n", len);//'\0'!!! 
}
输出将会是13

5. 模拟实现(strlen模拟(完整版).c · 黄灿灿/C语言 - Gitee.com)

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <assert.h>

//strlen 是求字符串长度的,求出的长度是不可能为负数的
//所以返回类型设置为size_t 也是合情合理的
//typedef unsigned int size_t
//size_t strlen(const char* string);

递归
//int my_strlen(const char* str)
//{
//	assert(str != NULL);
//	if (*str != '\0')
//		return 1 + my_strlen(str + 1);
//	else
//		return 0;
//}
//
指针-指针
//int my_strlen(const char* str)
//{
//	const char* start = str;
//	assert(str != NULL);
//	while (*str)
//	{
//		str++;
//	}
//	return str - start;
//}

int my_strlen(const char* str)
{
	assert(str != NULL);
	int count = 0;
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}


int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);
	printf("%d\n", len);

	return 0;
}

6. 性能注意事项

在处理非常大的字符串时,strlen 可能会比较慢,因为它需要遍历整个字符串直到遇到终止符。因此,在性能敏感的应用中,考虑缓存字符串的长度可能是个好主意,尤其是当字符串不会改变时。

二、strcpy

1. 函数介绍

strcpy - C++ Reference的介绍:

strcpy 函数是 C 语言标准库中的一个函数,用于将一个字符串复制到另一个字符串中。这里的“字符串”是指以空字符 \0 结尾的一系列字符。strcpy 函数可以用来安全地复制字符串,但需要注意目标缓冲区的大小。

2. 函数原型

strcpy 函数的声明可以在 <string.h> 头文件中

#include <string.h>

具体的原型如下:

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

参数

  • dest: 指向目标字符串的指针。目标字符串必须有足够的空间来存储源字符串和终止符。
  • src: 指向源字符串的指针。

返回值

  • 返回指向目标字符串 dest 的指针。

3. 注意事项

  • 缓冲区溢出如果目标缓冲区不够大以容纳完整的源字符串加上终止符,那么使用 strcpy 可能会导致缓冲区溢出,这是一种常见的安全漏洞。
  • 空字符串strcpy 可以用来复制空字符串。
  • 终止符strcpy 会复制源字符串的终止符 \0 到目标字符串的末尾。
  • 不可重叠的内存区域strcpy 要求源和目标字符串不能重叠。如果需要复制重叠的内存区域,可以使用 memmove 函数。

4. 代码示例

#include <string.h>//strcpy(目标字符串的地址, 源字符串的地址);
#include <stdio.h>

int main() {

char src[] = "我喜欢编程"; // 这是源字符串,相当于装满苹果的篮子
char dest[20]; // 这是目标字符串,一个空篮子,需要足够大来装下"我喜欢编程"以及结尾的'\0'

strcpy(dest, src); // 调用strcpy函数,将src的内容复制到dest中
 printf("复制的字符串: %s\n", dest);
 
char a[] = "Source String"; // 源字符串
char b[20]; // 目标字符串,需要足够大
strcpy(b, a); // 正确地从a复制到b
printf("复制的字符串: %s\n", b);
}
#include <stdio.h>
#include <string.h>

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

    // 复制字符串
    result = strcpy(dest, src);

    printf("Copied string: %s\n", result);  // 输出结果

    return 0;
}
在这个例子中,strcpy 将 "Hello, world!" 复制到 dest 数组中。
由于 dest 数组足够大,复制操作是安全的。
输出将会是:Copied string: Hello, world!

5. 模拟实现(strcpy实现.c · 黄灿灿/C语言 - Gitee.com

#define _CRT_SECURE_NO_WARNINGS 1

//模拟实现库函数strcpy
#include <stdio.h>

//char* my_strcpy(char* dest, const char* src)
//{
//    char* ret = dest;
//    assert(dest != NULL);
//    assert(src != NULL);
//
//    while ((*dest++ = *src++))
//    {
//        ;
//    }
//    return ret;
//}

my_strcpy(char* arr1, char* arr2)
{
    while (*arr2 != '\0')
    {
        *arr1 = *arr2;
        arr1++;
        arr2++;
    }
    *arr1 = *arr2;
}
int main()
{
    char arr1[20] = "***************";
    char arr2[] = "hello";
    my_strcpy(arr1, arr2);
    printf("%s\n", arr1);
    
    return 0;
}

6. 性能注意事项

strcpy 函数的时间复杂度为 O(n),其中 n 是源字符串的长度(包括终止符)。因此,在处理非常大的字符串时,strcpy 可能会比较慢。此外,还需要确保目标缓冲区足够大以避免缓冲区溢出。

7. 更多使用strncpy (strncpy - C++ Reference)

三、strcat

1. 函数介绍

strcat - C++ Reference的介绍:

strcat 函数是 C 语言标准库中的一个函数,用于将一个字符串连接到另一个字符串的末尾。这里的“字符串”是指以空字符 \0 结尾的一系列字符。strcat 函数可以用来安全地连接两个字符串,但需要注意目标缓冲区的大小

2. 函数原型

strcat 函数的声明可以在 <string.h> 头文件中找到:

#include <string.h>

具体的原型如下:

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

参数

  • dest: 指向目标字符串的指针。目标字符串必须有足够的空间来存储源字符串和终止符。
  • src: 指向源字符串的指针。

返回值

  • 返回指向目标字符串 dest 的指针。

3. 注意事项

  • 缓冲区溢出如果目标缓冲区不够大以容纳完整的源字符串加上终止符,那么使用 strcat 可能会导致缓冲区溢出,这是一种常见的安全漏洞。
  • 终止符strcat 会从源字符串的开始位置复制到目标字符串的末尾,直到遇到终止符 \0。它不会复制终止符本身,而是保留目标字符串的原有终止符。
  • 不可重叠的内存区域strcat 要求源和目标字符串不能重叠。如果需要连接重叠的内存区域,可以先复制其中一个字符串到一个新的位置,然后再进行连接。

4. 代码示例

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

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

    // 连接字符串
    result = strcat(dest, src);

    printf("Concatenated string: %s\n", result);  // 输出结果

    return 0;
}
在这个例子中,strcat 将 "world!" 连接到 "Hello, " 后面。
由于 dest 数组足够大,连接操作是安全的。
输出将会是:Concatenated string: Hello, world!
#include <string.h>
#include <stdio.h>

int main() {
    char str1[20] = "Hello, ";
    char str2[] = "World!";
    strcat(str1, str2);
    printf("Ӻַ: %s\n", str1);
}

5. 性能注意事项

strcat 函数的时间复杂度为 O(n),其中 n 是源字符串的长度。因此,在处理非常大的字符串时,strcat 可能会比较慢。此外,还需要确保目标缓冲区足够大以避免缓冲区溢出

6. 更多使用strncat(strncat - C++ Reference)

四、strcmp

1. 函数介绍

strcmp - C++ Reference的介绍:

strcmp 函数是 C 语言标准库中的一个函数,用于比较两个字符串。这里的“字符串”是指以空字符 \0 结尾的一系列字符。strcmp 函数可以用来判断两个字符串是否相等,或者它们之间的字典顺序关系。

2. 函数原型

strcmp 函数的声明可以在 <string.h> 头文件中找到:

#include <string.h>

具体的原型如下:

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

参数

  • str1: 指向第一个字符串的指针。
  • str2: 指向第二个字符串的指针。

返回值

  • 如果 str1 和 str2 相等,则返回 0。
  • 如果 str1 在字典顺序上小于 str2,则返回负数。
  • 如果 str1 在字典顺序上大于 str2,则返回正数。

字典顺序

字典顺序比较是基于 ASCII 值进行的。对于两个字符串 str1str2strcmp 会逐个字符地比较,直到找到第一个不匹配的字符或到达一个字符串的终止符。如果所有字符都相同,且两个字符串长度相同,则认为这两个字符串相等。

3. 代码示例

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

int main() {
    char str1[] = "Hello";
    char str2[] = "hello";
    int result;

    // 比较字符串
    result = strcmp(str1, str2);

    if (result == 0) {
        printf("Strings are equal.\n");
    } else if (result < 0) {
        printf("String '%s' is less than '%s'.\n", str1, str2);
    } else {
        printf("String '%s' is greater than '%s'.\n", str1, str2);
    }

    return 0;
}
在这个例子中,strcmp 比较 "Hello" 和 "hello"。
由于 'H' 的 ASCII 值小于 'h' 的 ASCII 值,
输出将会是:String 'Hello' is less than 'hello'.
#include <string.h>
#include <stdio.h>

int main() {
    char str1[] = "apple";
    char str2[] = "banana";
    int result = strcmp(str1, str2);
    if (result < 0) printf("str1 С str2\n");
    else if (result > 0) printf("str1  str2\n");
    else printf("str1  str2\n");
}

4. 特殊情况

  • 如果两个字符串完全相同,strcmp 返回 0。
  • 如果两个字符串中有一个是空字符串(只包含一个 \0),则非空字符串在字典顺序上更大。
  • 如果两个字符串在某个点之后都是空字符串,那么 strcmp 会比较到第一个不匹配的字符为止。

5. 性能注意事项

strcmp 函数的时间复杂度为 O(n),其中 n 是较短字符串的长度(包括终止符)。因此,在处理非常大的字符串时,strcmp 可能会比较慢。

6. 其他比较函数

除了 strcmp,还有其他几个相关的字符串比较函数,如:

  • strncmp(常用strncmp - C++ Reference: 比较字符串的前 n 个字符。
  • strcasecmp (或 _stricmp): 不区分大小写地比较字符串。请注意,strcasecmp 并不是标准 C 库的一部分,但在一些平台上可用。

五、strstr

1. 函数介绍

strstr - C++ Reference的介绍:

strstr 函数是 C 语言标准库中的一个函数,用于在一个字符串中查找子字符串首次出现的位置。这里的“字符串”是指以空字符 \0 结尾的一系列字符。strstr 函数返回指向子字符串首次出现位置的指针,如果没有找到则返回 NULL

2. 函数原型

strstr 函数的声明可以在 <string.h> 头文件中找到:

#include <string.h>

具体的原型如下:

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

参数

  • haystack: 指向主字符串的指针,也称为“被搜索字符串”。
  • needle: 指向子字符串的指针,也称为“搜索字符串”。

返回值

  • 如果在 haystack 中找到了 needle,则返回指向 haystack 中 needle 首次出现位置的指针。
  • 如果没有找到 needle,则返回 NULL

3. 注意事项

  • 空字符串如果 needle 是一个空字符串(只包含一个 \0),strstr 会返回指向 haystack 的指针,因为每个字符串都可以被认为是包含一个空字符串。
  • 子字符串needle 必须是 haystack 的一个连续子序列,即 haystack 中连续出现的字符序列。
  • 多次出现strstr 只返回首次出现的位置,即使 needle 在 haystack 中多次出现。
  • 字符串边界strstr 在 haystack 的终止符之前停止搜索。

4. 代码示例

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

int main() {
    char haystack[] = "This is a test string.";
    char needle[] = "test";
    char *found;

    // 查找子字符串
    found = strstr(haystack, needle);

    if (found != NULL) {
        printf("Found '%s' at position: %lu\n", needle, found - haystack);
    } else {
        printf("Could not find '%s'.\n", needle);
    }

    return 0;
}
在这个例子中,strstr 查找 "test" 在 "This is a test string." 中的位置。
输出将会是:Found 'test' at position: 10

这里 found - haystack 计算了 needle 在 haystack 中的位置。注意,这里的索引是从 0 开始的。
#include<stdio.h>
#include<string.h>
 
//strstr函数的应用
int main()
{
	char arr1[] = "abbbcdef";
	char arr2[] = "bbc";
 
	printf("%s\n", strstr(arr1, arr2));
 
	return 0;
}

5. 性能注意事项

strstr 函数的时间复杂度为 O(m*n),其中 m 是 haystack 的长度,n 是 needle 的长度。因此,在处理非常大的字符串时,strstr 可能会比较慢。此外,如果 needle 很长而 haystack 很短,或者 needle 根本不存在于 haystack 中,效率可能会更低。

6. 其他搜索函数

除了 strstr,还有一些其他的字符串搜索函数,如:

  • strchr: 查找单个字符首次出现的位置。
  • strrchr: 查找单个字符最后一次出现的位置。
  • strpbrk: 查找任何给定字符集中的字符首次出现的位置。
  • strcasestr: 不区分大小写地查找子字符串首次出现的位置。请注意,strcasestr 并不是标准 C 库的一部分,但在一些平台上可用。

六、strtok

1. 函数介绍

strtok - C++ Reference的介绍:

strtok 函数是 C 语言标准库中的一个函数,用于将字符串分割成多个子字符串(通常称为“标记”或“tokens”)。这些子字符串是由指定的分隔符界定的。strtok 函数可以用来解析命令行参数、配置文件等场景中的字符串数据。

2. 函数原型

strtok 函数的声明可以在 <string.h> 头文件中找到:

#include <string.h>

具体的原型如下:

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

还有一个重载版本,用于继续分割同一个字符串:

char *strtok_r(char *str, const char *delim, char **save_ptr);

参数

  • str: 指向要分割的字符串的指针。
  • delim: 指向包含分隔符的字符串的指针。分隔符可以是一个或多个字符。

返回值

  • 第一次调用 strtok 时,返回指向字符串中的第一个标记的指针。
  • 后续调用 strtok 时,返回指向下一个标记的指针。
  • 如果没有更多的标记可返回,strtok 返回 NULL

3. 使用方法

strtok 函数通常需要多次调用来分割一个字符串。第一次调用时,你需要提供要分割的字符串和分隔符;之后的每次调用只需要提供分隔符即可。

4. 代码示例

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

int main() {
    char str[] = "This is a test string.";
    char *token;
    const char delimiters[] = " ";

    // 分割字符串
    token = strtok(str, delimiters);

    while (token != NULL) {
        printf("%s\n", token);
        token = strtok(NULL, delimiters);
    }

    return 0;
}

在这个例子中,strtok 使用空格作为分隔符来分割 "This is a test string."。
输出将会是:
This
is
a
test
string.
int main()
{
	//char arr[] = "12345@yeah.net";//"@."
	char arr[] = "192#168.120.85";
	char* p = "#.";
	char buf[20] = { 0 };//"12345\0yeah\0net"
	strcpy(buf, arr);
	char* ret = NULL;
	for (ret = strtok(buf, p); ret != NULL; ret=strtok(NULL, p))
	{
		printf("%s\n", ret);
	}
	
	//char* ret = strtok(buf, p);
	//printf("%s\n", ret);
	//ret = strtok(NULL, p);
	//printf("%s\n", ret);
	//ret = strtok(NULL, p);
	//printf("%s\n", ret);

	//zpengwei
	//yeah
	//net
	return 0;
}

5. 注意事项

  • 修改原字符串strtok 函数会修改原始字符串,将分隔符替换为空字符,以便后续调用能够找到下一个标记。
  • 状态保存strtok 会在内部保存状态,以便在多次调用之间知道当前位置。
  • 多线程安全:由于 strtok 保存状态,它不是线程安全的。如果你在多线程环境中使用 strtok,应该使用 strtok_r 函数,它是线程安全的。
  • 分隔符:分隔符可以包含多个字符。例如,你可以使用 " ,;" 作为分隔符来同时分割逗号、空格和分号。
  • 空字符串:如果输入字符串是空字符串,strtok 会立即返回 NULL

6. strtok_r 函数

strtok_rstrtok 的线程安全版本,它需要额外的一个参数 save_ptr 用于保存状态。save_ptr 是一个指向 char * 的指针,用于跟踪当前的分割位置。

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

int main() {
    char str[] = "This is a test string.";
    char *token;
    const char delimiters[] = " ";
    char *save_ptr;

    // 分割字符串
    token = strtok_r(str, delimiters, &save_ptr);

    while (token != NULL) {
        printf("%s\n", token);
        token = strtok_r(NULL, delimiters, &save_ptr);
    }

    return 0;
}
在这个例子中,我们使用 strtok_r 替代 strtok,并传递了一个 save_ptr 来保存状态。
输出与使用 strtok 相同。

七、strerror(【C语言】处理文件错误:详解 strerror, perror, 和 fopen-CSDN博客

1. 函数介绍

strerror - C++ Reference的介绍:

strerror 函数是 C 语言标准库中的一个函数,用于将整型错误代码转换为描述该错误的人类可读字符串。这对于调试和错误报告非常有用,因为它可以帮助开发者理解发生了什么类型的错误。

2. 函数原型

strerror 函数的声明可以在 <string.h><errno.h> 头文件中找到:

#include <string.h>
#include <errno.h>

具体的原型如下:

const char *strerror(int errnum);

参数

  • errnum: 一个整型值,表示错误代码。

返回值

  • 返回一个指向描述错误的字符串的指针。

3. 注意事项

  • 错误代码:错误代码通常由系统调用失败时设置的全局变量 errno 提供。你可以通过检查 errno 的值来确定发生了哪种类型的错误。
  • 国际化strerror 函数可能支持国际化,这意味着返回的错误消息可以根据当前的语言环境而变化。
  • 线程安全性:在某些实现中,strerror 可能不是线程安全的,因为它可能修改静态数据。在这种情况下,你可以使用 strerror_r 函数来获得线程安全的行为。
  • 错误消息strerror 返回的字符串通常是只读的,不应该尝试修改。

4. 代码示例

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

int main() {
    int errnum = 0;
    const char *error_msg;

    // 假设某个系统调用失败并设置了 errno
    if (access("nonexistentfile.txt", F_OK) == -1) {
        errnum = errno;
    }

    // 获取错误消息
    error_msg = strerror(errnum);

    printf("Error code: %d, Message: %s\n", errnum, error_msg);

    return 0;
}
在这个例子中,我们尝试使用 access 函数检查文件是否存在。
如果文件不存在,access 函数会返回 -1 并设置 errno。
然后我们使用 strerror 函数获取错误消息并打印出来。


假设文件确实不存在,输出将会类似于:Error code: 2, Message: No such file or directory

这里的错误代码 2 对应于 ENOENT 错误,表示“没有这样的文件或目录”。

4. strerror_r 函数

strerror_rstrerror 的线程安全版本,它可以接受一个缓冲区和一个缓冲区的大小,以确保不会发生缓冲区溢出。

函数原型

char *strerror_r(int errnum, char *buf, size_t buflen);

参数

  • errnum: 错误代码。
  • buf: 指向缓冲区的指针,用于存放错误消息。
  • buflen: 缓冲区的大小。

返回值

  • 返回指向缓冲区 buf 的指针。
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

int main() {
    int errnum = 0;
    char error_buf[100];

    // 假设某个系统调用失败并设置了 errno
    if (access("nonexistentfile.txt", F_OK) == -1) {
        errnum = errno;
    }

    // 获取错误消息
    strerror_r(errnum, error_buf, sizeof(error_buf));

    printf("Error code: %d, Message: %s\n", errnum, error_buf);

    return 0;
}
在这个例子中,我们使用 strerror_r 替代 strerror,并提供了一个缓冲区 error_buf 来存放错误消息。
输出与使用 strerror 相同。

八、memcpy

1. 函数介绍

memcpy - C++ Reference的介绍:

memcpy 是 C 语言标准库中的一个函数,用于在内存区域之间进行数据的复制。它通常被用来快速地将一个内存块的内容复制到另一个内存块中。memcpy 函数定义在 <string.h> 头文件中。memcpy 它的主要工作是从一个地方复制一些数据到另一个地方。想象一下你有一堆卡片,你想把这些卡片从一个盒子复制到另一个盒子里。)

2. 函数原型

memcpy 的原型如下:

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

参数说明

  • dest: 目标内存块的起始地址。目标盒子:你要把卡片放到哪个盒子里。)
  • src: 源内存块的起始地址。来源盒子:你要从哪个盒子里拿卡片。)
  • n: 要复制的字节数。卡片的数量:你要复制多少张卡片。)

返回值

memcpy 函数返回目标内存块的起始地址(即 dest 的值)。

3. 代码示例

假设我们有两个字符串,一个叫做 source(来源),一个叫做 dest(目标)。
我们要把 source 中的一些字符复制到 dest 中。

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

int main() {
    char source[100] = "Hello, World!";
    char dest[100];

    // 清空目标字符串
    memset(dest, 0, sizeof(dest));

    // 复制前五个字符
    memcpy(dest, source, 5);

    printf("Source: %s\n", source);  // 输出: Source: Hello, World!
    printf("Dest:   %s\n", dest);    // 输出: Dest:   Hello

    return 0;
}

在这个例子中:

source 字符串包含了 "Hello, World!"。
dest 字符串开始时为空。
我们只复制了 source 的前五个字符到 dest。
最终 dest 变成了 "Hello"。

4. 注意事项

  • 如果你复制的是字符串,请确保你复制了完整的字符串,包括末尾的空字符(\0)。
  • 如果源字符串和目标字符串有重叠的部分,memcpy 的行为可能会出问题。这种情况下,最好使用 memmove

5. 性能考虑

memcpy 在处理大量数据时通常比循环逐个字节复制更快,因为它可能利用了硬件优化(例如,使用 SIMD 指令)。

九、memmove

1. 函数介绍

memmove - C++ Reference的介绍:

memmove() 是 C 语言标准库中的一个函数,用于处理内存区域重叠的情况下的数据复制。它在复制内存时能够正确处理源和目标内存区域可能重叠的情况,这是 memcpy() 所不能保证的。

2. 函数原型

memmove 的原型定义在 <string.h> 头文件中:

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

参数说明

  • dest: 目标内存起始地址。
  • src: 源内存起始地址。
  • n: 要复制的字节数。

返回值

memmove() 函数返回指向目标内存区域的指针(即 dest)。

使用场景

当源和目标内存区域不重叠时,memmove()memcpy() 的行为相同;但是当它们重叠时,memmove() 会确保正确的复制行为,而 memcpy() 的结果则未定义。

3. 代码示例

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

int main() {
    char str[] = "Hello, World!";
    
    printf("Before memmove: %s\n", str);
    
    // 将 "Hello, " 替换为 "Good "
    memmove(str + 6, str + 13, strlen(str + 13) + 1);  // 移动 "World!" 到 "Hello, " 后面
    memmove(str, "Good ", 5);                          // 将 "Good " 写入到 "Hello, " 的位置
    
    printf("After memmove: %s\n", str);
    
    return 0;
}
如果运行上述代码,输出应该是:
Before memmove: Hello, World!
After memmove: Good World!

4. 注意事项

  • 确保 n 不超过源内存区域的大小,以避免越界访问。
  • 如果 n 为零,则 memmove() 不会对 dest 和 src 所指向的内存做任何操作,并且返回 dest
  • memmove() 可以用来处理字符串,但通常情况下,如果只涉及到字符串的复制或移动,更推荐使用 strncpy() 或者 strncat() 等专门针对字符串的操作函数。

十、memset

1. 函数介绍

memset - C++ Reference的介绍:

memset 是 C 语言标准库中的一个函数,用于在内存块中设置一定数量的字节为特定值。这个函数通常被用来初始化或清除一段连续的内存区域。memset 函数定义在 <cstring>(C++) 或 <string.h>(C)头文件中。

2. 函数原型:

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

参数说明:

  • ptr: 指向要操作的内存块的指针。
  • value: 要设置到每个字节的值。注意这个值会被转换成 unsigned char 类型。
  • num: 要设置的字节数。

返回值:

  • 返回值是指向被填充内存块的原始指针 ptr

3. 代码示例

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

int main() {
    char str[50] = "Hello World!";
    
    // 将 str 中的前 10 个字节设置为 '\0' (即清空这些位置)
    memset(str, 0, 10);

    printf("After memset: %s\n", str);  // 输出可能为空字符串或者部分清空后的结果
    
    // 初始化一个数组的所有元素为 1
    int arr[10];
    memset(arr, 1, sizeof(arr));

    // 打印数组的内容
    for (int i = 0; i < 10; i++) {
        printf("%d ", arr[i]);
    }
    
    return 0;
}

4. 注意事项:

  • 如果 value 不是 0,并且你想要填充的是大于一个字节的数据类(如 int 或 double),那么需要注意,memset 可能会导致未对齐访问,这在某些架构上可能会导致问题。
  • 当填充非零值时,需要确保该值适合目标数据类型。例如,如果填充 int 数组,那么传递给 memset 的 value 应当适合 int 类型的表示范围。

十一、memcmp

1. 函数介绍

memcmp - C++ Reference的介绍:

memcmp 函数是 C 语言标准库中的一个函数,用于比较两个内存块的内容。与 strcmp 不同,memcmp 可以比较任意类型的内存块,而不仅仅是字符串。它逐字节地比较两个内存块,直到达到指定的字节数或找到第一个不匹配的字节为止。

2. 函数原型

memcmp 函数的声明可以在 <string.h> 头文件中找到:

#include <string.h>

具体的原型如下:

int memcmp(const void *s1, const void *s2, size_t n);

参数

  • s1: 指向第一个内存块的指针。
  • s2: 指向第二个内存块的指针。
  • n: 要比较的字节数。

返回值

  • 如果两个内存块完全相同,则返回 0。
  • 如果 s1 在字节顺序上小于 s2,则返回负数。
  • 如果 s1 在字节顺序上大于 s2,则返回正数。

3. 注意事项

  • 字节顺序memcmp 按照字节顺序比较内存块。这意味着它会逐字节地比较,直到找到第一个不同的字节或达到指定的字节数 n
  • 大小n 参数定义了要比较的字节数,而不是内存块的大小。
  • 类型无关memcmp 可以用于比较任何类型的内存块,例如数组、结构体等。
  • 零填充:如果两个内存块中有一个比另一个短,那么超出部分会被视为零填充。

4. 代码示例

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

int main() {
    char s1[] = "Hello";
    char s2[] = "hello";
    int result;

    // 比较两个字符串
    result = memcmp(s1, s2, 5);

    if (result == 0) {
        printf("Memory blocks are equal.\n");
    } else if (result < 0) {
        printf("Memory block '%s' is less than '%s'.\n", s1, s2);
    } else {
        printf("Memory block '%s' is greater than '%s'.\n", s1, s2);
    }

    return 0;
}
在这个例子中,memcmp 比较 "Hello" 和 "hello" 的前五个字节。
由于 'H' 的 ASCII 值小于 'h' 的 ASCII 值,

输出将会是:Memory block 'Hello' is less than 'hello'.

5. 性能注意事项

memcmp 函数的时间复杂度为 O(n),其中 n 是 n 参数的值。因此,在处理非常大的内存块时,memcmp 可能会比较慢。

6. 其他比较函数

除了 memcmp,还有一些相关的内存比较函数,如:

  • memcmp: 比较两个内存块的前 n 个字节。
  • strcmp: 专门用于比较字符串。
  • strncmp: 比较字符串的前 n 个字符。
  • memcmp: 可以用于比较两个浮点数或整数的内存表示。

十二、总结

通过本篇博客,从简单的字符操作到复杂的字符串处理,这些内置函数极大地简化了我们的开发工作,并提高了代码的可读性和效率。掌握这些函数的正确用法,能够帮助我们在编写程序时更加得心应手,无论是进行数据验证、格式化输出还是文本解析等任务都能游刃有余。

最后,虽然标准库提供的函数已经非常丰富,但在实际项目中,我们有时也需要根据具体需求来实现自定义的字符或字符串处理逻辑。这种情况下,理解底层原理变得尤为重要。希望本文能为你在C语言的学习之旅上提供有价值的指导和支持。继续探索,不断实践,相信你会在编程的世界里走得更远!制作不易,还请三连支持!

共勉

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1976324.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一拖三无线充底座-带给你极致的便利生活

随着科技的不断进步&#xff0c;无线充电技术已经逐渐渗透到我们日常生活的方方面面&#xff0c;一拖三无线充底座作为其中的佼佼者&#xff0c;以其高效、便捷的特点受到广大用户的青睐。本文将从电磁感应原理、多线圈设计、频率匹配、电能传输、功率分配以及充电管理六个方面…

【Rust日报】终端表格查看工具

[new ver] Tabiew v0.6.1 Tabiew 是一个轻量级的、基于终端的应用程序&#xff0c;用于查看和查询分隔符分隔值格式的文档&#xff0c;例如 CSV 或 TSV 文件。 功能如下&#xff1a; &#x1f4ca; 表格视图: 通过易于导航的表格视图来探索数据。&#x1f50d; 工作表视图: 深入…

深入剖析隐私安全测试:在数字化时代的守护者

大家好&#xff0c;我是一名_全栈_测试开发工程师&#xff0c;已经开源一套【自动化测试框架】和【测试管理平台】&#xff0c;欢迎大家关注我&#xff0c;和我一起【分享测试知识&#xff0c;交流测试技术&#xff0c;趣聊行业热点】。 一、引言 在日新月异的数字化浪潮中&…

FPGA开发——基于Quartus的工程建立以及下载仿真全过程

一、概述 在使用了这么久的FPGA之后&#xff0c;才发现前面光发了各个设计的设计文件和测试问价&#xff0c;还没有发过怎么利用我们编写的代码进行仿真和使用软件进行工程建立和下载验证的过程&#xff0c;今天就补发一篇Quartus的工程建立以及下载仿真全过程。 二、文件夹建…

程序执行的环境和预处理

目录&#xff1a; 思维导图 一 程序的翻译环境 二 程序的执行环境 三 C 语言程序的编译和链接 四 预定义符号 五 预处理指令 六 宏和函数对比 七 预处理操作符# 和 ## 八 命令定义 九 预处理指令#include 十 预处理指令#undef 十一 条件编译 WeChat_2024073122290…

JavaScript 打印 V 和倒 V 图案的程序(Program to print V and inverted-V pattern)

倒 V 型模式&#xff1a;给定 n 的值&#xff0c;打印倒 V 型模式。示例&#xff1a; 输入&#xff1a;n 5 输出 &#xff1a; E D D C C B B A A 输入&#xff1a;n 7 输出 &#xff1a; G F F E E D D C C B B A…

中药养发护发

按照中医理论,头发和肝肾有密切联系,肝主血,肾藏精, 其华在发,肝肾强健,上荣于头,则毛发乌黑浓密. 中药育发的应用 以当归,天麻,桑疹子养血润发,配合干姜祛风活血,能通畅经络, 加快循环,激活毛囊,能促进皮肤组织营养成分吸收和废弃物的排泄,改善 头发生态. 用苦参 皂角 清热化…

yum仓库的制作与使用

目录 前言&#xff1a; 1 查看系统内核 2 获取网络源 3 搭建yum网络仓库 4 rpm包的下载 4.1 将rpm包下载至本地 4.2 对下载的rpm包进行备份 5 制作本地yum源 5.1 软件仓库制作工具createrepo 5.2 使用createrepo创建本地yum仓库 6 搭建docker本地仓库 前言&#x…

【Dash】使用 HTML 和 CSS 创建图表

一、Styling Your App The examples in the previous section used Dash HTML Components to build a simple app layout, but you can style your app to look more professional. This section will give a brief overview of the multiple tools that you can use to enhan…

Scalable Diffusion Models with Transformers(DIT)代码笔记

完整代码来源&#xff1a;DiT DiT模型主要是在diffusion中&#xff0c;使用transformer模型替换了UNet模型&#xff0c;使用class来控制图像生成。 根据论文&#xff0c;模型越大&#xff0c;patch size 越小&#xff0c;FID越小。 模型越大&#xff0c;参数越多&#xff0c;pa…

3.罗马数字转数字

3.罗马数字转数字 题目题目分析 题目 题目分析 基础思路是先想到键值对&#xff0c;然后遍历字符串利用键值对匹配来计算值&#xff0c;要注意处理IV&#xff0c;IX之类需要将当前处理字符与前一个字符进行比较。 class Solution { public:int romanToInt(string s) {//这个函…

项目负责人视角:结构化思考在竞赛中的力量

项目负责人视角&#xff1a;结构化思考在竞赛中的力量 前言结构化思考的定义与重要性结构化思考的五个关键步骤1. 明确问题2. 问题分解3. 优先级排序4. 制定行动计划5. 执行与监控 结构化思考的实际应用案例结语 前言 在这个充满挑战和机遇的时代&#xff0c;项目管理已成为企业…

Nero Lens 智图 - 适用于 iOS 和 iPadOS 的专业图片处理 App

首先是手机端的无损放大 App&#xff1a;Nero Lens 智图&#xff0c;适用于 iOS 和 iPadOS&#xff0c;不仅可以放大&#xff0c;还有多种 AI 图片增强功能。 使用这款 App 可以通过 AI 模型智能放大可达 400%&#xff0c;还有老照片去划痕、上色&#xff0c;抠图移除背景、照…

数论第四节:不定方程(习题)

文章目录 例1例2例3例4例5 例1 解下列不定方程&#xff1a; ( a ) 15 x 25 y 100 (a)15x25y100 (a)15x25y100 ( b ) 306 x − 360 y 630 (b)306x-360y630 (b)306x−360y630 (a)解: (15,25)5&#xff0c;且5|100&#xff0c;所以方程有解。 方程两边同时除以(15,24)&…

53 SSLVPN IP 接入

(1) 配置接口IP地址 # 根据组网图中规划的信息,配置各接口的IP地址,具体配置步骤如下。 <Device> system-view [Device] interface gigabitethernet 1/2/5/1 [Device-GigabitEthernet1/2/5/1] ip address 1.1.1.2 255.255.255.0 [Device-GigabitEthernet1/2/…

Unity获取Animator动画播放完成事件

整理了一些在日常经验中处理动画播放完成事件的方法 方法: 1.Dotween配合异步实现 2.状态机计时方法实现 3.原生动画行为方法实现 方法一&#xff1a;Dotween异步方法 using UnityEngine; using System.Threading.Tasks; using DG.Tweening;public class PlayerAnimAsync : M…

从Excel高手到SQL大师-解锁数据分析的无限潜力 -10分钟读懂职场必备技能

目录 Excel 和 SQL&#xff1a;看似相似却大不相同的数据处理利器Excel vs SQL&#xff1a;表面相似&#xff0c;本质迥异Excel&#xff1a;直观但受限的电子表格SQL&#xff1a;强大而灵活的数据库查询语言 从 Excel 到 SQL&#xff1a;跨越鸿沟Excel 数据筛选SQL 数据筛选 结…

MySQL:数据库权限与角色

权限 MySQL 的权限管理系统是保障数据库安全性的关键组件之一。它允许数据库管理员精确控制哪些用户可以对哪些数据库对象执行哪些操作。 自主存取控制 DAC&#xff08;DiscretionaryAccess Control)&#xff1a;用户对于不同的数据库对象有不同的存取权限&#xff0c;不同的…

Java并发编程中的FutureTask详解

Java并发编程中的FutureTask详解 1、核心特点2、基本用法2.1 包装 Callable 任务2.2 包装 Runnable 任务 3、注意事项 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; FutureTask 是 Java 并发包 java.util.concurrent 中的一个类&#xff0…

Trimble 电子水准仪数据传输与预处理

0 安装软件。 1 将设备传输线与电脑相连接。 2 运行软件&#xff0c;选择对应的设备&#xff0c;显示连接成功。 3 点击添加&#xff0c;选择工程文件&#xff0c;再点击打开&#xff08;可以选择多个&#xff09;。 4 点击Transfer All&#xff0c;数据会传输到对应路径。 5 查…