【C语言】C 语言内存函数与数据存储深度解析
C语言内存操作函数与数据存储机制 本文系统介绍了C语言四大内存操作函数:memcpy(非重叠内存复制)、memmove(支持重叠复制)、memset(内存初始化)和memcmp(内存比较),并给出了模拟实现。深入解析了数据存储机制:整数以补码形式存储,浮点数遵循IEEE754标准,以及大小端字节序问题。通过典型例题演示了字符类型存储差异和无符号循环陷阱,最后总结了面试常见考点,包括函数区别、存储规
目录
一、C 语言内存函数
1. memcpy:内存复制函数
void *memcpy(void *destination, const void *source, size_t num)用于从源内存地址复制指定字节数到目标内存地址。
核心特性:
- 以字节为单位复制数据,适用于任意类型的内存块。
- 复制过程中不会因
\0停止,严格按照num参数指定的字节数操作。 - 不支持重叠内存区域,若源和目标地址重叠,结果未定义。
示例代码:
#include <stdio.h>
#include <string.h>
int main() {
int arr1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int arr2[10] = {0};
// 复制前20字节(5个int类型元素)
memcpy(arr2, arr1, 20);
for (int i = 0; i < 10; i++) {
printf("%d ", arr2[i]); // 输出:1 2 3 4 5 0 0 0 0 0
}
return 0;
}
模拟实现:
#include <assert.h>
void *my_memcpy(void *dst, const void *src, size_t count) {
assert(dst && src); // 确保指针非空
void *ret = dst; // 保存目标地址用于返回
// 按字节逐个复制
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
return ret;
}
2. memmove:处理重叠内存的复制函数
void *memmove(void *destination, const void *source, size_t num)与memcpy功能相似,但支持源和目标内存区域重叠的场景。
核心特性:
- 通过判断地址关系选择复制方向,避免数据覆盖。
- 当目标地址 <= 源地址 或 目标地址>= 源地址 +
num时,正向复制(从低地址到高地址)。 - 当内存区域重叠时,反向复制(从高地址到低地址)。
示例代码:
#include <stdio.h>
#include <string.h>
int main() {
int arr1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 将前20字节复制到从arr1+2开始的位置(重叠场景)
memmove(arr1 + 2, arr1, 20);
for (int i = 0; i < 10; i++) {
printf("%d ", arr1[i]); // 输出:1 2 1 2 3 4 5 8 9 10
}
return 0;
}
模拟实现:
#include <assert.h>
void *my_memmove(void *dst, const void *src, size_t count) {
assert(dst && src);
void *ret = dst;
if (dst <= src || (char *)dst >= (char *)src + count) {
// 无重叠或目标在源右侧,正向复制
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
} else {
// 有重叠,反向复制
dst = (char *)dst + count - 1;
src = (char *)src + count - 1;
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst - 1;
src = (char *)src - 1;
}
}
return ret;
}
3. memset:内存初始化函数
void *memset(void *ptr, int value, size_t num)用于将指定内存区域的前num个字节设置为value(以字节为单位)。
核心特性:
- 按字节设置值,因此对非字符类型的数组可能产生非预期结果。
value参数虽为int,但实际只使用低 8 位(即unsigned char范围)。
示例代码:
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "hello world";
memset(str, 'x', 6); // 将前6字节设置为'x'
printf("%s\n", str); // 输出:xxxxxxworld
return 0;
}
4. memcmp:内存比较函数
int memcmp(const void *ptr1, const void *ptr2, size_t num)用于比较两个内存区域的前num个字节。
返回值规则:
- 若
ptr1<ptr2(按无符号字节比较),返回负数。 - 若
ptr1==ptr2,返回 0。 - 若
ptr1>ptr2,返回正数。
示例代码:
#include <stdio.h>
#include <string.h>
int main() {
char buffer1[] = "DWgaOtP12df0";
char buffer2[] = "DWGAOTP12DF0";
int n = memcmp(buffer1, buffer2, sizeof(buffer1));
if (n > 0)
printf("buffer1 > buffer2\n");
else if (n < 0)
printf("buffer1 < buffer2\n");
else
printf("buffer1 == buffer2\n");
return 0;
}
二、数据在内存中的存储(第 18 讲)
1. 整数的存储方式
整数在内存中以补码形式存储,原因如下:
- 统一符号位和数值位的处理逻辑。
- 简化加法和减法运算(CPU 仅需加法器)。
- 补码与原码的转换规则统一,无需额外硬件。
原码、反码、补码的转换:
- 正整数:原码 = 反码 = 补码。
- 负整数:
- 原码:直接翻译的二进制(符号位为 1)。
- 反码:原码符号位不变,其他位按位取反。
- 补码:反码 + 1。
2. 大小端字节序
超过 1 字节的数据在内存中存储时,存在字节顺序问题:
- 大端模式:数据的低位字节存于内存高地址,高位字节存于内存低地址。
- 小端模式:数据的低位字节存于内存低地址,高位字节存于内存高地址。
字节序判断方法:
// 方法1:指针法
int check_sys() {
int i = 1;
return *(char *)&i; // 小端返回1,大端返回0
}
// 方法2:联合体法
int check_sys() {
union {
int i;
char c;
} un;
un.i = 1;
return un.c; // 小端返回1,大端返回0
}
3. 浮点数的存储方式
根据 IEEE 754 标准,浮点数表示为:\(V = (-1)^S \times M \times 2^E\)
- S:符号位(0 为正,1 为负)。
- M:有效数字(1 ≤ M < 2)。
- E:指数位。
存储细节:
- 32 位浮点数(float):1 位 S + 8 位 E + 23 位 M。
- 64 位浮点数(double):1 位 S + 11 位 E + 52 位 M。
- M 的存储:省略整数部分 1,仅存小数部分,读取时补回。
- E 的存储:存入时加上中间值(8 位 E 加 127,11 位 E 加 1023)。
示例解析:
#include <stdio.h>
int main() {
int n = 9;
float *pFloat = (float *)&n;
printf("n = %d\n", n); // 输出:9
printf("*pFloat = %f\n", *pFloat); // 输出:0.000000(整数9的补码按浮点数解析极小)
*pFloat = 9.0;
printf("n = %d\n", n); // 输出:1091567616(浮点数9.0的存储按整数解析)
printf("*pFloat = %f\n", *pFloat); // 输出:9.000000
return 0;
}
三、典型例题解析
1. 字符类型存储问题
#include <stdio.h>
int main() {
char a = -1;
signed char b = -1;
unsigned char c = -1;
// a和b为有符号char,补码0xff对应-1;c为无符号,0xff对应255
printf("a=%d, b=%d, c=%d\n", a, b, c); // 输出:a=-1, b=-1, c=255
return 0;
}
2. 无符号循环陷阱
#include <stdio.h>
unsigned char i = 0;
int main() {
// i为无符号char,范围0~255,i<=255恒成立,导致死循环
for (i = 0; i <= 255; i++) {
printf("hello world\n");
}
return 0;
}
四、面试考察要点
- 内存函数:
memcpy与memmove的区别(重叠内存处理),memset的字节操作特性,memcmp的比较逻辑。 - 整数存储:补码的作用,原码、反码、补码的转换规则,符号位的影响。
- 字节序:大小端的判断方法,不同硬件架构的字节序差异(如 x86 为小端)。
- 浮点数存储:IEEE 754 标准的核心规则,整数与浮点数在内存中的解读差异。
- 边界问题:无符号类型的循环陷阱,字符类型的溢出处理(如
char的范围 - 128~127)。
掌握内存操作函数和数据存储规则,是理解 C 语言底层逻辑的关键,尤其在系统编程和性能优化中至关重要。建议结合调试工具观察内存布局,加深对存储细节的理解。

更多推荐


所有评论(0)