🦄个人主页:小米里的大麦-CSDN博客
🎏所属专栏:https://blog.csdn.net/huangcancan666/category_12718530.html
🎁代码托管:C语言: C语言方向(基础知识和应用) (gitee.com)
⚙️操作环境:Visual Studio 2022
目录
5. 模拟实现(strlen模拟(完整版).c · 黄灿灿/C语言 - Gitee.com)
5. 模拟实现(strcpy实现.c · 黄灿灿/C语言 - Gitee.com)
7. 更多使用strncpy (strncpy - C++ Reference)
6. 更多使用strncat(strncat - C++ Reference)
七、strerror(【C语言】处理文件错误:详解 strerror, perror, 和 fopen-CSDN博客)
一、引言
在编程的世界里,字符与字符串是构建软件应用的基本元素之一。无论是简单的命令行工具还是复杂的图形用户界面程序,都需要处理文本数据。特别是在 C 语言中,虽然它是一种强大且灵活的语言,但其本身并未提供专门的字符串类型。相反,C 语言中的字符串通常被表示为字符数组或字符串常量,这为开发者带来了更大的自由度,同时也增加了处理字符串时的复杂性和潜在的错误。
本篇文章旨在深入探讨 C 语言中用于处理字符和字符串的各种库函数,包括它们的用法、注意事项以及一些实用技巧。我们将从基本概念入手,逐步深入到具体的函数实现细节,并通过实际例子来说明如何高效地利用这些函数来简化字符串操作任务。无论你是刚刚接触 C 语言的新手,还是希望提升自己技能水平的有经验的开发者,都能从中获得有价值的见解。
内容一览
求字符串长度
- strlen
长度不受限制的字符串函数
- strcpy
- strcat
- strcmp
长度受限制的字符串函数介绍
- strncpy
- strncat
- strncmp
字符串查找
- strstr
- strtok
错误信息报告
- strerror
字符操作
内存操作函数
- memcpy
- memmove
- memset
- memcmp
一、strlen
1. 函数介绍
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 语言标准库中的一个函数,用于将一个字符串复制到另一个字符串中。这里的“字符串”是指以空字符\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 语言标准库中的一个函数,用于将一个字符串连接到另一个字符串的末尾。这里的“字符串”是指以空字符\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 语言标准库中的一个函数,用于比较两个字符串。这里的“字符串”是指以空字符\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 值进行的。对于两个字符串
str1
和str2
,strcmp
会逐个字符地比较,直到找到第一个不匹配的字符或到达一个字符串的终止符。如果所有字符都相同,且两个字符串长度相同,则认为这两个字符串相等。
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 语言标准库中的一个函数,用于在一个字符串中查找子字符串首次出现的位置。这里的“字符串”是指以空字符\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 语言标准库中的一个函数,用于将字符串分割成多个子字符串(通常称为“标记”或“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_r
是strtok
的线程安全版本,它需要额外的一个参数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 语言标准库中的一个函数,用于将整型错误代码转换为描述该错误的人类可读字符串。这对于调试和错误报告非常有用,因为它可以帮助开发者理解发生了什么类型的错误。
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_r
是strerror
的线程安全版本,它可以接受一个缓冲区和一个缓冲区的大小,以确保不会发生缓冲区溢出。
函数原型
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 语言标准库中的一个函数,用于在内存区域之间进行数据的复制。它通常被用来快速地将一个内存块的内容复制到另一个内存块中。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 语言标准库中的一个函数,用于处理内存区域重叠的情况下的数据复制。它在复制内存时能够正确处理源和目标内存区域可能重叠的情况,这是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 语言标准库中的一个函数,用于在内存块中设置一定数量的字节为特定值。这个函数通常被用来初始化或清除一段连续的内存区域。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 语言标准库中的一个函数,用于比较两个内存块的内容。与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语言的学习之旅上提供有价值的指导和支持。继续探索,不断实践,相信你会在编程的世界里走得更远!制作不易,还请三连支持!