Linux中字符串拷贝函数strlcpy的实现
字符串拷贝函数strlcpy
size_t strlcpy(char *dest, const char *src, size_t size)
{size_t ret = strlen(src);if (size) {size_t len = (ret >= size) ? size-1 : ret;memcpy(dest, src, len);dest[len] = '\0';}return ret;
}
static inline void * __memcpy(void * to, const void * from, size_t n)
{
int d0, d1, d2;
__asm__ __volatile__("rep ; movsl\n\t""testb $2,%b4\n\t""je 1f\n\t""movsw\n""1:\ttestb $1,%b4\n\t""je 2f\n\t""movsb\n""2:": "=&c" (d0), "=&D" (d1), "=&S" (d2):"0" (n/4), "q" (n),"1" ((long) to),"2" ((long) from): "memory");
return (to);
}
1. strlcpy
函数功能概述
strlcpy
是一个安全的字符串拷贝函数,可以防止缓冲区溢出
1.1. 第一段:获取源字符串长度
size_t strlcpy(char *dest, const char *src, size_t size)
{size_t ret = strlen(src);
dest
:目标缓冲区src
:源字符串size
:目标缓冲区大小ret = strlen(src)
先计算源字符串的长度
1.2. 第二段:安全检查和非空缓冲区处理
if (size) {size_t len = (ret >= size) ? size-1 : ret;
if (size)
检查目标缓冲区大小是否为0- 三元运算符计算实际拷贝长度:
- 如果源字符串长度 >= 缓冲区大小:拷贝
size-1
个字符(留1字节给结束符) - 否则:拷贝整个源字符串长度
- 如果源字符串长度 >= 缓冲区大小:拷贝
1.3. 第三段:内存拷贝和字符串终止
memcpy(dest, src, len);dest[len] = '\0';}return ret;
}
memcpy(dest, src, len)
拷贝指定长度的字符dest[len] = '\0'
确保目标字符串正确终止return ret
返回源字符串长度(不是实际拷贝长度)
2. __memcpy
函数功能概述
这是一个用内联汇编优化的内存拷贝函数
2.1. 第一段:函数声明和变量定义
static inline void * __memcpy(void * to, const void * from, size_t n)
{
int d0, d1, d2;
to
:目标内存地址from
:源内存地址n
:要拷贝的字节数d0, d1, d2
:临时变量用于汇编约束
2.2. 第二段:主拷贝循环 - 按4字节拷贝
__asm__ __volatile__("rep ; movsl\n\t"
rep movsl
重复执行movsl
,每次拷贝4字节(双字)ecx
寄存器控制重复次数(n/4
)- 这是最高效的拷贝方式,一次处理4字节
2.3. 第三段:处理剩余2字节
"testb $2,%b4\n\t""je 1f\n\t""movsw\n"
testb $2,%b4
测试字节操作数4的低字节第1位(判断是否剩余2字节)je 1f
如果没有剩余2字节,跳转到标签1je
: 当ZF=1
时执行跳转,即testb
结果为0时
movsw
拷贝2字节
2.4. 第四段:处理剩余1字节
"1:\ttestb $1,%b4\n\t""je 2f\n\t""movsb\n""2:"
1:\ttestb $1,%b4
标签1:测试是否剩余1字节,注意\t
是制表符je 2f
如果没有剩余1字节,跳转到标签2(结束)movsb
拷贝1字节
2.5. 第五段:汇编约束条件
: "=&c" (d0), "=&D" (d1), "=&S" (d2):"0" (n/4), "q" (n),"1" ((long) to),"2" ((long) from): "memory");
输出操作数:
"=&c" (d0)
:d0
绑定到ecx
寄存器(& 表示早期破坏)"=&D" (d1)
:d1
绑定到edi
寄存器"=&S" (d2)
:d2
绑定到esi
寄存器
输入操作数:
"0" (n/4)
:n/4
使用与操作数0相同的约束(ecx
,重复次数)"q" (n)
:n
使用字节寄存器(a,b,c,d
之一)"1" ((long) to)
:to
使用与操作数1相同的约束(edi
,目标指针)"2" ((long) from)
:from
使用与操作数2相同的约束(esi
,源指针)
破坏描述:
"memory"
:告诉编译器内存内容被修改
2.6. 第六段:函数返回
return (to);
}
- 返回目标指针(标准
memcpy
行为)
3. 函数工作流程图
4. 寄存器使用说明
寄存器 | 用途 |
---|---|
ecx | 重复计数器(n/4) |
edi | 目标指针(to) |
esi | 源指针(from) |
字节寄存器 | 存储n用于位测试 |
5. 汇编指令详解
rep movsl
:重复拷贝双字(4字节),ecx
次testb
:测试字节的特定位je
:等于零时跳转movsw
:拷贝字(2字节)movsb
:拷贝字节(1字节)
6. 拷贝策略分析
优化思路:
- 批量处理:先用
movsl
按4字节批量拷贝 - 剩余处理:按2字节和1字节处理剩余部分
- 位测试:用位运算高效判断剩余字节数
字节数处理:
n % 4 的可能情况:
0: 直接由movsl处理完
1: movsl + movsb
2: movsl + movsw
3: movsl + movsw + movsb