c语言memcpy和java_C语言字符串和内存函数介绍

strlen

函数原型:size_t strlen(const char* str),计算字符串长度的函数,不包含'\0',注意返回值为size_t。

strlen与sizeof的区别:1.strlen是函数,sizeof是关键字。2.strlen传入的参数是const char*,因此用于计算字符串的长度,而sizeof可以是类型,且sizeof数组时数组名代表整个数组。3.strlen遇到'\0'结束,sizeof计算一个数据类型占用内存的大小。

模拟实现:

size_t my_strlen(const char* str)

{

assert(str);

size_t ret = 0;

while (str[ret] != '\0')

ret++;

return ret;

}

2. strcpy

** 函数原型**:char* strcpy(char* des,const char* src),将一个字符串复制到des开始的内存空间中。

src必须以'\0'结尾,复制的时候会将'\0'也复制到des中,des空间必须能容纳复制后的字符串,des必须是可改变的内存空间。

如果复制超出des空间的字符串,程序会崩溃并报错堆栈空间被破坏,即缓冲区溢出。

模拟实现strcpy时还要注意内存重叠问题,如果des所在的地址比src的地址大,且des地址比src的'\0'处地址还要小,那么此时如果按照从低到高的地址进行赋值,则会导致前面赋的值改变了后面的值,结果就是无法复制正确的结果。过程如图:

47a308e1b301c28a8470a4ba9abae79e.png

模拟实现:

//不考虑内存重叠

char* my_strcpy(char* des, const char* src)

{

assert(des != NULL && src != NULL);

char* ret = des;

while ((*des++ = *src++) != '\0');

return ret;

}

//考虑内存重叠

char* mem_strcpy(char* des, const char* src,size_t count)

{

assert(des != NULL && src != NULL);

char* ret = des;

if (des > src && des < src + count)

{

des = des + count - 1;

src = src + count - 1;

while (count--)

*des-- = *src--;

}

else

{

while (count--)

*des++ = *src++;

}

return ret;

}

3. strcat

函数原型:char* strcat(char* des,const char* src),简单来说是向des后插入字符串,同样的,源字符串必须以'\0'结尾,且des是可改变的内存,且空间足够,否则运行时报错堆栈溢出。

在模拟实现时要注意如果用strcat自己向自己后面插入的问题,如果只是单纯的将des向后移动到'\0'的位置,然后用src的内容依次给des赋值,带来的问题是会将'\0'覆盖,且由于src也是这个字符串,所以'\0'已经无法获得了,我的解决方式是如果自己向自己后面插入,则从高位向地位赋值。

模拟实现:

char* my_strcat(char* des, const char* src)

{

assert(des != NULL && src != NULL);

char* tmp = des;

while (*des)

des++;

if (tmp == src)

{

size_t n = strlen(src) + 1;

des = des + strlen(src);

src = src + strlen(src);

while (n)

{

*des-- = *src--;

n--;

}

return tmp;

}

while ((*des++ = *src++) != '\0');

return tmp;

}

4. strcmp

函数原型:int strcmp(const char* str1,const char* str2),比较两个字符串的大小,如果str1大,则返回大于0的数,str1小则返回小于0的数,相等则返回0,比较规则为一个字符一个字符的按照ASCII码大小比较,注意:小写字母要比大写字母的ASCII码大。优先比较字符大小,字符都相等则比较长度。

模拟实现:

int my_strcmp(const char* str1, const char* str2)

{

assert(str1 != NULL && str2 != NULL);

while (*str1!='\0' && *str1 == *str2)

{

str1++;

str2++;

}

if (*(unsigned char*)str1 > *(unsigned char*)str2)

return 1;

else if (*(unsigned char*)str1 < *(unsigned char*)str2)

return -1;

else

return 0;

}

5. strncpy

函数原型:char* strncpy(char* des,const char* src,size_t n),同样是拷贝字符串,但是可以指定长度,如果n大于src长度,则在多余的空间后赋值'\0'。

模拟实现:

char* my_strncpy(char* des, const char* src, size_t n)

{

assert(des != NULL && src != NULL);

char* ret = des;

while (n && (*des++=*src++)!='\0')

n--;

while (n)

{

*des++ = '\0';

n--;

}

return ret;

}

6. strncat

函数原型:char* strncat(char* des,const char* src,size_t n),拷贝字符串到字符串的末尾,与strcat不同的是可以自己给定要拷贝多少字节。

注意:n大于src的长度的话,多余的也要填充'\0',且这个函数同样需要考虑给末尾添加自己的情况。

模拟实现:

char* my_strncat(char* des, const char* src, size_t n)

{

assert(des != NULL && src != NULL);

char* ret = des;

while (*des)

des++;

//在自己后面添加自己的情况

if (ret == src)

{

if (n > strlen(ret))

{

char* des_tmp = des;

des_tmp += strlen(ret);

size_t count = n - strlen(ret);

while (count--)

*des_tmp++ = '\0';

}

size_t num = strlen(ret);

while (num--)

*des++ = *src++;

return ret;

}

while (n && (*des++ = *src++) != '\0')

n--;

while (n)

{

*des++ = '\0';

n--;

}

if (*(--src) != '\0')

*des = '\0';

return ret;

}

7. strstr

函数原型:char* strstr(const char* str1,const char* str2),查找一个字符串在另一个字符串中的位置,如果不存在则返回NULL。

此函数可用KMP算法来写。

模拟实现

char* my_strstr(const char* str1, const char* str2)

{

char* cp = (char*)str1;

char* sub = (char*)str2;

char* cpcur = NULL;

while (*cp)

{

cpcur = cp;

sub = (char*)str2;

while (*cpcur && *sub && *cpcur == *sub)

{

cpcur++;

sub++;

if (*sub == '\0')

return cpcur;

}

cp++;

}

return NULL;

}

8. memcpy

函数原型:void memcpy(void des,const void src,size_t n),按照字节进行拷贝,因为形参类型是void,因此可以传入任意类型的指针,但是在拷贝时是按照一个字节一个字节进行拷贝的。

此函数遇到'\0'并不会停下,因为并不是字符串拷贝函数。

此函数同样需要考虑内存重叠问题,但是源码并没有实现,而是用了另一个函数memmove。

模拟实现:

//不考虑内存重叠

void* my_memcpy1(void* des,const void* src, size_t n)

{

assert(des != NULL && src != NULL);

void* ret = des;

while (n--)

{

*(char*)des = *(char*)src;

des = (char*)des + 1;

src = (char*)src + 1;

}

return ret;

}

//考虑内存重叠

void* my_memcpy2(void* des, const void* src, size_t n)

{

assert(des != NULL && src != NULL);

char* des_tmp = (char*)des;

char* src_tmp = (char*)src;

if (des_tmp > src_tmp && des_tmp < src_tmp + n)

{

des_tmp = des_tmp + n - 1;

src_tmp = src_tmp + n-1;

while (n--)

*des_tmp-- = *src_tmp--;

}

else

{

while (n--)

*des_tmp++ = *src_tmp++;

}

return des;

}

9. memmove

函数原型:void* memmove(void* des,const void* src,size_t n),同样是内存拷贝,但是考虑了内存重叠问题。

模拟实现:

void* my_memmove(void* des, const void* src, size_t n)

{

assert(des != NULL && src != NULL);

char* des_tmp = (char*)des;

char* src_tmp = (char*)src;

if (des_tmp > src_tmp && des_tmp < src_tmp + n)

{

des_tmp = des_tmp + n - 1;

src_tmp = src_tmp + n - 1;

while (n--)

*des_tmp-- = *src_tmp--;

}

else

{

while (n--)

*des_tmp++ = *src_tmp++;

}

return des;

}

10. memset

**函数原型:void memset(void str,int c,size_t n),将内存空间str n个字节设置成c,虽然传入的是int型,但是存储时是以无符号字符型存储。

模拟实现:

//c:以int传入,但是在填充内存时是以该值的无符号char填写。

void* my_memset(void* str, int c, size_t n)

{

assert(str != NULL);

char* str_tmp = (char*)str;

while (n--)

*str_tmp++ = (char)c;

return str;

}

11. memcmp

**函数原型:int memcmp(const void str1,const void str2),按字节进行比较大小,比较原理同strcmp。

模拟实现:

int my_memcmp(const void* str1, const void* str2,size_t n)

{

assert(str1 != NULL && str2 != NULL);

unsigned char* str1_tmp = (unsigned char*)str1;

unsigned char* str2_tmp = (unsigned char*)str2;

while (n && *str1_tmp == *str2_tmp)

{

n--;

str2_tmp++;

str1_tmp++;

}

if (n > 0)

{

if (*str1_tmp > *str2_tmp)

return 1;

else

return -1;

}

return 0;

}

总结:

例如strcpy函数,原型是char* strcpy(char* des,const char* src),在使用这个函数时我们可能直接这样,char str1[20]="hello",const char* str2="world",strcpy(str1,str2),导致一种错觉,那就是这好像是个字符串替换函数,然后des必须从字符串开头开始一样,实际上也可以这样strcpy(str1+2,str2+1)。这样看来,实际上des和src不过是标记一段内存的开始位置罢了,我想用内存a这个位置开始的内存中的内容来替换内存b位置开始的内存中的内容。这样在理解内存重叠问题时更加清晰。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值