目录
指向源字符串的指针是const:1是为了避免无意修改源字符串2是如果源字符串是"hello world"这种字符串字面值也能奏效。 string.h里面定义了str和mem开头的函数,除了memmove以外,其他函数都没有定义重叠对象间的复制行为;
1:strcpy
注意: 源指针指向的值要用const修饰,防止修改,同时可以指向字符串字面值;
目标指针指向的值要修改所以不能为const;
如果src为nullptr,返回dest即可;如果dest为nullptr,返回nullptr;
如果src == dest,那么直接返回dest;
用ret保存dest的值,后面需要返回它,这样可支持链式表达式,如:
int len = strlen(strcpy(strA,strB));
依次拷贝源指针指向的每个字符到目标指针,然后二个指针移动,最后如果遇到'\0'后就结束;
char* myStrcpy(char *dest,const char* src){
assert(dest && src);
if(dest == src) return dest;
char* ret = des;
while ((*dest++ = *src++) !='\0');
//退出循环时:*dest == '\0'
return ret;
}
2. strncpy
char * myStrncpy(char *dest,const char * src,int n){
char * tmp = dest;
assert(dest,src);
if(dest == src || n<=0) return dest;
while (n-- && (*tmp ++ = *src++) != '\0');
*tmp = '\0';
//n--为0后推出循环,但是'\0'还没有拷贝,最后不要忘记加上字符串结尾标记
return dest;
}
3.strlen
int myStrlen(const char * str){
int len = 0;
if(str == nullptr ) return len;
while (*str != '\0'){
++str;
++len;
}
return len;
}
4.strcat
char* myStrcat(char * dest,const char *src){ //在dest字符串末尾添加字符串src
//调用者必须保证dest有足够的空间去将src放进去,本程序不提供保证。
//在vc++上面测试,strcat源指针和目标指针相同的情况下也可以实现拼接
assert(dest,src);
char* tmp_str = dest;
while (*tmp_str!='\0')tmp_str++; //这个就是strlen实现方式
// char * tmp_str = dest + myStrlen(dest); //指针移动到dest的末尾,调用strlen
while ((*tmp_str++ = *src++) !='\0');
//循环退出时:*tmp_str = '\0';
return dest;
}
5.memcpy
memcpy不考虑内存重叠问题,所以有安全问题;
memmove考虑内存重叠,所以比memcpy安全,但效率比memcpy差一点
void* myMemcpy(void* dest,const void* src,int n){
if(dest == nullptr || src == nullptr ) return nullptr;
if(dest == src) return dest; //二个指针指向相同,直接返回
void* ret = dest; //ret指向dest,后面直接返回ret
char* pdest = (char*)dest;
const char* psrc = (const char*)src;
while (n--){
*pdest++ = *psrc++;
}
return ret;
}
6.memmove
目标dest不能是const,因为要赋值;源src加const保护
memmove可以处理内存重叠的情况,而memcpy不能,所以memmove更安全,但如果确定没有内存重叠则memcpy效率高;
void* myMemmove(void* dest,const void* src,int n ){
if(dest == nullptr || src == nullptr ) return nullptr;
if(dest == src) return dest; //二指针指向相同,直接返回
char* pdest = (char*)dest;
void* ret = dest; //ret指向dest,后面直接返回
const char* psrc = (const char *)src;
if(pdest<=psrc || pdest>=psrc+n){//不存在内存重叠的2种问题,则从起始处开始逐一拷贝
while (n--){
*pdest++ = *psrc++;
}
} else{//可能发生内存重叠的1种情况:src<dest
pdest+=n;//先移动n个字节,然后从后往前拷贝
psrc+=n;
while (n--){
*--pdest = *--psrc;//先移动一个字节再拷贝
}
}
return ret;
}
memcpy和memove的区别:
当没有内存重叠时,二者都可,但memcpy速度比memmove快一点;
当有内存重叠时候,memmove安全,memcpy不安全,要使用memmove;
当出现内存重叠时候,memmove保证源dest在被覆盖之前将重叠区域的字节拷贝到目标区域,但复制后src内容会被更改
strcpy和memcpy的区别:
1)复制的内容不同:strcpy用于拷贝字符串,还会复制字符串的末尾结束标记'\0';memcpy用于一般内存的复制,对需要复制的内容没有限制,用途更广。
2)复制的方式不同:strcpy复制时候不需要指定长度,通过判断是否遇到源字符串结束标记'\0'才结束,所以容易溢出;而memcpy需要第三个参数指定拷贝的长度,但出现内存重叠问题,则memcpy不安全,推荐使用memmove;
3)用途不同:通常复制字符串使用strcpy,复制其它数据类型使用memcpy;
memset常见用法:
1)初始化为0: memset(a,0,sizeof a);
2)初始化为-1: memset(a,-1,sizeof a);
3)初始化为MAX: define MAX 0x3f3f3f3f memset(a,0x3f,sizeof a);
这样a数组里面的全部元素,就定义成了0x3f3f3f3f,这是个很好用的数字可以定义为无穷大,大概是1e9,算法题里面很多输入都没有超过它,并且二个相加也没有超过int的最大值。
经常看到定义最大值INF为0x7fffffff,这个十六进制数字是32-bit int的最大值,如果这个无穷大只适用于一般的比较,那么0x7fffffff是一个好的选择,但是有时候需要无穷大在加上一个数,那么这样0x7fffffff就会溢出了,所以可以使用INF=0x3f3f3f3f作为无穷大,它的十进制是1061109567,是10^9级别的(和0x7fffffff一个数量级),而一般场合下的数据都是小于10^9的,所以它可以作为无穷大使用而不致出现数据大于无穷大的情形。另一方面,由于一般的数据都不会大于10^9,所以当我们把无穷大加上一个数据时,它并不会溢出(这就满足了“无穷大加一个有穷的数依然是无穷大”),事实上0x3f3f3f3f+0x3f3f3f3f=2122219134,这非常大但却没有超过32-bit int的表示范围,所以0x3f3f3f3f还满足了我们“无穷大加无穷大还是无穷大”的需求。
strcmp实现:strcmp主要是用来判断二个字符串是否相等的库函数(判断相等时区分大小写)
(1)当str1==str2时,返回0
(2)当str1>str2时,返回大于0的整数
(3)当str1<str2时,返回小于0的整数
编写实现的思路:首先判断参数入参有效性,参数错误则返回error,注意error提前宏定义为了-2,因为函数返回类型是int,所以即使参数错误返回也应该为int类型。循环的判断条件为(*str1!='\0')&&(*str1==*str2),实际上当str1或者str2为结束符'\0'或者str1和str2不再相等时,则退出循环。换种理解方式就是,当当前判断的字符不为'\0',并且相等两字符串当前的字符相等,则*str++后执行下一次判断,一旦不相等则进行比较大小。剩下的if就比较简单了,主要还是循环的判断条件要写对。
#include <stdio.h>
#define error -2
int mystrcmp(const char* str1, const char* str2)
{
if((NULL == str1)||(NULL == str2)){
return error;
}
while((*str1!='\0')&&(*str1==*str2))
{
str1++;
str2++;
}
if(*str1>*str2){
return 1;
}
else if(*str1<*str2){
return -1;
}else
return 0;
}