# C标准库实现-glibc为例
glibc 和 libc 都是 Linux 下的 C 函数库。
- libc 是 Linux 下的 ANSI C 函数库
- glibc 是 Linux 下的 GNU C 函数库
# 目录
[toc]
# 实现memcpy「腾讯」
- 这是一个C语言实现的内存拷贝函数,它的作用是从源地址
src
拷贝num
个字节到目的地址dest
。
为什么在构造的时候把它强制转换成char*,而不是int*等其他类型的呢?因为,memcpy可以拷贝任意类型的内存,所以如果转成int*,那么它每次只能拷贝4个字节,而对于字符串或是结构体的拷贝,它们的大小可能不是4的整数倍,这样就会导致拷贝的失败,所以为了可以拷贝全部内存,要一个一个字节的拷贝,而char*恰好满足这样的条件.
void *memcpy (void * dest, const void * src, size_t num) {
assert(NULL !=src && NULL !=dest); //「坑1」断言,防止空指针
char *m_dest=(char *)dest;
const char *m_src=(const char *)src;
for(size_t i=0; i<num; ++i){
m_dest[i]=m_src[i];
}
return dest;
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 实现memmove「腾讯」
memmove
函数的作用是从源内存地址src
处开始拷贝n
个字节到目标内存地址dest
处。它的优点是可以处理源内存和目标内存有重叠的情况,而memcpy
函数则不能处理这种情况。
最后,memcpy在内存没有重复的情况下能够正确复制,若有重叠情况则复制结果错误,但是它的效率比memmove高。所以,在确定没有重复的情况下用memcpy,在不确定是否有内存重复的情况用memmove。
如上!三种情况描述如下:
- dst
<=
src- 此时,正向的拷贝即可,不会覆盖数据
- src
<
dst<
src+n- 此时,需要逆向的拷贝!否则会覆盖数据
- src+n
<=
dst- 此时,正向的拷贝即可,不会覆盖数据
void * memmove ( void * dst,const void * src,size_t count)
{
assert(NULL !=src && NULL !=dst);//「坑1」断言,防止空指针
void * ret = dst;
if (dst <= src || (char *)dst >= ((char *)src + count))
{ // 若dst和src区域没有重叠,则从起始处开始逐一拷贝
while (count--)
{
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
}
else
{ // 若dst和src 区域交叉,则从尾部开始向起始位置拷贝,这样可以避免数据冲突
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);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void * memmove ( void * dst,const void * src,size_t n)
{
assert(NULL !=src && NULL !=dst);//「坑1」断言,防止空指针
char *m_dst=(char *)dst;
char *m_src=(char *)src;
if( m_dst<= m_src || m_dst>=(m_src+n) ){
memcpy(m_dst,m_src,n); //情况1和3的正向拷贝
}
else{
while(n--){
m_dst[n]=m_src[n]; //逆向拷贝
}
}
return dst;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 实现strcpy
把从src地址开始且含有'\0'
结束符的字符串复制到以dst开始的地址空间,返回值的类型为char*
char *strcpy(char *dst, char *src){
assert( NULL!=dst && NULL!=src );
char *ret=dst;
while( '\0'!=(*src) ){
*(dst++)=*(src++);
}
*dst='\0';
return dst;
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 实现strlen
计算给定字符串的⻓度
int strlen( const char *str ){
assert( NULL!=str );
int len=0;
while( '\0'!=(*str) ){
++len;
++str;
}
return len;
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 实现strcat
作⽤是把src所指字符串添加到dest结尾处
char * strcat( char *dst, const char *src ){
assert( NULL!=dst && NULL!=src ); //assert( dst && src); 也是可以的
char *ret=dst;
while( '\0'!=(*dst) ){
++dst;
}
//从'\0'添加开始
while( '\0'!=src ){
(*dst)=(*src);
++dst;
++src;
}
*dst='\0';
return ret;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 实现strcmp「学习」
⽐较两个字符串设这两个字符串为str1,str2
若str1 == str2,则返回0
若str1 < str2,则返回负数
若str1 > str2,则返回正数
int strcmp(const char *s1, const char *s2){
assert( NULL!=s1 && NULL!=s2 ); //assert( s1 && s2); 也是可以的
int i;
for (i = 0; s1[i] != '\0' && s2[i] != '\0'; ++i){
if (s1[i] != s2[i]){
return (s1[i] - s2[i]);
}
}
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11