猛戳订阅 详解数据结构专栏 深度解析
大家好,我是_奇奇,一名C/C++博主。河牧院大一在读。欢迎一起交流学习我会将大一学的数据结构和C语言深度解析写成笔记记录下来。后期会慢慢推进。感兴趣可以订阅以下专栏。详解数据结构专栏深度理解C语言专栏 个人主页编程的前途是光明的,道路是曲折的。
这不是目录个人简介❄️前言字符串函数
strlen
函数模拟 strcpy
函数模拟 strcat
函数模拟 strcmp
函数模拟 strncpystrncatstrstr
函数模拟 strtokstreeor 字符分类函数内存函数
memcpy
函数模拟实现 memmove
函数模拟 memcmp
❄️前言C语言中对字符和字符串的处理很是频繁,不仅如此在以后的校招面试中字符串的题目也会涉及不少,这点在LeetCode中就能体现到,在刷题时经常会遇到处理字符串的内容,字符串还和指针相关联,对字符串的深入理解也会增加对指针的理解。
字符串函数 strlen❄️功能:求字符串长度
❄️函数原型
size_t strlen ( const char* str );
注意:
字符串以 ‘ ’ 作为结束标志,strlen函数返回的是在字符串中 ‘ ’ 前面出现的字符个数(不包含 ‘ ’ )。参数指向的字符串必须要以 ‘ ’ 结束。注意函数的返回值为size_t,是无符号整型,也就是不是负数。( 易错!!! ) 函数模拟
1.指针-指针的方式
注意:这种方式只适用于同一个数组
int my_strlen(char *s)
{
char *p = s;
while(*p != ‘0’ )
p++;
return p-s;
}
2.计数器方法
int my_strlen(const char* str)
{
int count = 0;
while(*str)
{
count++;
str++;
}
return count;
}
3.递归方法(面试官最爱考)
//不能创建临时变量计数器
int my_strlen(const char * str)
{
if(*str == ' ')
return 0;
else
return 1+my_strlen(str+1);
}
strcpy
❄️函数功能:拷贝源字符串到目的地空间,包括" "也拷贝过去。
❄️函数原型
char *strcpy( char *strDestination, const char *strSource );
注意:
源字符串必须以 ‘ ’ 结束。会将源字符串中的 ‘ ’ 拷贝到目标空间。目标空间必须足够大,以确保能存放源字符串。目标空间必须可变,原字符串不可变! 函数模拟
注意点:
assert断言const修饰
char *my_strcpy(char *dest, const char*src)
{
char *ret = dest;
assert(dest != NULL);
assert(src != NULL);
while((*dest++ = *src++))
{
;
}
return ret;
}
strcat
❄️函数功能:
将源字符串和目标字符串连接起来。
❄️函数原型
char * strcat ( char * destination, const char * source );
注意:
源字符串必须以 ‘ ’ 结束。目标空间必须有足够的大,能容纳下源字符串的内容。目标空间必须可修改。返回的是目标字符串的首元素的地址。 函数模拟
注意:
后置++是先使用后++。
char *my_strcat(char *dest, const char*src)
{
char *ret = dest;
assert(dest != NULL);
assert(src != NULL);
while(*dest)
{
dest++;
}
while((*dest++ = *src++))
{
;
}
return ret;
}
strcmp
❄️函数功能:
比较两个字符串的大小,按照每个字符的ASCII码值依次往后比较,一个字符一个字符的比较,只要遇到字符不同,就返回。
❄️函数原型
char * strncpy ( char * destination, const char * source, size_t num );
注意:
并不是大于返回1,小于返回-1,没有这个规定
规定如下
第一个字符串大于第二个字符串,则返回大于0的数字第一个字符串等于第二个字符串,则返回0第一个字符串小于第二个字符串,则返回小于0的数字 函数模拟
注意;
while的循环条件&&操作符,左操作数为假时,有操作数就不用执行了。
int my_strcmp (const char * src, const char * dst)
{
int ret = 0 ;
assert(src != NULL);
assert(dest != NULL);
while( ! (ret = *(unsigned char *)src- *(unsigned char *)dst) && *dst)
{
++src;
++dst;
}
if ( ret < 0 )
ret = -1 ;
else if ( ret > 0 )
ret = 1 ;
return( ret );
}
strncpy
❄️函数功能:
拷贝num个字符从源字符串到目标空间。
❄️函数原型
char * strncpy ( char * destination, const char * source, size_t num );
注意:
如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。目标空间必须足够大。和strcpy函数唯一区别就是拷贝的个数受限制。 strncat
❄️函数功能:
将num个源字符串和目标字符串连接起来。
❄️函数原型
char * strncat ( char * destination, const char * source, size_t num );
注意:
目标字符串足够大 strstr
函数功能:
在目标字符串str1里找到源字符串,并返回这个源字符串的首元素的地址。
函数原型
char * strstr ( const char *str1, const char * str2);
注意:
重点在函数模拟的实现 函数模拟
如果str2为空,不用照了。直接返回str1。
如图包含了找字符串的所有情况。
思考问题要考虑全面。
我认为下面代码是最优的代码了。但是需要自己细看。描述的话太麻烦了。
#includestrtok#include char* my_strstr(const char* str1, const char* str2) { assert(str1 && str2); if (!*str2) { return((char*)str1); } char* cp = str1; char* s1 = 0; char* s2 = 0; while (*cp != ' ') { s1 = cp; s2 = str2; while (*s2 != ' ' && *s1 != ' ' && *s2 == *s1) { s1++; s2++; if (!*s2) { return cp; } } cp++; } return NULL; } int main() { char arr[] = "hworld"; char arr1[] = "world"; char* p = my_strstr(arr, arr1); printf("%s", p); return 0; }
❄️函数功能:将字符串分割。
❄️函数原型
char * strtok ( char * str, const char * sep );
注意
sep参数是个字符串,定义了用作分隔符的字符集合第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记strtok函数找到str中的下一个标记,并将其用 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。如果字符串中不存在更多的标记,则返回 NULL 指针。
如图例子
❄️函数功能:返回错误码所对应的错误信息。
❄️函数原型
char * strerror ( int errnum );
例子
#include字符分类函数#include #include //必须包含的头文件 int main () { FILE * pFile; pFile = fopen ("unexist.ent","r"); if (pFile == NULL) printf ("Error opening file unexist.ent: %sn",strerror(errno)); //errno: Last error number return 0; } Edit & Run
| 函数 | 如果他的参数符合下列条件就返回真 |
|---|---|
| isspace | 空白字符:空格‘ ’,换页‘f’,换行’n’,回车‘r’,制表符’t’或者垂直制表符’v’ |
| isdigit | 十进制数字 0~9 |
| isxdigit | 十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A ~ F |
| islower | 小写字母a~z |
| isupper | 大写字母A~Z |
| isalpha | 字母a~ z或A~Z |
| isalnum | 字母或者数字,a ~ z,A~ Z,0~9 |
| ispunct | 标点符号,任何不属于数字或者字母的图形字符(可打印) |
| isgraph | 任何图形字符 |
| isprint | 任何可打印字符,包括图形字符和空白字符 |
| iscntrl | 任何控制字符 |
❄️函数功能:
函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置
❄️函数原型
void * memcpy ( void * destination, const void * source, size_t num );
注意:
注意是以字节为单位的拷贝。这个函数在遇到 ‘ ’ 的时候并不会停下来。如果source和destination有任何的重叠,复制的结果都是未定义的。 函数模拟实现
因为不知道数据是哪种类型所以需要进行强制类型转换。
void * memcpy ( void * dst, const void * src, size_t count)
{
void * ret = dst;
assert(dst);
assert(src);
while (count--)
{
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
return(ret);
}
memmove
❄️函数功能:
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
❄️函数原型
void * memmove ( void * destination, const void * source, size_t num );
注意:
如果源空间和目标空间出现重叠,就得使用memmove函数处理。 函数模拟
注意
如果src > dest ,或者det > src+ count 从前往后拷贝就可以了否则 从后往前拷贝不遵循这个规则的话就会覆盖,导致拷贝错误。
void* memmove(void* dst, const void* src, size_t count)
{
void* ret = dst;
if (dst <= src || (char*)dst >= ((char*)src + count))
{
while (count--)
{
*(char*)dst = *(char*)src;
dst = (char*)dst + 1;
src = (char*)src + 1;
}
}
else
{
dst = (char*)dst + count - 1;
src = (char*)src + count - 1;
while (count--)
{
*(char*)dst = *(char*)src;
dst = (char*)dst - 1;
src = (char*)src - 1;
}
}
return(ret);
}
memcmp
❄️函数功能:
比较从ptr1和ptr2指针开始的num个字节和strcmp函数功能相同。
❄️函数原型
int memcmp ( const void * ptr1,
const void * ptr2,
size_t num );
函数返回值



