memcmp,memset函数详解

这篇文章详细介绍了C/C++标准库中的memcmp函数,用于比较两个内存区域的内容,包括其函数原型、参数含义、返回值以及使用示例。同时提到了函数的一些注意事项,如数据类型和内存对齐、空指针处理以及越界访问问题。
摘要由CSDN通过智能技术生成

memcmp

memcmp 是 C 和 C++ 标准库中的一个函数,用于比较两个内存区域的内容。它定义在 <string.h>(C)或 <cstring>(C++)头文件中。memcmp 函数逐字节地比较两个内存块,直到发现不同的字节或到达指定数量的字节。

函数原型如下:

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


参数说明:

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

返回值:

  • 如果 s1 指向的内存块(前 n 个字节)小于 s2 指向的内存块,函数返回一个负数。
  • 如果 s1 指向的内存块(前 n 个字节)等于 s2 指向的内存块,函数返回零。
  • 如果 s1 指向的内存块(前 n 个字节)大于 s2 指向的内存块,函数返回一个正数。

使用示例:  

 #include <stdio.h>  
 #include <string.h>  
   int main() {  
 char str1[] = "Hello";  
 char str2[] = "World";  
 char str3[] = "Hello";  
   int result1 = memcmp(str1, str2, 5); // 比较前5个字节  
 int result2 = memcmp(str1, str3, 5); // 比较前5个字节  
   printf("Result of memcmp(str1, str2, 5): %d\n", result1); // 应该输出一个小于0的数  
 printf("Result of memcmp(str1, str3, 5): %d\n", result2); // 应该输出0  
   return 0;  
 } 

 注意:

  • memcmp 不会考虑数据类型或内存对齐,它只是简单地逐字节比较。
  • memcmp 不会检查指针是否为空或无效。如果传递了空指针或无效指针给 memcmp,可能会导致程序崩溃或其他未定义的行为。
  • 在比较字符串时,如果 s1 和 s2 指向的字符串以 null 终止符 \0 结尾,并且 n 的值足够大以至于包含了这些终止符,那么比较将在遇到第一个 null 终止符时停止(但 memcmp 本身并不会检查 null 终止符)。然而,在实践中,如果你正在比较 C 风格的字符串,并且想要根据字符串的自然长度进行比较,那么你应该使用 strcmp 而不是 memcmp。

注意事项1:不会考虑数据类型或内存对齐
假设你有两个 int 类型的变量,但它们在内存中的布局可能由于对齐或其他原因而有所不同。如果你尝试使用 memcmp 来比较这两个变量,它只会逐字节地比较它们,而不会考虑它们作为 int 类型的意义。

 #include <stdio.h>  
 #include <string.h>  
   int main() {  
 int num1 = 0x01020304; // 假设这是大端序存储  
 int num2 = 0x04030201; // 假设这是小端序存储,但具有相同的数值  
   // 这两个整数在二进制表示上相同,但可能在内存中的字节顺序不同  
 // memcmp 会逐字节比较,因此可能会返回非零值,即使数值相同  
 int result = memcmp(&num1, &num2, sizeof(int));  
 printf("Result of memcmp: %d\n", result); // 根据字节序,可能会输出非零值  
   return 0;  
 } 

在这个例子中,尽管 num1 和 num2 在数值上相等(都是 0x01020304 的不同字节序表示),但 memcmp 可能会因为它们在内存中的字节顺序不同而返回非零值。

注意事项2:不会检查指针是否为空或无效
如果你传递给 memcmp 的指针是 NULL 或指向了无效的内存区域,程序可能会崩溃。

 #include <stdio.h>  
 #include <string.h>  
   int main() {  
 char *ptr1 = NULL; // 空指针  
 char *ptr2 = "Hello";  
   // 尝试使用 memcmp 比较一个空指针和一个有效指针  
 // 这会导致未定义的行为,通常会导致程序崩溃  
 int result = memcmp(ptr1, ptr2, 5);  
   // 这里的代码不会执行,因为程序很可能已经崩溃了  
 printf("Result of memcmp: %d\n", result);  
   return 0;  
 } 

在这个例子中,尝试使用 memcmp 比较一个空指针和一个有效指针会导致程序崩溃。

注意事项3:越界访问
如果传递给 memcmp 的 n 值超过了源或目标内存块的实际大小,将会导致越界访问,可能会导致数据损坏、程序崩溃或其他未定义的行为。

 #include <stdio.h>  
 #include <string.h>  
   int main() {  
 char str[] = "Hello"; // 只有6个字节的空间(包括null终止符)  
   // 尝试复制超过字符串实际长度的字节数  
 // 这将导致越界访问,因为str只有5个可见字符和1个null终止符  
 int result = memcmp(str, "Hello", 10); // 越界了,因为只分配了6个字节给str  
   // 这里的代码可能会执行,但之前的越界访问可能已经导致了未定义的行为  
 printf("Result of memcmp: %d\n", result);  
   return 0;  
 } 

在这个例子中,尽管 memcmp 可能不会直接崩溃程序(取决于具体的实现和运行时环境),但越界访问已经导致了未定义的行为,可能会影响到程序的其他部分。

memset

memset 函数是 C 和 C++ 标准库中的一个函数,用于将一块内存区域的内容设置为一个特定的值。它定义在 <string.h>(C)或 <cstring>(C++)头文件中。memset 通常用于初始化内存区域为某个特定的字节值,比如将所有字节设置为 0 或某个特定的字符。

函数原型如下:

 void *memset(void *s, int c, size_t n); 

参数说明:

  • s:指向要设置的内存块的指针。
  • c:要设置的值。注意,这个值会被转换为 unsigned char 类型,然后复制到内存块中。因此,虽然参数是 int 类型,但实际上只有低 8 位会被使用。
  • n:要设置的字节数。

返回值:

  • memset 函数返回指向被修改的内存区域的指针,但这通常不是很有用,因为调用者已经有了这个指针。

使用示例:

 #include <stdio.h>  
 #include <string.h>  
   int main() {  
 char str[20];  
   // 使用 memset 将 str 的前 20 个字节设置为 'A'(在 ASCII 中是 65)  
 memset(str, 'A', 20);  
   // 打印结果,注意字符串以 null 终止符 '\0' 结尾,但 memset 不会设置它  
 // 所以这里的字符串实际上没有终止符,打印时可能会继续输出直到遇到 null 终止符  
 // 为了安全起见,我们只打印前 20 个字符  
 for (int i = 0; i < 20; i++) {  
 printf("%c ", str[i]);  
 }  
 printf("\n");  
   // 如果你想要 str 作为一个字符串来使用,你需要手动添加 null 终止符  
 str[19] = '\0'; // 设置第 20 个字节为 null 终止符  
   // 现在可以安全地作为字符串打印了  
 printf("%s\n", str); // 输出 "AAAAAAAAAAAAAAAAAA"  
   return 0;  
 } 

注意事项1:使用字节值而非字符值
当使用 memset 函数时,需要意识到它操作的是字节值,而不是字符值。这意味着你需要确保你传递给 memset 的字符值在内存中的表示是你所期望的。在 ASCII 编码中,字符 'A' 的字节值是 65(十进制),但在其他编码中可能不同。
错误示例(假设使用非ASCII编码):

char str[10];  
 memset(str, 'A', sizeof(str)); // 如果'A'在非ASCII编码中不是65,则可能不会得到预期的结果 

在这个例子中,如果字符 'A' 在当前使用的编码中不是用字节值 65 来表示的,那么 memset 将不会将内存区域设置为预期的字符 'A'。

注意事项2:缓冲区溢出
如果传递给 memset 的指针指向的内存区域小于要设置的字节数 n,那么将会发生缓冲区溢出,这可能导致数据损坏、程序崩溃或其他未定义的行为。
错误示例:

char str[5];  
 memset(str, 'A', 10); // 缓冲区溢出,因为 str 只有 5 个字节的空间 

在这个例子中,我们尝试将 10 个字节的值写入只有 5 个字节空间的 str 数组中,这会导致缓冲区溢出。

注意事项3:memset 不是用于复制字符串
尽管 memset 可以用来填充内存区域为某个值,但它并不是用来复制字符串的。如果你尝试使用 memset 来复制字符串,你可能会遇到问题,因为它不会添加字符串的 null 终止符。
错误示例:

 char src[] = "Hello";  
 char dest[5]; // 注意这里的大小不足以容纳整个字符串和null终止符  
 memset(dest, src[0], sizeof(dest)); // 这只是将 'H' 填充到 dest 中,并没有复制整个字符串  
 // dest 现在包含 "HHHHH",而不是 "Hello",并且没有 null 终止符 

在这个例子中,我们使用 memset 试图将 src 字符串复制到 dest 中,但由于 memset 只是将单个字节值填充到内存区域中,并且没有添加 null 终止符,所以结果并不是我们期望的完整字符串。
正确的字符串复制应该使用 strcpy、strncpy 或其他类似的函数。

  • 18
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值