目录
1 bcmp
1.1 函数原型
在 C 语言中,bcmp 函数并不是标准 C 库(如 ISO C 标准)的一部分,但它经常在一些系统(如 UNIX 或类 UNIX 系统)的字符串处理库中提供,用于比较内存区域。其原型通常定义在 <strings.h> 头文件中(注意,<string.h> 是处理 C 字符串的标准库,而 <strings.h> 提供了额外的字符串和内存比较函数,但并非所有系统都支持)。bcmp 的函数原型如下:
#include <strings.h>
int bcmp(const void *s1, const void *s2, size_t n);
这里,s1 和 s2 是指向要比较的内存区域的指针,n 是要比较的字节数。
1.2 功能说明
bcmp 函数用于比较两个内存区域的前 n 个字节。这个函数对于检查内存块是否相等非常有用,但它不会告诉你它们在哪里不同。
- 如果两个内存区域完全相同,则返回 0;
- 如果不同,则返回非 0 值(具体值依赖于实现,但通常不关心这个值的具体含义,只关心是否为零)。
1.3 案例演示
以下是一个使用 bcmp 函数的简单示例:
#include <stdio.h> // 引入标准输入输出库
#include <strings.h> // 引入字符串处理库,其中包含 bcmp 函数
int main()
{
char str1[] = "Hello, World!"; // 定义第一个字符串
char str2[] = "Hello, World!"; // 定义第二个字符串,与第一个相同
char str3[] = "Hello, C!"; // 定义第三个字符串,与前两个不同
// 计算字符串长度(包括末尾的结束符)
printf("sizeof(str1)=%zu\n", sizeof(str1)); // 14
// 使用 bcmp 比较 str1 和 str2 的前 sizeof(str1) - 1 个字节
if (bcmp(str1, str2, sizeof(str1) - 1) == 0)
{
printf("str1 and str2 are equal.\n"); // 如果相等,则打印信息
}
else
{
printf("str1 and str2 are not equal.\n"); // 如果不相等,则打印信息
}
// 使用 bcmp 比较 str1 和 str3 的前 sizeof(str1) - 1 个字节
if (bcmp(str1, str3, sizeof(str1) - 1) == 0)
{
printf("str1 and str3 are equal.\n"); // 如果相等,则打印信息
}
else
{
printf("str1 and str3 are not equal.\n"); // 如果不相等,则打印信息
}
return 0; // 返回 0 表示程序正常结束
}
输出结果如下所示:
1.4 注意事项
可移植性问题:由于 bcmp 不是标准 C 库的一部分,因此在使用它时可能会遇到可移植性问题。如果代码需要在不同的系统上运行,最好使用标准 C 库中的函数,如 memcmp,它提供了相同的功能但具有更好的可移植性。
安全性:当使用 bcmp 或 memcmp 时,确保不会读取超出数组或内存块边界的数据,以避免未定义行为。
返回值:虽然 bcmp 返回非 0 值表示两个内存区域不同,但具体值并不重要。不要依赖这个值进行任何逻辑判断,除了检查它是否为零。
性能:对于敏感或性能关键的应用,注意 bcmp(或 memcmp)的性能影响,尤其是在处理大量数据时。在某些情况下,可能需要考虑更高效的比较方法。
2 bcopy
2.1 函数原型
bcopy 函数并不是标准 C 库(如 ISO C 标准)的一部分,但它在一些 UNIX 或类 UNIX 系统中被广泛使用,用于内存块的复制。尽管它不是标准的,但了解它的用法对于理解某些旧代码或特定环境下的编程是有帮助的。其函数原型通常如下(但请注意,在不同的系统或库中可能有所不同):
#include <strings.h>
void bcopy(const void *src, void *dest, size_t n);
这里,src 是指向源内存块的指针,dest 是指向目标内存块的指针,n 是要复制的字节数。
2.2 功能说明
bcopy 函数用于从 src 指向的内存位置复制 n 个字节到 dest 指向的内存位置。它不会检查目标内存区域是否足够大来容纳要复制的字节,因此使用时需要确保 dest 指向的内存区域至少与 n 个字节一样大。
此外,bcopy 不会设置目标内存区域中未被复制的字节的值为零或任何特定值,也就是说:bcopy 只会复制指定数量的字节 (n) 从源内存区域到目标内存区域,目标内存区域中超出这 n 个字节的部分不会被改变。即是说:这些未被复制覆盖的字节将保持它们在调用 bcopy 之前的状态。因此,如果目标内存区域比实际要复制的数据大,那么多余的空间不会被自动初始化为零或其他任何特定值,它们将保留原有内容。
2.3 案例演示
以下是一个使用 bcopy 函数的简单示例:
#include <stdio.h> // 引入标准输入输出库
#include <strings.h> // 引入字符串处理库,其中包含 bcopy 函数
int main()
{
char src[] = "Hello, World!";
char dest[20]; // 确保目标数组足够大
// 使用 bcopy 复制字符串(包括结尾的 '\0')
size_t len = sizeof(src); // 获取 src 的长度,包括结尾的 '\0'
bcopy(src, dest, len); // 复制 len 个字节,包括结尾的 '\0'
// 验证复制结果
printf("源字符串: %s\n", src);
printf("目标字符串: %s\n", dest);
return 0;
}
输出结果如下所示:
调试验证目标内存块中的字符串内容,如下所示:
2.4 注意事项
可移植性:由于 bcopy 不是标准 C 库的一部分,因此在使用它时可能会遇到可移植性问题。如果代码需要在不同的系统上运行,建议使用标准 C 库中的 memcpy 函数。
安全性:使用 bcopy 时,必须确保目标内存区域足够大以容纳要复制的字节数,以避免缓冲区溢出。
内存重叠:与 memcpy 不同,bcopy 的实现通常不处理源和目标内存区域重叠的情况。然而,由于它不是标准函数,具体的行为可能因实现而异。如果需要处理内存重叠的情况,请使用 memmove。
包含头文件:尽管在一些系统中 bcopy 可能在 <string.h> 中声明,但在许多 UNIX 和类 UNIX 系统中,它位于 <strings.h>,请确保包含正确的头文件。
3 bzero
3.1 函数原型
bzero 函数同样不是标准 C 库(如 ISO C标准)的一部分,但在一些 UNIX 或类 UNIX 系统中被广泛使用,用于将内存块清零。其函数原型通常如下:
#include <strings.h>
void bzero(void *s, size_t n);
这里,s 是指向要清零的内存块的指针,n 是要清零的字节数。
3.2 功能说明
bzero 函数将 s 指向的内存区域的前 n 个字节设置为零。这个函数对于初始化内存块非常有用,特别是当想要确保内存区域不包含任何旧数据或垃圾值时。
3.3 案例演示
以下是一个使用 bzero 函数的简单示例:
#include <stdio.h> // 引入标准输入输出库
#include <strings.h> // 引入字符串处理库,其中包含 bzero 函数
int main()
{
// 定义一个字符数组作为内存块
char buffer[50];
// 使用 bzero 将 buffer 的前 20 个字节清零
bzero(buffer, 20);
// 验证 buffer 的前 20 个字节是否已被清零
// 注意:这里我们假设 buffer 的剩余部分可能包含垃圾值,但只检查前 20 个字节
for (int i = 0; i < 20; i++)
{
if (buffer[i] != 0)
{
printf("buffer[%d] 未被清零!\n", i);
}
}
// 由于我们期望所有字节都被清零,所以上面的循环不会打印任何内容
// 但为了演示,我们可以打印一些信息来表明程序已经执行到这一点
printf("buffer 的前 20 个字节已被成功清零。\n");
// 注意:这里打印 buffer 的内容,前 20 个字节都是 0,后面字节内容无法确定
for (int i = 0; i < 50; i++)
{
if (i % 7 == 0)
{
// 换行
printf("\n");
}
// 打印试一试
printf("buffer[%d]:%c\t", i, buffer[i]);
}
return 0;
}
在上述代码中,由于 buffer 的剩余部分(即索引 20 到 49 的部分)没有被显式清零,它们可能包含垃圾值。在实际应用中,如果需要整个数组都被清零,应该将 bzero 的第二个参数设置为数组的总大小。
输出结果如下所示:
调试验证内存块中的字符串内容,如下所示:
3.4 注意事项
可移植性:由于 bzero 不是标准 C 库的一部分,因此在使用它时可能会遇到可移植性问题。如果代码需要在不同的系统上运行,建议使用标准 C 库中的 memset 函数,因为 memset 提供了相同的功能并且是可移植的。
安全性:使用 bzero 时,必须确保目标内存区域足够大以容纳要清零的字节数,以避免越界访问。
性能:在大多数情况下,bzero 和 memset 的性能差异可以忽略不计,但如果在处理大量数据或对性能有严格要求,最好查看编译器和平台上的具体实现。
包含头文件:尽管在一些系统中 bzero 可能在 <string.h> 中声明,但在许多 UNIX 和类 UNIX 系统中,它位于 <strings.h>。确保包含正确的头文件。然而,为了可移植性,推荐使用 <string.h> 中的 memset 函数。
4 memcmp
4.1 函数原型
memcmp 函数是 C 语言标准库中的一个函数,用于比较内存区域。其函数原型定义在 <string.h> 头文件中,具体如下:
#include <string.h>
int memcmp(const void *s1, const void *s2, size_t n);
4.2 功能说明
memcmp 函数比较由 s1 和 s2 指向的前 n 个字节。比较是逐字节进行的,按照无符号字符(unsigned char)的升序进行。
- 如果 s1 和 s2 指向的前 n 个字节完全相同,则函数返回 0;
- 如果 s1 中的第一个不匹配的字节小于 s2 中对应的字节,则返回一个小于 0 的值;
- 如果 s1 中的第一个不匹配的字节大于 s2 中对应的字节,则返回一个大于 0 的值。
4.3 案例演示
以下是一个使用 memcmp 函数的简单示例:
#include <stdio.h> // 引入标准输入输出库
#include <string.h> // 引入字符串处理库,其中包含 memcmp 函数
int main()
{
char str1[] = "Hello, World!";
char str2[] = "Hello, C!";
char str3[] = "Hello, World!";
// 比较 str1 和 str2 的前 7 个字节
int result1 = memcmp(str1, str2, 7);
printf("比较 str1 和 str2 的前 7 个字节: %d\n", result1);
// 应输出 0,因为前 7 个字节相同
// 比较 str1 和 str3 的前 13 个字节
int result2 = memcmp(str1, str3, 13);
printf("比较 str1 和 str3 的前 13 个字节: %d\n", result2);
// 应输出 0,因为完全相同
// 比较 str1 和 str2 的前 13 个字节
int result3 = memcmp(str1, str2, 13);
printf("比较 str1 和 str2 的前 13 个字节: %d\n", result3);
// 应输出一个大于 0 的值,因为从第 8 个字节开始不同,W 大于 C
return 0;
}
输出结果如下所示:
4.4 注意事项
内存对齐和类型安全:memcmp 函数不检查内存对齐或类型安全,它仅按字节进行比较。因此,在比较不同类型的内存区域时要特别小心。
空指针和越界:调用 memcmp 时,应确保 s1 和 s2 不是空指针,并且 n 不会使访问超出 s1 或 s2 指向的内存区域。
性能考虑:虽然 memcmp 在大多数情况下是高效的,但在处理大量数据或性能敏感的应用中,应考虑是否有更优化的比较方法。
返回值的使用:memcmp 的返回值仅表示比较的结果(小于、等于、大于),而不提供具体差异的数量或位置。如果需要这些详细信息,可能需要实现自定义的比较函数。
字符串比较:虽然 memcmp 可以用于比较字符串,但通常建议使用 strcmp 或 strncmp(当只比较字符串的一部分时)来比较以 null 结尾的字符串,因为这些函数能够正确处理字符串的结束符('\0')。
5 memicmp
5.1 函数原型
memicmp 函数不是标准 C 库的一部分,但在某些系统(如 Windows)中提供。它的功能类似于 memcmp,但不区分大小写。函数原型如下:
#include <string.h> // 引入字符串处理库,其中包含 memicmp 函数(如果系统支持)
int memicmp(const void *s1, const void *s2, size_t n);
这里,s1 和 s2 是指向要比较的内存区域的指针,n 是要比较的字节数。
5.2 功能说明
memicmp 函数用于比较两个内存区域的前 n 个字节,但不区分大小写。具体来说:
- 如果两个内存区域的前 n 个字节完全相同(不区分大小写),则返回 0。
- 如果第一个内存区域小于第二个内存区域(不区分大小写),则返回负数。
- 如果第一个内存区域大于第二个内存区域(不区分大小写),则返回正数。
5.3 案例演示
以下是一个使用 memicmp 函数的简单示例:
#include <stdio.h> // 引入标准输入输出库
#include <string.h> // 引入字符串处理库,其中包含 memicmp 函数(如果系统支持)
int main()
{
char str1[] = "HELLO, WORLD!";
char str2[] = "hello, world!";
char str3[] = "Hello, C!";
// 比较 str1 和 str2
int result1 = memicmp(str1, str2, 13);
printf("比较 str1 和 str2 : %d\n", result1);
// 应输出 0,因为不区分大小写,前 13 个字节完全相同
// 比较 str1 和 str3
int result2 = memicmp(str1, str3, 13);
printf("比较 str1 和 str3 : %d\n", result2);
// 应输出一个正数,因为从第 8 个字节开始不同,W 大于 C
printf("W 的ASCII码:%d,C 的ASCII码:%d,他们差值为:%d\n", 'W', 'C', 'W' - 'C');
// W 的ASCII码:87,C 的ASCII码:67,他们差值为:20
return 0;
}
输出结果如下所示:
5.4 注意事项
非标准函数:memicmp 不是标准 C 库的一部分,因此在某些平台上可能不可用。在使用前,请确保编译环境支持该函数。
大小写不敏感:memicmp 比较时不区分大小写,这对于某些应用场景非常有用,但在其他场景中可能需要使用 memcmp 来进行区分大小写的比较。
内存区域大小:确保传递给 memicmp 的内存区域足够大,以避免访问越界导致的未定义行为。
返回值:memicmp 的返回值可以用于判断两个内存区域的相对顺序,但具体的数值意义不大,主要是用来判断是否相等、大于或小于。
6 memcpy
6.1 函数原型
memcpy 函数是 C 语言标准库中的一个函数,用于从源内存地址的起始位置开始拷贝 n 个字节到目标内存地址的起始位置中。其函数原型定义在 <string.h> 头文件中,如下所示:
#include <string.h>
void *memcpy(void *dest, const void *src, size_t n);
这里,dest 是指向用于存储复制内容的目标数组的指针,src 是指向要复制的数据源数组的指针,n 是要复制的字节数。函数返回目标数组的指针,但通常这个返回值并不被使用。
6.2 功能说明
memcpy 函数执行从源地址 src 开始的 n 个字节的数据复制到目标地址 dest 所指向的内存中。源内存区域和目标内存区域必须完全不重叠。如果源和目标内存区域有重叠部分,memcpy 的行为是未定义的,这意味着结果可能是不确定的,甚至可能导致程序崩溃或产生其他不可预料的行为。这个函数不会检查目标内存区域的大小是否足以容纳要复制的数据,因此程序员需要确保目标内存区域足够大,以避免缓冲区溢出。
6.3 案例演示
以下是一个使用 memcpy 函数的简单示例:
#include <stdio.h> // 引入标准输入输出库
#include <string.h> // 引入字符串处理库,其中包含 memcpy 函数
int main()
{
char src[40] = "Hello, World! This is a test string.";
char dest[20]; // 注意:dest 的大小小于 src,但这里我们只复制前 19 个字节(包括 '\0')
// 使用 memcpy 复制 src 的前 19 个字节到 dest
memcpy(dest, src, 19);
// 这是错误的用法,因为源和目标区域重叠
// memcpy(src + 1, src, 19);
// 确保字符串以 '\0' 结尾
dest[19] = '\0'; // 这一步在 memcpy 后手动添加,因为 memcpy 不会添加字符串结束符
// 输出复制后的字符串
printf("复制后的字符串: %s\n", dest);
return 0;
}
在上面的代码中,手动添加了 dest[19] = '\0'; 来确保 dest 是一个以空字符结尾的字符串。
输出结果如下所示:
6.4 注意事项
内存重叠:如前所述,如果源和目标内存区域重叠,则 memcpy 的行为是未定义的。在这种情况下,应使用 memmove 函数。
缓冲区溢出:程序员必须确保目标内存区域足够大,以容纳要复制的数据,包括任何必要的终止字符(如字符串的 '\0')。
字符串结束符:memcpy 不会添加字符串结束符 '\0' 到目标内存区域。如果复制的是字符串,程序员需要确保目标区域有足够的空间,并在复制后手动添加字符串结束符(如果必要的话)。
性能:memcpy 通常被高度优化,因此在处理大量数据时,它是非常高效的。然而,对于小块数据的复制,其性能优势可能不明显。
可移植性:memcpy 是 C 语言标准库的一部分,因此它在所有遵循 C 标准的平台上都是可用的,具有良好的可移植性。
7 memccpy
7.1 函数原型
memccpy 函数是 C 语言标准库中的一个函数,用于从源内存地址的起始位置开始拷贝数据,直到遇到指定的字符(包括该字符)或拷贝了指定数量的字节为止。其函数原型定义在 <string.h> 头文件中,如下所示:
#include <string.h>
void *memccpy(void *dest, const void *src, int c, size_t n);
这里,dest 是指向用于存储复制内容的目标数组的指针,src 是指向要复制的数据源数组的指针,c 是要搜索的特定字符(以无符号字符形式传递,但函数内部会转换为 unsigned char 进行比较),n 是要复制的字节数的上限。如果找到字符 c,则函数返回指向 dest 中该字符之后位置的指针;如果没有找到且已经复制了 n 个字节,则返回 NULL。
7.2 功能说明
memccpy 函数执行从源地址 src 开始的内存拷贝,直到遇到指定的字符 c(包括该字符)或达到指定的字节数 n。
- 如果找到了字符 c,则拷贝会包括该字符,并且函数会返回一个指向 dest 中该字符之后位置的指针。
- 如果没有在 n 个字节内找到字符 c,则函数返回 NULL,表示没有成功找到字符 c。
源内存区域和目标内存区域必须完全不重叠。如果源和目标内存区域有重叠部分,memccpy 的行为是未定义的,这意味着结果可能是不确定的,甚至可能导致程序崩溃或产生其他不可预料的行为。这个函数不会检查目标内存区域的大小是否足以容纳要复制的数据,因此程序员需要确保目标内存区域足够大,以避免缓冲区溢出。
7.3 案例演示
以下是一个使用 memccpy 函数的简单示例:
#include <stdio.h> // 引入标准输入输出库
#include <string.h> // 引入字符串处理库,其中包含 memccpy 函数
int main()
{
char src[] = "Hello, World! This is a test.";
char dest[50]; // 足够大以容纳复制的数据
char c = 'z'; // 要搜索的字符
// 使用 memccpy 复制数据,直到遇到字符 'z' 或复制了 40 个字节
void *ptr = memccpy(dest, src, c, 40);
if (ptr != NULL)
{
// 如果找到了字符 'z',则 ptr 指向 'z' 之后的字符
printf("找到字符 '%c',复制的内容为: %s\n", c, dest);
printf("找到字符 '%c' 之后的内容为: %s\n", c, (char *)ptr); // 这里输出可能会乱码
}
else
{
// 如果没有找到字符 'z',则复制了前 40 个字节
dest[40] = '\0'; // 确保字符串以 '\0' 结尾
printf("未找到字符 '%c',复制的内容为: %s\n", c, dest);
}
return 0;
}
在上述代码中,由于 memccpy 不会自动在目标字符串末尾添加 '\0',因此如果函数返回 NULL(即没有找到指定的字符),可能需要手动添加字符串结束符。
输出结果如下所示:
7.4 注意事项
内存重叠:与 memcpy 类似,如果源和目标内存区域重叠,则 memccpy 的行为也是未定义的。在这种情况下,应使用 memmove 函数。
缓冲区溢出:程序员必须确保目标内存区域足够大,以容纳要复制的数据(包括可能的字符 c 和其后的数据)。
字符串结束符:memccpy 不会自动在目标字符串末尾添加 '\0'。如果复制的是字符串,并且可能在没有找到字符 c 的情况下停止复制,则程序员需要确保目标区域有足够的空间,并在复制后手动添加字符串结束符(如果必要的话)。
返回值:memccpy 的返回值是指向目标字符串中字符 c 之后位置的指针,或者如果没有找到字符 c 且已经复制了 n 个字节,则返回 NULL。这允许调用者检查是否找到了字符 c。
8 memmove
8.1 函数原型
memmove 函数的原型定义在 <string.h> 头文件中,它是 C 语言标准库中的一个函数。其原型如下:
#include <string.h>
void *memmove(void *dest, const void *src, size_t n);
- void *dest:指向目标内存块的指针,即数据将要被移动到的位置。
- const void *src:指向源内存块的指针,即数据将要被移动出来的位置。const 表示这个指针指向的数据在函数执行过程中不会被修改。
- size_t n:要移动的字节数。
- memmove 函数返回 dest 参数的值,即指向目标内存块的原始指针。这个返回值允许 memmove 函数被用在需要指针链式调用的场景中,尽管在实际使用中,这个返回值往往被忽略。
8.2 功能说明
memmove 函数用于从源内存块复制 n 个字节到目标内存块。与 memcpy 不同的是,memmove 能够正确处理源内存块和目标内存块重叠的情况。当源内存块和目标内存块重叠时,memmove 会确保原始数据在被覆盖之前被复制,从而避免了数据丢失或损坏。
8.3 案例演示
以下是一个使用 memmove 函数的示例代码,演示了如何在一个数组中移动数据(包括重叠的情况)。
#include <stdio.h> // 引入标准输入输出库
#include <string.h> // 引入字符串处理库,其中包含 memmove 函数
int main()
{
char arr[] = "Hello, World!";
size_t len = sizeof(arr) - 1;
/* 1. 正常移动,没有重叠 */
printf("原始数组 arr: %s\n", arr);
// 将 5 个长度的字符串 "Hello" 复制到第 8 个位置上,即原来的 'W' 位置
memmove(arr + 7, arr, 5);
// 确保字符串正确结束
arr[len] = '\0';
printf("非重叠移动后: %s\n", arr); // Hello, Hello!
printf("==========\n");
char arr2[] = "Hello, World!";
size_t len2 = sizeof(arr2) - 1;
/* 2.重叠移动 */
printf("原始数组 arr2: %s\n", arr2);
// 将 8 个长度的字符串 "Hello, W" 移动到第 3 个位置上,即原来的 'l' 位置
memmove(arr2 + 2, arr2, 8);
arr2[len2] = '\0'; // 确保字符串正确结束(注意:此时 len 可能不再表示新字符串的长度)
printf("重叠移动后: %s\n", arr2); // 重叠移动后: HeHello, Wld!
return 0;
}
输出结果如下所示:
8.4 注意事项
重叠处理:当源内存块和目标内存块重叠时,应使用 memmove 而不是 memcpy,以避免未定义行为。
性能:在源内存块和目标内存块不重叠的情况下,memmove 的性能可能略低于 memcpy,因为 memmove 需要处理更复杂的场景(包括重叠)。然而,在大多数情况下,这种性能差异是可以接受的。
数据完整性:在使用 memmove 时,要确保目标内存块有足够的空间来接收被复制的数据,以防止缓冲区溢出。
结束符:如果 memmove 用于处理字符串,并且目标内存块是字符串的存储空间,那么在移动后,可能需要手动添加或重新设置字符串的结束符('\0')。
返回值:虽然 memmove 的返回值是指向目标内存块的指针,但在大多数情况下,这个返回值并不被直接使用。然而,了解它的存在是有益的,特别是当需要将 memmove 的调用与其他需要指针的操作结合使用时。
9 movmem
9.1 函数原型
movmem 函数不是标准 C 库的一部分,但在某些系统(如旧的 MS-DOS 系统)中提供。它的功能类似于 memmove,用于在内存之间安全地复制数据,即使源和目标内存区域重叠也能正确处理。函数原型如下:
#include <string.h> // 引入字符串处理库,其中包含 movmem 函数(如果系统支持)
void movmem(const void *src, void *dest, size_t n);
- const void *src:指向源内存区域的指针。
- void *dest:指向目标内存区域的指针。
- size_t n:要复制的字节数。
9.2 功能说明
movmem 函数用于从源内存区域复制数据到目标内存区域,即使源和目标内存区域重叠也能正确处理。具体来说:
- movmem 会确保在复制过程中不会出现数据覆盖或丢失的问题,即使源和目标内存区域有重叠部分。
- 这个函数的行为类似于 memmove,但 memmove 是标准 C 库的一部分,而 movmem 不是。
9.3 案例演示
下面是一个使用 movmem 函数的示例代码:
#include <stdio.h> // 引入标准输入输出库
#include <string.h> // 引入字符串处理库,其中包含 movmem 函数(如果系统支持)
int main()
{
char buffer[20] = "Hello, World!"; // 初始化一个缓冲区
// 使用 movmem 将 buffer 的前 5 个字节 "Hello" 复制到 buffer 的第 6 个字节位置
movmem(buffer, buffer + 5, 5);
// 打印结果
printf("复制后的缓冲区内容: %s\n", buffer);
return 0;
}
9.4 注意事项
非标准函数:movmem 不是标准 C 库的一部分,因此在某些平台上可能不可用。在使用前,请确保编译环境支持该函数。如果 movmem 不可用,可以使用标准 C 库中的 memmove 函数作为替代。
内存区域重叠:movmem 能够正确处理源和目标内存区域重叠的情况,这使得它在处理重叠内存区域时非常有用。
内存区域大小:确保传递给 movmem 的内存区域足够大,以避免访问越界导致的未定义行为。
替代函数:如果 movmem 不可用,可以使用 memmove 函数来实现相同的功能。memmove 是标准 C 库的一部分,广泛支持。
10 memset
10.1 函数原型
memset 函数的原型定义在 <string.h> 头文件中,它是标准 C 库的一部分。其原型如下:
#include <string.h>
void *memset(void *s, int c, size_t n);
- void *s:指向要填充的内存块的指针。void * 类型的指针表示可以指向任何类型的数据,这使得 memset 能够非常灵活地用于不同类型的数据。
- int c:要设置的值。尽管参数是 int 类型,但在函数内部,这个值会被转换为 unsigned char,然后用来填充内存块。
- size_t n:要填充的字节数。size_t 是一个无符号整数类型,它用于表示对象的大小,以字节为单位。
- memset 函数返回 s 参数的值,即指向被填充内存块的原始指针。虽然这个返回值在很多情况下并不直接使用,但它允许 memset 函数被用在需要指针链式调用的场景中。
10.2 功能说明
memset 函数的功能是将指定内存块的前 n 个字节设置为某个特定的值(按字节操作)。这个函数常用于内存初始化,将内存块清零或填充为某个特定的字符(当以字符数组的形式操作时)。
10.3 案例演示
以下是一个使用 memset 函数的示例代码,演示了如何初始化一个字符数组并将其所有元素设置为字符 'A'(注意,由于是按字节操作,所以实际上是将每个字节设置为 'A' 的 ASCII 码值,对于字符数组来说,这意味着每个字符都将被设置为 'A')。
#include <stdio.h> // 引入标准输入输出库
#include <string.h> // 引入字符串处理库,其中包含 memset 函数
int main()
{
char str[50]; // 定义一个字符数组
// 使用 memset 将 str 数组的前 49 个字节设置为 'A' 的 ASCII码 值
// 注意:由于字符串以 '\0' 结尾,这里实际上没有设置最后一个字节为 '\0'
// 所以在打印之前需要手动设置,或者使用适当长度的字符串(不足 50 字节)
memset(str, 'A', sizeof(str) - 1); // 留出最后一个字节作为字符串的结尾符
str[sizeof(str) - 1] = '\0'; // 手动设置字符串结尾符
// 打印 str 数组(前 49 个字符为 'A',最后一个字符为 '\0')
// 但为了演示效果,我们只打印前几个字符
printf("str的前 %d 个字符: %.*s\n", 20, 20, str); // 使用 %.*s 限制打印长度(第二个参数 20)
// 如果想打印整个数组(注意'\0'是不可见的),可以使用循环或特定函数
printf("完整的str数组(前 49 个'A'): ");
for (int i = 0; i < 49; i++)
{
printf("%c", str[i]);
}
printf("\n");
return 0;
}
输出结果如下所示:
10.4 注意事项
类型安全:虽然 memset 可以用于任何类型的内存块,但它实际上是按字节设置值的。如果尝试用非零值填充一个整数数组,每个整数元素的每个字节都将被设置为该值。
性能:memset 通常经过高度优化,可以提供非常快的内存填充速度。然而,对于复杂的初始化模式(即不是简单地将每个字节设置为同一个值),使用循环或其他方法可能更合适。
内存越界:确保 n 的值不会导致对 s 指向的内存区域之外的访问。内存越界是常见的安全漏洞之一。
初始化非零值:如果需要初始化内存块为非零值,并且该值依赖于目标数据的位宽(如整数),那么可能需要使用循环或其他初始化方法,因为 memset 总是按字节操作的。
字符串结尾符:当使用 memset 初始化字符串时,记得手动设置字符串的结尾符 '\0',除非确定后续操作会覆盖该位置。
11 setmem
11.1 函数原型
setmem 函数不是标准 C 库的一部分,但在某些系统(如旧的 MS-DOS 系统)中提供。它的功能类似于 memset,用于将指定的字节值设置到指定的内存区域。函数原型如下:
#include <string.h> // 引入字符串处理库,其中包含 setmem 函数(如果系统支持)
void setmem(void *dest, int c, size_t n);
- void *dest:指向目标内存区域的指针。
- int c:要设置的字节值(通常是一个无符号字符)。
- size_t n:要设置的字节数。
11.2 功能说明
setmem 函数用于将指定的字节值设置到目标内存区域的前 n 个字节。具体来说:
- setmem 会将目标内存区域的前 n 个字节全部设置为指定的字节值 c。
- 这个函数的行为类似于 memset,但 memset 是标准 C 库的一部分,而 setmem 不是。
11.3 案例演示
下面是一个使用 setmem 函数的示例代码:
#include <stdio.h> // 引入标准输入输出库
#include <string.h> // 引入字符串处理库,其中包含 setmem 函数(如果系统支持)
int main()
{
char buffer[20]; // 初始化一个缓冲区
// 使用 setmem 将 buffer 的前 10 个字节设置为 'A'
setmem(buffer, 'A', 10);
// 终止字符串
buffer[10] = '\0';
// 打印结果
printf("设置后的缓冲区内容: %s\n", buffer);
return 0;
}
11.4 注意事项
非标准函数:setmem 不是标准 C 库的一部分,因此在某些平台上可能不可用。在使用前,请确保编译环境支持该函数。如果 setmem 不可用,可以使用标准 C 库中的 memset 函数作为替代。
内存区域大小:确保传递给 setmem 的内存区域足够大,以避免访问越界导致的未定义行为。
字节值:setmem 的第二个参数 c 是一个 int 类型,但实际上会被转换为 unsigned char 类型。因此,传递的值应该在 0 到 255 之间。
字符串终止:如果目标内存区域用于存储字符串,确保在设置完字节值后添加字符串终止符 \0,以确保字符串的有效性。
12 memchr
12.1 函数原型
memchr 函数的原型定义在 <string.h> 头文件中,它是 C 语言标准库中的一个函数。其原型如下:
#include <string.h>
void *memchr(const void *s, int c, size_t n);
- const void *s:指向要搜索的内存块的指针。因为是 const 指针,所以这个函数不会修改所指向的内存内容。
- int c:要搜索的字符(实际上是以 int 形式接收,但在内部比较时会被转换为 unsigned char)。
- size_t n:要搜索的字节数。
- 如果找到指定的字符 c,则返回指向该字符的指针。如果没有找到,则返回 NULL 指针。
12.2 功能说明
memchr 函数用于在指定的内存块中搜索首次出现的指定字符(实际上是按字节搜索)。它从内存块的起始位置开始搜索,直到找到指定的字符或搜索了指定的字节数为止。这个函数对于在字节流中查找特定字节或字符非常有用。
12.3 案例演示
以下是一个使用 memchr 函数的示例代码,它在一个字符串中搜索字符 'a' 并打印出该字符的位置(从 0 开始计数)。
#include <stdio.h> // 引入标准输入输出库
#include <string.h> // 引入字符串处理库,其中包含 memchr 函数
int main()
{
const char str[] = "hello, world! are you there?";
const char c = 'a'; // 要搜索的字符
const char *found; // 返回值
// 使用 memchr 搜索字符 'a'
found = memchr(str, c, sizeof(str) - 1);
if (found != NULL)
{
// 计算并打印字符 'a' 的位置
printf("字符'%c'在字符串中的位置是:%d\n", c, found - str);
}
else
{
printf("字符'%c'在字符串中没有找到。\n", c);
}
return 0;
}
输出结果如下所示:
12.4 注意事项
类型安全:尽管 memchr 的第一个参数是 const void * 类型,可以指向任何类型的内存块,但通常用于处理字节或字符数组。在搜索宽字符(如 wchar_t)时,应使用 wmemchr 函数。
字符比较:在内部,memchr 将内存块中的每个字节与给定的 int 值(实际上是 unsigned char)进行比较。这意味着它不考虑字符的编码或特定于语言的特性。
性能:memchr 函数通常经过优化,可以高效地搜索内存块。然而,对于非常大的内存块或复杂的搜索模式,可能需要考虑其他搜索算法或数据结构。
边界条件:如果 n 的值大于内存块的实际大小,memchr 可能会访问未定义或受保护的内存区域,导致未定义行为。因此,确保 n 的值不超过内存块的实际大小是非常重要的。
返回值处理:在使用 memchr 的返回值时,要检查它是否为 NULL,以避免解引用空指针的错误。如果 memchr 返回 NULL,则表示在指定的内存块中没有找到指定的字符。
13 总结对比表
函数原型 | 功能描述 | 注意事项 |
---|---|---|
#include <strings.h> int bcmp(const void *s1, const void *s2, size_t n); | 比较两块内存区域的内容,直到发现差异或达到指定长度。返回 0 表示相等,非 0 表示不等。 | 已被 memcmp 替代,不推荐使用,因为 memcmp 是标准 C 库的一部分,具有更好的可移植性和安全性。 |
#include <strings.h> void bcopy(const void *src, void *dest, size_t n); | 从源内存区域复制指定长度的数据到目标内存区域。 | 已被 memcpy 替代,不推荐使用,因为 memcpy 提供了更清晰、更标准的接口。 |
#include <strings.h> void bzero(void *s, size_t n); | 将指定长度的内存区域的内容全部设置为 0。 | 已被 memset 替代,不推荐使用,因为 memset 提供了更灵活的设置值选项。 |
#include <string.h> int memcmp(const void *s1, const void *s2, size_t n); | 比较两块内存区域的内容,直到发现差异或达到指定长度。返回 0 表示相等,非 0 表示不等。 | 安全的内存比较函数,不会修改任何内存区域。 |
#include <string.h> int memicmp(const void *s1, const void *s2, size_t n); | 忽略大小写比较两块内存区域的内容,直到发现差异或达到指定长度。 | 非标准函数,具体实现可能因平台而异,不推荐在跨平台代码中使用。 |
#include <string.h> void *memcpy(void *dest, const void *src, size_t n); | 从源内存区域复制指定长度的数据到目标内存区域。要求源和目标内存区域不能重叠。 | 如果源和目标内存区域重叠,则行为未定义,应使用 memmove。 |
#include <string.h> void *memccpy(void *dest, const void *src, int c, size_t n); | 从源内存区域复制数据到目标内存区域,直到遇到指定的字符(包括该字符)或达到指定长度。 | 返回指向目标内存区域中复制到的指定字符的指针,如果未找到指定字符则返回 NULL。 |
#include <string.h> void *memmove(void *dest, const void *src, size_t n); | 从源内存区域复制指定长度的数据到目标内存区域,支持源和目标内存区域重叠的情况。 | 安全地处理内存区域重叠的情况,但可能比 memcpy 慢。 |
#include <string.h> void movmem(const void *src, void *dest, size_t n); | 类似于 memmove 的自定义函数,用于复制内存区域,可能支持重叠区域。 | 非标准函数,具体实现和行为可能因平台或库而异。 |
#include <string.h> void *memset(void *s, int c, size_t n); | 将指定长度的内存区域的内容全部设置为特定的值(通常是字节值)。 | 常用于内存初始化,设置值为 0 或 -1(即 0xFF )等。 |
#include <string.h> void setmem(void *dest, int c, size_t n); | 类似于 memset 的自定义函数,用于将内存区域的内容设置为特定的值。 | 非标准函数,具体实现和行为可能因平台或库而异。 |
#include <string.h> void *memchr(const void *s, int c, size_t n); | 在指定长度的内存区域中搜索首次出现的指定字符。 | 返回指向找到的字符的指针,如果未找到则返回 NULL。 |
请注意,bcmp、bcopy、bzero 这些函数虽然在某些旧的系统或库中仍然可用,但现代 C 编程中更推荐使用 memcmp、memcpy、memset 等标准函数,因为它们提供了更好的可移植性和安全性。同时,对于需要处理内存区域重叠的情况,应使用 memmove 而不是 memcpy。memicmp、movmem、setmem 等函数则是非标准的,具体实现和使用可能受到限制。