字符串函数的标准库为
strlen的功能是计算所给字符串str的长度,直到遇到空结束(即’\0’)字符,但不包含空结束字符。
size_t strlen(const char*str)
这里的size_t,是指unsigned int类型,表明它返回的字符串长度是一个无符号整型的数值
size_t my_strlen(const char* str)
{
size_t i = 0;//unsigned int
while (*str)
{
++str;
++i;
}
return i;
}
strcpy的功能是把 src 所指向的字符串复制到 dest。但需要注意的是如果目标数组 dest 不够大,而源字符串的长度又太长,可能会造成内存缓冲溢出的情况。
char *strcpy(char *dest, const char *src)
char* my_strcpy(char* str1, const char* str2)
{
char* tmp = str1;
while (*str1++ = *str2++);
return tmp;
}
代码中 while (*str1++ = *str2++); 的含义是将 str2 所指向的值赋给 str1 所指向的值,然后str1和str2分别++,while循环判断的是 *str的值(即为左值)。
strcat的功能是把 src 所指向的字符串追加到 dest 所指向的字符串的结尾。
char *strcat(char *dest, const char *src)
//把src中的内容追加到dst的末尾,前提是dst的容量足够大。
//strcat实现时不用开辟新的空间
char* my_strcat(char* dst, const char* src) {
char* tmp = dst;
while (*dst) {
//将指针指向dst的'\0'处
dst++;
}
while (*dst++ = *src++); //将src中的内容赋值给dst
return tmp;
}
strcmp的功能是把 str1 所指向的字符串和 str2 所指向的字符串进行比较,然后返回一个int类型的数值。
int strcmp(const char *str1, const char *str2)
int my_strcmp(const char* str1, const char* str2)
{
while (*str1 && *str2) {
if (*str1 > *str2) {
return 1;
}
else if(*str1<*str2){
return -1;
}
else {
str1++;
str2++;
}
}
if (*str1) {
return 1;
}
else if (*str2) {
return -1;
}
else {
return 0;
}
}
strstr的功能是在字符串str中查找第一次出现字符串 sub的位置,不包含终止符 ‘\0’。该函数返回在str中第一次出现 sub字符串的位置,如果未找到则返回 null。
char *strstr(const char *str, const char *sub)
char* my_strstr(const char* str, const char* sub)
{
while (*str) {
//首先遍历父串
if (*str != *sub) {
//如果不相等,则父串指针+1
++str;
}
else {
//如果首元素相等,则进行向后继续遍历,直到遇到'\0';
char* p1 = str;
char* p2 = sub;
while (*p1 && *p2 && *p1 == *p2) {
//向后遍历
++p1;
++p2;
}
if (*p1 == '\0') {
//父串首先遍历完,说明不包含子串
return NULL;
}
else if (*p2 == '\0')//子串首先遍历完,说明包含
{
return str;
}
else {
//说明遍历过程中,出现不相等的情况
++str;
}
}
}
return NULL;
}
内存函数的标准库为
memcpy的功能是从存储区 str2 复制 size 个字节到存储区 str1。该函数返回一个指向目标存储区 str1 的指针。
void *memcpy(void *str1, const void *str2, size_t size)
由于不知道传入的参数指向的内存是int类型(4Byte),还是char类型(1Byte),因此就用void来全部代替他们。
void* my_memcpy(void* dst, const void* src,size_t size)
{
char* ch_dst = (char*)dst;
char* ch_src = (char*)src;
for (size_t i = 0;i < size;++i) {
ch_dst[i] = ch_src[i];
}
return dst;
}
由于不知道这些参数所指向的内容的类型是什么,则统统将其强转为指向char类型的指针,因为char在内存中是1Byte,memcpy是从src复制size个Byte传给dst,因此不必担心改变类型后,结果的改变。
memmove的功能和memcpy的功能基本一样,均是从 str2 复制 nsize个字节到 str1中。但是它可以解决内存重叠的问题,是一个比memcpy更安全的函数。
内存重叠,顾名思义,就是当进行内存拷贝时,拷贝的区间和原始的区间发生了重叠。举个例子:
int a[10] = {
1,2,3,4,5 };
memcpy(a+4,a,20);
当上面的代码运行完后,a的值为1234123410。而我们期望的值为1234123450。这时候就发生了内存的重叠,a+4指向的是 5,从 5 这个元素的位置开始内存拷贝,首先会将5变为1,然后再继续向下按照a的指针进行赋值,而解决这种情况可以采取从后往前的赋值方式,首先赋值最后的元素,这样就可以得到我们预期的值1234123450。
内存重叠有3种情况:前重叠、后重叠、不重叠
- 前重叠
- 后重叠
- 不重叠
void *memmove(void *str1, const void *str2, size_t size)
void* my_memmove(void* dst, const void* src, size_t size)
{
//为了解决不知道大小的内存的拷贝(void),可以将其强转为char类型的,每次只拷贝一个字节,分多次拷贝即可
char* ch_dst = (char*)dst;
char* ch_src = (char*)src;
//后重叠
if (ch_dst >= ch_src && ch_dst <= ch_src + size)
{
//从后向前,注:这里不能用size_t来声明i,unsigned int为无符号整型,它是减不到0的,会造成for无限循环。
for (int i = size - 1;i >= 0;--i) {
ch_dst[i] = ch_src[i];
}
}
else {
//前重叠和不重叠的情况
//从前至后,这时可以直接用size_t声明,不会造成无限循环
for (size_t i = 0;i < size;++i) {
ch_dst[i] = ch_src[i];
}
}
return dst;
}