超详细的C进阶教程!字符串及内存函数的使用及其模拟实现
Posted 东条希尔薇
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了超详细的C进阶教程!字符串及内存函数的使用及其模拟实现相关的知识,希望对你有一定的参考价值。
作者的码云地址:https://gitee.com/dongtiao-xiewei
后续作者会更新力扣的每日一题系列,原代码会全部上传码云,推荐关注哦,笔芯~
还像更深入地了解c语言?快来订阅作者的c语言进阶专栏!作者承诺本系列不会TJ!预计更新:指针,字符串处理,内存管理,结构体,预处理等等
模拟实现函数的规范性问题我已经在以前的文章讲过了,本文章不再做详细阐述,原文章地址https://blog.csdn.net/weixin_57402822/article/details/119394214?spm=1001.2014.3001.5501
今天为大家带来字符串函数的使用,为了能够帮助大家更加深入的了解C语言字符串函数,会将大部分函数模拟实现一遍,加深大家的理解
字符串以及内存函数
若要使用字符串函数,请引入<string.h>头文件!
strlen函数,求字符串长度的函数
定义:这是一个库函数,作用是可以求出指定字符串的长度
这是其在cplusplus上的定义
注意!strlen函数只能识别到字符’\\0’才会停止!
错误使用示例
int main()
{
char arr[] = { 'a','b' };//这里没有添加‘\\0’
printf("%d\\n", strlen(arr));
return 0;
}
所以输出的结果并没有达到我们的预期,函数直到在随机内存中找到’\\0’才能停下,所以会输出一个随机值
正确使用用例
int main()
{
char arr[] = "ab";//字符串会默认在末尾加上'\\0'
printf("%d\\n", strlen(arr));
return 0;
}
函数模拟实现
这里介绍三种模拟实现的方式
- 引入计数器
- 指针-指针
- 递归实现
计数器实现
毫无疑问,这是最简单的一种逻辑了,直接数,直到遇到’\\0’停止计数
unsigned my_strlen(const char* str)
{
//my_strlen_count
assert(str);
unsigned count=0;
while(*str)
{
str++;
count++;
}
return count;
}
指针-指针实现
我们可以再定义一个指针变量来往后,寻找,直到找到’\\0’为止,利用指针-指针=中间元素的个数来计算出字符串长度
//my_strlen_ptr
unsigned my_strlen(const char* str)
{
assert(str);
char* cur = str;
while (*cur)
cur++;
return cur - str;
}
递归实现
假如有一个字符串"abcd"
我们可以这么想:
1. "abcd"的长度等于"abc"长度+1
2. "abc"长度等于"ab"长度+1
3. "ab"长度等于"a"长度+1
这么一直循环下去,可以轻松的写出我们的递归实现逻辑
unsigned my_strlen(const char* str)
{
//my_strlen_recursion
if (!(*str))
return 0;
else
return my_strlen(str + 1) + 1;
}
字符串函数:长度不受限
这里介绍一些常用的字符串操作函数
strcpy
我在这篇函数编写规范中已经讲到了这个函数,这里不再阐述
原文地址:https://blog.csdn.net/weixin_57402822/article/details/119394214?spm=1001.2014.3001.5501
strcat
这是cplusplus上的定义
这个函数可以实现在目标字符后追加源字符串的功能
注意!必须确保源空间足够大,追加的字符串直到’\\0’为止
使用示范
int main()
{
//strcat_test
char arr1[30] = "hello";
char arr2[] = "world!\\0hahahaha";//注意这一条语句
printf("%s\\n", strcat(arr1, arr2));
return 0;
}
输出结果以’\\0’终止
模拟实现
根据函数的定义,我们需要先找到目标字符串的末尾,再进行追加,其中目标字符串的’\\0’将会替代,新函数的’\\0’将是源字符串的’\\0’
//my_strcat
char* my_strcat(char* dst, const char* src)
{
assert(dst && src);
char* ret = dst;//保存目标字符串的起始地址,方便返回
while (*dst)
dst++;//找到目标字符串的末尾
while (*dst++ = *src++)
;//开始追加
return ret;
}
strcmp
这是cplusplus上的定义
此函数的比较方式:
会一个字符一个字符的往后比较,直到遇到不相等的字符为止
关于返回值的一点说明
- 如果1字符串比2字符串大,则返回一个大于0的值,VS默认返回1,linux上返回两个字符串ASC码差值
- 如果1字符串比2字符串小,则返回一个小于0的值,VS默认返回-1,linux上返回两个字符串ASC码差值
- 如果1字符串比2字符串相等,则返回一个等于0的值,VS默认返回0
使用示范
int main()
{
//strcmp_test
char arr1[] = "abcdef";
char arr2[] = "abq";
char arr3[] = "ab";
int ret = strcmp(arr1, arr2);
int ret1 = strcmp(arr1, arr3);
printf("%d,%d\\n", ret, ret1);
return 0;
}
模拟实现
要模拟实现,首先要搞清楚函数的工作原理
我们也需要一个字符一个字符的进行比较
//my_strcmp
int my_strcmp(const char* s1, const char* s2)
{
assert(s1 && s2);
while (*s1 == *s2 && *s1 && *s2)//如果字符串相等,应该终止
{
s1++;
s2++;//相等就比较下一个
}
return *s1 - *s2;//返回asc码差值
}
以上的函数,由于数量不受控制,所以函数不够安全,为了保证函数的安全性,以下引入长度受限的字符串函数
长度受限的字符串函数
以下函数跟上面函数唯一的区别都是加了一个控制操作数量的参数
strncpy
cplusplus上的定义
与字符串拷贝函数唯一的区别是规定了拷贝的个数
使用示例
//strncpy_test
int main()
{
char arr1[100] = "hello world!";
char arr2[] = "xxxxx";
strncpy(arr1, arr2, 2);//只拷贝两个字符到源字符串中
printf("%s\\n", arr1);
return 0;
}
模拟实现
首先需要了解一些细节
需要记录还剩多少字符等待拷贝
若源字符串长度小于num,则自动补’\\0’直到num为止
//my_strncpy
char* my_strncpy(char* dst, const char* src, size_t n)
{
assert(dst && src);
char* ret = dst;
while (*src && n)
{
*dst = *src;
dst++;
src++;
n--;//记录已经拷贝了多少字符
}
if (n)
{
*dst = '\\0';
dst++;
n--;
//若n还没到0,拷贝'\\0'
}
return ret;
}
strncmp
与上述函数差不多,这里不再做详细阐述
功能:比较指定长度的字符串
strncat
功能:将指定源字符串长度追加至目标字符串
字符串查找
strstr函数
cplusplus上的定义
介绍:此函数会查找将源字符串做为子串后,第一次出现在目标字符串的起始地址,若不存在,返回NULL
使用示例
int main()
{
//strstr_test
char arr1[] = "i am a good student, bit student!";
char arr2[] = "student";
char* ret = strstr(arr1, arr2);
if (ret == NULL)
printf("none");
else
printf("%s\\n", ret);
return 0;
}
模拟实现
这个函数的实现有点意思,大家听好了!
测试字符
char arr1[]="abbbcdef";
char arr2[]="bbc";
需要在arr1中找到是否有arr2的子串
大家可能会想到这一种办法,就是指定一个指针arr1,若与arr2相等,就开始遍历两个字符比较,不过这种方法在这个用例中不适用
所以,我们希望做出以下的改进
- 需要一个指针来记录开始匹配arr1的位置
- 需要一个指针一直指向arr2的起始位置,方便每次的遍历
- 需要两个指针在找到第一个相等的字符后,分别开始遍历
- 返回可以直接返回第一大点说明
的指针 - 退出条件,arr1或arr2其中一个到达’\\0’
以上思路我们也可以画图解释
代码实现
//my_strstr
char* my_strstr(const char* s1, const char* s2)
{
assert(s1 && s2);
if (!(*s2))
{
return s1;//如果s2是空字符串,直接返回s1
}
char* st1;
char* st2;
//起始位置
char* cur = s1;
//每一次尝试匹配的位置
//abbbcdef
//bbc
while (*cur)
{
st1 = cur;
st2 = s2;
//遍历寻找,cur是否与s2相等
while (*st1 && *st2 && *st1 == *st2)
{
st1++;
st2++;//遍历中
}
if (!(*st2))
{
return cur;//跳出情况一:s2到达末尾
}
cur++;
}
return NULL;
//跳出情况二:s2到达末尾
}
strtok
cplusplus定义
此函数的使用比较复杂
功能:在某一字符串中寻找是否有分隔字符串中的元素,有,则将其分割,并将其置为’\\0’
若第一个参数不为NULL,将找到str中第一个标记,并记住相关位置
若第一个参数为NULL,则从保存的标记开始继续往下寻找
若不存在更多的标记,返回NULL
使用示例
//strtok_test
int main()
{
char arr1[] = "123456789@qq.com";
char set[] = "@.";
char* ret = NULL;
for (ret = strtok(arr1, set); ret != NULL; ret = strtok(NULL, set))
{
//for第一句,初始化ret,strtok会记住分割的位置
//for第二句,判断ret是否为NULL,不存在可分割的元素才会返回空
//for第三句,利用strtok的记忆特性,传空指针使用即可
printf("%s\\n", ret);
}
return 0;
}
以上是关于字符串操作的,关于非字符串操作又该怎么进行呢?
就需要用到内存函数了
内存操作函数
memcpy
cplusplus上的定义
功能:拷贝指定字节数的数据
使用示例
//memcpy_test
int main()
{
int arr1[20] = { 0 };
int arr2[] = { 1,2,3,4,5,6,7,8,9,10 };
memcpy(arr1, arr2, sizeof(int) * 10);
for (int i = 0; i < 20; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
模拟实现
先看看定义,需要用void*为啥?
void*是万能接受指针,可以接受任意类型的数据
但不能进行指针加减和解引用等操作,需要强转,最好转为char方便操作
void* my_memcpy(void* dst, const void* src, size_t count)
{
assert(dst && src);
void* ret = dst;
int i = 0;
for (i = count; i > 0; i--)
{
*(char*)dst = *(char*)src;
//dst = (char*)dst + 1;
//src = (char*)src + 1;
(char*)dst += 1;
(char*)src += 1;
}
return ret;
}
但是,这个函数不能实现这一种拷贝
int arr[]={1,2,3,4,5,6,7,8,9,0};
arr与arr+2的拷贝
这种重叠的情况,memcpy是未定义的,需要使用memmove函数
memmove
这个函数就是为了改进上面情况设计的功能,这里就不再做详细阐述
可能需要改变一下上述的复制方式
重点讲讲其模拟实现
模拟实现
第一种情形:dst地址小于src的地址
这里从前往后复制没有什么问题
第二种情形,dst地址大于src的地址
**注意!**这里我们为了避免数据被覆盖,需要从后往前复制了
若任然从前往后复制
只能从后往前复制
核心思想:防止数据被覆盖!!!!
void* my_memmove(void* dst, const void* src, size_t count)
{
void* ret = dst;
if (*(char*)dst < *(char*)src)
{
while (count--)
{
*(char*)dst = *(char*)src;
(char*)dst += 1;
(char*)src += 1;
}
}
else
{
(char*)dst += count - 1;
(char*)src += count - 1;
while (count--)
{
*(char*)dst = *(char*)src;
(char*)dst -= 1;
(char*)src -= 1;
}
}
return ret;
}
以上是关于超详细的C进阶教程!字符串及内存函数的使用及其模拟实现的主要内容,如果未能解决你的问题,请参考以下文章