- 求字符串长度
- strlen
- 长度不受限制的字符串函数
- strcpy
- strcmp
- strcat
- 长度受限制的字符串函数
- strnlen
- strncmp
- strncpy
- strncat
- 字符串查找
- strstr
- strtok
- 错误信息报告
- strerror
- 内存操作函数
- memcpy
- memmove
- memset
- memcmp
- 首先我们来看strlen
- 字符串是以‘\0’为结束标志,strlen函数返回的是‘\0’出现的字符个数(但不包括‘\0’)
- 我们还要注意一下就是strlen的返回值,它是无符号的(size_t)
size_t my_strlen(const char *str)
{
assert(str);
int count = 0;
while (*str != '\0')
{
count++;
str++;
}
return count;
}
int main()
{
size_t ret = my_strlen("abcdef");
printf("%d\n", ret);
return 0;
}
- 源字符串必须以‘\0’结束
- 会将源字符串中的‘\0’拷贝到目标空间中
- 目标空间必须足够大了,以确保能存放源字符串
char* my_strcpy(char *dest,const char *src)
{
assert(src);
assert(dest);
char* ret = dest;
while (*src != '\0')
{
*dest = *src;
src++;
dest++;
}
*dest = *src;
return ret;
}
int main()
{
char arr1[20] = "hello wolrd";
char arr2[] = "xxxxxx";
my_strcpy(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
- 首先,我们来看返回值,你是否一开始和我有一样的疑问呢?? 为什么返回值是char*的??
- 我从《C语言和陷阱》中得到了答案,这是为了链式访问
- strcpy传递的两个参数,第一个就是目标空间,第二个就是源字符串
- 思路:
- 我们要注意源字符串的‘\0’,我们是将源字符串一个一个拷贝到dest中,并且‘\0’也是要拷贝到'\0'的
- 标准规定:
- 第一个字符串大于第二个字符串,则返回大于0的数字
- 第一个字符串等于第二个字符串,则返回0
- 第一个字符串小于第二个字符串,则返回小于0的数字
int my_strcmp(const char *str1,const char *str2) { assert(str1); assert(str2); while (*str1 == *str2) { if(*str1 == '\0') { return 0; } str1++; str2++; } //if (*str1 > *str2) //{ // return 1; //} //else // return -1; return (*str1 - *str2); } int main() { int ret = my_strcmp("ab", "abc"); printf("%d\n", ret); return 0; }
思路:
我们也是和之前一样的比较方法,结束标志都是看'\0‘,我们通过一个一个比较,最后c会和‘\0’比较,因为我们的c大于0,所以会返回一个小于0的数字,我们return (*str1 - *str2); 就是返回了一个大于或小于0的值,这样写就不用用if else来判断了,代码少了很多
- strcat,就是在目标字符串的后面加上源字符串
char* my_strcat(char *dest,const char *src) { assert(dest); assert(src); char* ret = dest; while (*dest!='\0') { dest++; } while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = "hello "; char arr2[] = "world"; my_strcat(arr1,arr2); printf("%s\n", arr1); return 0; }
思路:
- 因为我们是将arr2追加到arr1中,所以我们特别要关于arr2‘’\0'的位置,这样我们通过一个一个追加到arr1后面的位置,就可以实现了strcat的实现,最后我们为了链式访问,我们要一开始要保存一个dest一开始的地址
strncmp就是在传参后面加上了要比较的个数
int main() { char arr1[] = "abcdefghijk"; char arr2[] = "abcdef"; int ret = strncmp(arr1, arr2, 7); printf("%d\n", ret); return 0; }
- 我们来看这个例子,因为我们在传参加上了要比较的个数,在这两个字符串中,前7个字符就是相同的,所以最后返回是的0
- 所以,我们如果要用字符串的函数,
- strnlen
- strncmp
- strncpy
- strncat
- 会安全很多,可以根据自己的需求来实现自己想达到的母的
int main() { for (int i =0; i<10; i++) { printf("%d:%s\n", i, strerror(i)); } return 0; }
这个函数就是可以给我们看错误的信息报告的
- 我们来看内存操作的函数,它接受的参数是void* ,这是因为它可能接受浮点型、整型、 字符型,类型是不确定的,所以我们用void* 来接受
void* my_memcpy(void *dest,const void *src,size_t num) { assert(dest && src); void* ret = dest; while (num--) { *(char*)dest = *(char*)src; src = (char*)src + 1; dest = (char*)dest + 1; } return ret; } int main() { int arr1[] = { 1,2,3,4,5,6,7,8,9,10 }; int arr2[10] = { 0 }; my_memcpy(arr1, arr2, 20); for (int i=0; i<10; i++) { printf("%d ", arr1[i]); } return 0; }
- 思路:我们注意,我们memcpy还要传递一个size_t的数,它表示的是字节
- 如代码所示,我们是将arr2中的0给arr1中,而且是20个字节,所以是把arr1中的前五个数改为0,这是因为一个int是4个字节 arr1中的1 就表示为01 00 00 00 ,2就是 02 00 00 00 ... 这样我们就是一个一个字节的拷贝到目标空间去
- memmove接收参数的含义其实和memcpy差不多,这里就不再重复了
void* my_memmove(void *dest,const void *src,size_t num) { assert(dest && src); void* ret = dest; if (dest < src) { while (num--) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = (char*)src + 1; } } else { while (num--) { *((char*)dest + num) = *((char*)src + num); } } return ret; } int main() { int arr1[] = { 1,2,3,4,5,6,7,8,9,10 }; int arr2[10] = { 0 }; my_memmove(arr1, arr2, 20); for (int i =0; i<10; i++) { printf("%d ", arr1[i]); } return 0; }
思路:
- 我们要分时从前->后拷贝还是从后->前拷贝,这是因为如果dest>src的时候,如果我们传递的是my_memmpve(arr1+2,arr1,20) 这是arr1的数据是1 2 1 2 1 2 1 8 9 10,原因就是当会形成覆盖
- 所以,我们要分情况,因为数组是从低地址向高地址存贮的,所以当dest< src的时候,就是从前向后存储,这时我们要注意我们是一个一个字节存储的,而且我们是void* 所以我们要强转一下类型
*(char*)dest = *(char*)src; dest = (char*)dest + 1; src = (char*)src + 1;
这就是我们移动一次的时候,我们要移动num次,加上while循环就可以了。
从后到前的移动是一样的思想
- 最后:今天关于常见的字符串函数的模拟实现和内存的函数就到这里了,后续我也会大家多多分享一些常见的知识,多谢各位的支持了。