此文介绍常用、面试爱考的几个函数: strlen()、strcpy()、strncpy()、strcat()、strncat()、strcmp()、strncmp()、strstr()、strchr()、strtok()、strerror()使用、memcpy()、memmove()、memset()、memcmp()
字符串函数在实现的时候一定得进行 :
Double check 即:1、函数调用者在函数调用时需保证参数的有效性。
合理化校验 2、函数使用者在函数内部保证参数的有效性。
即:判断参数是否为空,或者指向 '\0'.
方案一:
if(str == NULL)
{
return 0; //使用条件语句判空则函数结束
}
方案二:
assert(str != NULL); //断言函数
解释 :表达式为真,断言通过,函数顺利执行;
表示式为假,断言失败, 程序直接崩溃(函数封装时使用,但平时写大型程序时不介意使用);
使用时需包含头文件
size_t strlen (const char* str);
注: 函数参数必须为指向字符串;
strlen(),返回指向字符串中 '\0' 前的字符得个数, 返回值为size_t型,是无符号的。
函数模拟实现代码:
#include
#include
#include //使用断言函数必须包含头文件
size_t my_strlen(const char* dest) //返回值为无符号型
{
size_t count = 0;
assert(dest != NULL); //断言
while(*dest++) //循环直到dest指向 '\0'结束
{
count++;
}
return count;
}
int main ()
{
char str[] = "abcdefg"; //操作对象必须为字符串
int k;
k = my_strlen(str);
printf("字符串str的长度为:%d\n", k);
system("pause");
return 0;
}
上面实现的my_strlen()函数中while循环等价于:
while ( *dest ) //等价于 while ( * dest != '\0' )
{ dest++; count++; }
char* strcpy (char* dest, const char* src) ;
注 : 实现把src里的内容全部赋给dest, 包括 '\0',覆盖dest里原有的内容。
实参 dest 的空间必须保证足够大,保证拷贝不会出现内存越界行为。
返回值为: 拷贝后指向 dest 的指针。
函数模拟实现代码:
#include
#include
#include //使用断言函数必须包含头文件
char* my_strcpy(char* dest, const char* src)
{
char* ret = dest;
assert(dest != NULL);
assert(src != NULL);
while(*src) //src指向 '\0'时循环退出
{
*dest++ = *src++;
} //函数退出条件必须为尾指针指向'\0'
*dest = '\0'; // 此时 *dest 正好指向字符串的末尾,并给此位置赋值为'\0'
return ret; //返回dest 开始时的首地址
}
int main ()
{
char str1[1024] = "abcdefg"; //目标字符串的空间必须足够大,推荐写成1024
char* str2 = "hijk"; //保证str2开辟的内存小于str1,避免出现内存访问越界
char* ret;
ret = my_strcpy(str1, str2);
printf("my_strcpy的结果为:%s\n", ret);
system("pause");
return 0;
}
char* strncpy(char* dest, const char* src, size_t len);
注: 拷贝 len 长的字符串到 dest字符串中。
实参 dest 的空间必须保证足够大,保证拷贝不会出现内存越界行为。
当 len < strlen(src) 时,拷贝后字符时应向dest 后面追加 '\0' 直到 len 个。
其他与strcpy相同。
函数模拟实现代码:
注:必须得考虑 len 大于 strlen(src)的情况
#include
#include
#include //使用断言函数必须包含头文件
#include
char* my_strncpy(char* dest, const char* src, size_t len)
{
char* ret = dest;
size_t offset = 0;
assert(dest != NULL);
assert(src != NULL);
if(len > strlen(src)) //此条件语句完美解决 len 过长的问题
{
offset = len - strlen(src);
len = strlen(src); //文章中具体解释语句
}
while(len--)
{
*dest++ = *src++; //拷贝字符
}
while(offset--)
{
*dest = '\0'; //将len大于strlen(src)的长度全都赋值为'\0'
}
return ret; //返回dest 开始时的首地址
}
int main ()
{
char str1[1024] = "abcdefg"; //目标字符串的空间必须足够大,推荐写成1024
char* str2 = "hijk"; //保证str2开辟的内存小于str1,避免出现内存访问越界
int size = 100;
char* ret;
ret = my_strncpy(str1, str2, size);
printf("my_strncpy的结果为:%s\n", ret);
system("pause");
return 0;
}
上面函数中的 if 条件语句:
if(len > strlen(src)) //此条件语句完美解决 len 过长的问题
{
offset = len - strlen(src); 把len大于strlen(src)的具体值拿出来 ,拷贝完后把这个长度的每个地址都赋值为 '\0 '。
len = strlen(src); 再让len 等于strlen(src),代表要拷贝的字符。
}
char* my_strcat (char* dest, const char* src);
注: 首先还是一样的,目标字符串必须足够大并且可以修改。
实参 dest 的空间必须保证足够大,保证拷贝不会出现内存越界行为。
将 src 指向的字符串内容拼接到 dest 的后面,最后再补上 '\0' 。
函数模拟实现代码:
#include
#include
#include //使用断言函数必须包含头文件
char* my_strcat(char* dest, const char* src)
{
char* ret = dest;
assert(dest != NULL);
assert(src != NULL);
while(*dest)
{
dest++; // 让dest指向目标字符串中'\0'的位置
}
while(*src)
{
*dest++ = *src++; //从目标字符串'\0'的位置开始拷贝 src 中的字符串
}
*dest = '\0';
return ret; //返回dest 开始时的首地址
}
int main ()
{
char str1[1024] = "abcdefg"; //目标字符串的空间必须足够大,推荐写成1024
char* str2 = "hijk"; //保证str2开辟的内存小于str1,避免出现内存访问越界
char* ret;
ret = my_strcat(str1, str2);
printf("my_strcat的结果为:%s\n", ret);
system("pause");
return 0;
}
char* my_strncat(char* dest, const char* src, size_t len);
注: 基本用法和strcat相同,但只是拼接 len 个字符到 dest 里面 。
实参 dest 的空间必须保证足够大,保证拷贝不会出现内存越界行为。
特别注意 len > strlen(src) 的情况(应把大于 strlen(src) 的长度赋值为 '\0')。
函数模拟实现代码:
注:必须得考虑 len 大于 strlen(src)的情况
#include
#include
#include //使用断言函数必须包含头文件
#include
char* my_strncat(char* dest, const char* src, size_t len)
{
char* ret = dest;
size_t offset = 0;
assert(dest != NULL);
assert(src != NULL);
if(len > strlen(src)) //此条件语句完美解决 len 过长的问题
{
offset = len - strlen(src);
len = strlen(src);
}
while(*dest)
{
dest++; // 让dest指向目标字符串中'\0'的位置
}
while(len--)
{
*dest++ = *src++; //从目标字符串'\0'的位置开始拷贝 src 中的字符串
}
while(offset--)
{
*dest = '\0'; //将len大于strlen(src)的长度全都赋值为'\0'
}
return ret; //返回dest 开始时的首地址
}
int main ()
{
char str1[1024] = "abcdefg"; //目标字符串的空间必须足够大,推荐写成1024
char* str2 = "hijk"; //保证str2开辟的内存小于str1,避免出现内存访问越界
int size = 100;
char* ret;
ret = my_strncat(str1, str2, size);
printf("my_strncat的结果为:%s\n", ret);
system("pause");
return 0;
}
int strcmp(const char* dest, const char* src);
注: 返回值为 int 类型, 即 strcmp (str1, str2) str1 = str2 , 返回 0
str1 < str2 , 返回一负数
str1 > str2 , 返回一正数
参数 dest src 为常量字符串,不能被改变
函数模拟实现代码:
#include
#include
#include //使用断言函数必须包含头文件
int my_strcmp(const char* dest, const char* src)
{
int i = 0;
int j = 0;
assert(dest != NULL);
assert(src != NULL);
while(*dest++)
{
i++;
}
while(*src++)
{
j++;
}
return i - j;
}
int main ()
{
char str1[1024] = "abcdefg"; //目标字符串的空间必须足够大,推荐写成1024
char* str2 = "hijk";
int ret = 0;
ret = my_strcmp(str1, str2);
if(ret > 0)
{
printf("str1 大于 str2!\n");
}
else if(ret < 0)
{
printf("str1 小于 str2!\n");
}
else
{
printf("str1 等于 str2!\n");
}
printf("my_strcmp的结果为:%d\n", ret);
system("pause");
return 0;
}
int strncmp ( const char * dest, const char * src, size_t num );
注 : 返回值和 strcmp 相同
此函数表示 : dest 和 src 的前 num项比较大小
个人表示对这个函数很无语,感觉就是个鸡肋
函数模拟实现代码:
注: num 大小的处理
len > strlen(src)时len - strlen(src)的长度空间为空,需自动略去
当 len < strlen(src),只用比较strlen(src) - len 长度的字符
#include
#include
#include //使用断言函数必须包含头文件
#include
int my_strncmp(const char* dest, const char* src, size_t len)
{
int i = 0;
int j = 0;
assert(dest != NULL);
assert(src != NULL);
//首先判断并处理 len > strlen(src) 的情况
if(len > strlen(src))//len > strlen(src)时len - strlen(src)的长度空间为空,需自动略去
{
len = strlen(src);
}
//当 len < strlen(src),只用比较strlen(src) - len 长度的字符
if(len < strlen(src))
{
len = strlen(src) - len;
}
while(*dest++)
{
i++;
}
while(len--)
{
j++;
}
return i - j;
}
int main ()
{
char str1[1024] = "abcdefg"; //目标字符串的空间必须足够大,推荐写成1024
char* str2 = "hijk";
int size = 20; //此处的size大小应严格考虑再赋值
int ret = 0;
ret = my_strncmp(str1, str2, size);
if(ret > 0)
{
printf("str1 大于 str2!\n");
}
else if(ret < 0)
{
printf("str1 小于 str2!\n");
}
else
{
printf("str1 等于 str2!\n");
}
printf("my_strncmp的结果为:%d\n", ret);
system("pause");
return 0;
}
char* strstr(const char* dest, const char* src);
为实现判断字符串 dest 中是否包含字符串 src
注: 返回值为指针类型 包含则: 返回dest中和src首元素相同元素的指针;
不包含:返回空指针(NULL)。
函数模拟实现图解:
注: 需设定好三个指向分别指向 dest 和 src 如下图 分别黑、红、蓝三个指针
三个指针分别指向dest和src的开始的位置
红指针和蓝指针一起移动并做比较,当红指针和蓝指针指向的内容不同时,黑指针加一如下图
此时黑指针和和红指针都指向b, 蓝指针重新指向src的起始位置
重复图二 和 图三 的步骤直到达到下图的条件
蓝指针指向了 '\0',条件达成。
函数模拟实现代码:
#include
#include
#include //使用断言函数必须包含头文件
#include
char* my_strstr(const char* dest, const char* src)
//建议结合解释图来看
{
char* black_ptr = (char*)dest;
assert(dest != NULL);
assert(src != NULL);
assert(dest != '\0'); //判断字符串包含时如果字符串中只有 '\0'
assert(src != '\0'); //则必然包含,所以也许断言
while(*black_ptr)
{
char* red_ptr = black_ptr;
char* blue_ptr = (char*)src;
while(*red_ptr && *blue_ptr && *red_ptr == *blue_ptr)//表示图中的条件
{
red_ptr++;
blue_ptr++;
}
if(*blue_ptr == '\0')//当蓝指针指向'\0'时表示找到了
{
return black_ptr;
}
black_ptr++;
}
return NULL;
}
int main ()
{
char str1[1024] = "abcdefg";
char* str2 = "cde";
char* ret;
ret = my_strstr(str1, str2);
printf("my_strstr的结果为:%s\n", ret);
system("pause");
return 0;
}
KMP算法用来专门解决字符串包含的问题: 目前我还处于了解阶段,等以后掌握一定的按自己的理解写出来
给大家推荐大神写的 KMP 算法理解实现,特别棒
https://blog.csdn.net/starstar1992/article/details/54913261
char *strchr(const char *dest, char ch) ;
查找字符串s中首次出现c字符的位置
说明: 返回首次出现c的位置的指针,返回的地址是被查找的字符串指针开始的第一个与c相同字符的指针,若s中不存在c则返回 NULL。
返回值: 成功返回要查找的字符第一次出现的位置,否则返回NULL。
函数模拟实现代码:
#include
#include
#include //使用断言函数必须包含头文件
#include
char* My_strchr(const char* dest, char ch)
{
char* start = (char*)dest;
assert(dest != NULL);
assert(dest != '\0');
assert(ch != '\0');
while(*start && *start != ch)
{
start++;
}
if(*start == ch)
{
return start;
}
return NULL;
}
int main()
{
char* str = "abcdefgh";
char ch = 'd';
printf("strchr的结果为:%s\n", My_strchr(str, ch));
system("pause");
return 0;
}
char* strtok (char* dest, const char* src);
为实现 把 dest 以 src内标记的方式切分开来
函数使用示例:
************************************************************
函数实现: 这个函数应该是字符串函数中实现最复杂的一个,过程比较杂,一般面试中是不会提出这个问题的
等我掌握之后再补上具体实现。
char* strerror (int errnum);
注: 使用是必须包含头文件 < errno.h > 是操作系统中的错误码
具体功能: 如果库函数或操作系统函数执行出错时,就会给这个 errno 设置一个特定值,根据 errno 的值了解到出错的具体原因。
使用演示代码:
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include //使用断言函数必须包含头文件
#include
#include //使用strerror必须包含的头文件
int main()
{
FILE* pFile;
pFile = fopen ("unexist.ent", "r");
if(pFile == NULL)
{
printf("error opening file unexist.ent: %s\n", strerror(errno));
}//结果为: No such file or directory
printf("%d\n", errno); //第二种类型错误
system("pause");
return 0;
}
void* memcpy(void* dest, const void* src, size_t num);
函数实现,把src 开始的位置复制到 dest中 '\0' 开始的位置。
函数遇到 '\0' ,并不会停下来,复制的长度和结束都由num来决定。
函数返回 指针dest的起始地址。
注: 如果 dest 和 src 有任何的重叠, 复制结果都是未定义的(无法解决缓冲区重合的问题)。
函数模拟实现代码:
#include
#include
#include
#include
char* My_memcpy(char* dest, const char* src, size_t len)
{
char* ret = dest;
assert(dest != NULL);
assert(src != NULL);
while(len--) //len刚好等于src的长度时,循环这样写
{
*dest++ = *src++;
}
*dest = '\0';
return ret;
}
char* My_memcpy1(char* dest, const char* src, size_t len)
{
char* ret = dest;
int offset = 0;
assert(dest != NULL);
assert(src != NULL);
if(len > strlen(src)) //当len大于strlen(src)时
{
offset = len - strlen(src);
len = strlen(src);
}
while(len--)
{
*dest++ = *src++;
}
if(offset > 0)
{
while(offset--) //len大于strlen(src)的长度全部赋值为 '\0'
{
*dest++ = '\0';
}
}
*dest = '\0';
return ret;
}
int main()
{
char str1[1024] = "supreme";
char* str2 = "doit!";
int len = 0;
printf("%s\n", My_memcpy(str1, str2, strlen(str2)));
printf("请输入要复制str2 的长度(小于strlen(str1)):");
scanf("%d", &len);
printf("%s\n", My_memcpy1(str1, str2, len));
system("pause");
return 0;
}
注: 代码需要 考虑传递参数 len的长度
void* memmove(void* dest, const void* src, size_t num);
此函数和memcpy 的区别 可解决缓冲区重叠的问题。
具体方法: 当内存重合时应从后往前 拷贝。
函数模拟实现代码:
#include
#include
#include
#include
//memmove 与 memcpy主要的区别就是 可以解决缓冲区重合(内存重合)的问题
char* My_memmove(char* dest, const char* src, size_t len)
{
char* ret = dest;
int offset = 0;
assert(dest != NULL);
assert(src != NULL);
if(len > strlen(src)) //当len大于strlen(src)时
{
offset = len - strlen(src);
len = strlen(src);
}
//判断内存是否重合,是则从后往前复制
if(dest >= src && dest <= src + len - 1)
{
char* tem = NULL;
dest = dest + len - 1;
src = src + len - 1;
tem = dest;
while(len--)
{
*dest-- = *src--;
}
*tem = '\0';
}
else
{
while(len--)
{
*dest++ = *src++;
}
*dest = '\0';
}
if(offset > 0)
{
while(offset--) //len大于strlen(src)的长度全部赋值为 '\0'
{
*dest++ = '\0';
}
}
return ret;
}
int main()
{
char str1[1024] = "supreme";
char* str2 = "doit!";
int len = 0;
printf("请输入要复制str2 的长度(小于strlen(str1)):");
scanf("%d", &len);
printf("%s\n", My_memmove(str1, str2, len));
system("pause");
return 0;
}
注: 1. 考虑len长度过长
2.考虑两个字符串缓冲区重合的问题。
void* memset(void* dest, void ch, size_t len);
以dest为起始位置的n个字节的内存区域用整数set来进行填充,len为要填充的字节数,返回值为目标dest内存的起始地址
特别说明:len表示的是字节数,函数是以字节的形式每次赋值给目标地址
注: 考虑len的大小应小于dest的长度,否则出现内存越界行为。
函数模拟实现代码:
#include
#include
#include
#include
char *my_memset(char *dest, int set, size_t len)
{
char *ret = dest;
if (dest == NULL || len < 0)
{
return NULL;
}
while (len--)
{
*dest++ = set;
}
return ret;
}
int main()
{
char str[] = "hello worlddsfsafsfsfas";
int set = 4;
printf("%s\n", my_memset(str, set, strlen(str)));
system("pause");
return 0;
}
int memcmp(const char* dest, const char* src, size_t len);
比较内存区域 dest 和 src 的前 len 个字节。
返回值
当dest < src时,返回值 < 0
当dest = src时,返回值 = 0
当dest > src时,返回值 > 0
注:
该函数是按字节比较的。
例如:
s1,s2为字符串时候memcmp(s1,s2,1)就是比较s1和s2的第一个字节的ascII码值;
memcmp(s1,s2,n)就是比较s1和s2的前n个字节的ascII码值;
函数模拟实现代码:
#include
#include
#include
#include
int My_memcmp(const char* dest, const char* src, size_t len)
{
assert(dest != NULL);
assert(src != NULL);
while(len--)
{
while(*dest++ == *src++)
{
if(*dest == '\0')
{
return 0;
}
}
}
if(*dest > *src)
{
return 1;
}
if(*dest < *src)
{
return -1;
}
}
int main()
{
char str1[1024] = "ABCDE";
char* str2 = "abcd";
int ret = 0;
int len = 0;
printf("请输入要比较的两个字符串的长度(小于strlen(str1)):");
scanf("%d", &len);
ret = My_memcmp(str1, str2, len);
if(ret > 0)
{
printf("str1的前len项大于str2 的前len项!\n");
}
if(ret < 0)
{
printf("str1的前len项小于str2 的前len项!\n");
}
else
{
printf("两字符串前len项相同!\n");
}
system("pause");
return 0;
}