C语言中有好多针对于字符串的函数,今天我来给大家浅谈一下我的认识以及给大家介绍一下各种函数的优缺点。
1. strcpy(字符串拷贝函数)
- 原型声明:
char *strcpy(char* dest, const char *src);
- 头文件:
#include <string.h> 和 #include <stdio.h>
- 功能:把从src地址开始且含有NULL结束符的字符串复制到以dest开始的地址空间
- 说明:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符 串。返回指向dest的指针。
char* my_strcpy(char* dest, const char* src)
{
assert(dest != NULL);
assert(src != NULL);
char* ret = dest;
while (*dest++ = *src++)
{
;
}
return ret;
}
有几点是需要注意的哈
- 指针 src 所指向的内容是不会变的,所以需要加上 const 修饰。
- 函数my_strcpy的返回值是指针的哈,因此函数的返回值应该为 char*。
- 加上 asser 断言,谨防指针为空。
2. strlen(计算字符串的长度)
- 头文件:
string.h
- 格式:
strlen (字符数组名)
- 功能:计算给定字符串的(unsigned int型)长度,不包括’\0’在内
- 说明:返回s的长度,不包括结束符NUL。
(1). 第一种实现方式
- 采用定义一个新变量,让它遍历字符串。此方法比较直观,但是不够具有档次。
int my_strlen(const char* arr)
{
assert(arr != NULL);
int count = ;
while (*arr)
{
arr++;
count++;
}
return count;
}
(2). 第二种实现方式
- 这种方式采用函数递归,稍微来了一点境界,但是字符串过长的话,算法复杂度太高。
int my_strlen(const char* arr)
{
assert(arr != NULL);
if (*arr)
return + my_strlen(arr + );
else
return ;
}
(3). 第三种实现方式
- 创建一个新指针,让这个新指针指向字符串的末尾。那么字符串的末尾指针减去初始指针就是两指针之间所包含的元素。这种方法采用指针就比较上档次了。
int my_strlen(const char* arr)
{
assert(arr != NULL);
char* p = arr;
while (*p)
{
p++;
}
return p - arr;
}
有几点是需要注意的哈
- 指针 arr 所指向的内容是不会变的,所以需要加上 const 修饰。
- 函数my_strlen的返回值是元素个数的哈,因此函数的返回值应该为 int。
- 加上 asser 断言,谨防指针为空。
3. strcat(字符串拼接函数)
- 原型 :
extern char *strcat(char *dest, const char *src);
- 头文件:在C语言中,函数原型存在
<string.h>
头文件中。 - 功能:把src所指字符串添加到dest结尾处(覆盖dest结尾处的’\0’)。
- 说明:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。返回指向dest的指针。
char* my_strcat(char* dest, const char* src)
{
assert(dest != NULL);
assert(src != NULL);
char* ret = dest;
while (*dest)
{
dest++;
}
while (*dest++ = *src++)
{
;
}
return ret;
}
有几点是需要注意的哈
- 指针 src 所指向的内容是不会变的,所以需要加上 const 修饰。
- 函数my_strcat的返回值是指针的哈,因此函数的返回值应该为 char*。
- 加上 asser 断言,谨防指针为空。
3. strstr(字符串查找子字符串函数)
- 头文件:
string.h
- 函数原型:
extern char *strstr(char *dest, const char *src);
- 功能:
* strstr(dest, src)
- str1: 被查找目标。 str2:要查找对象
- 返回值:若str2是str1的子串,则返回str2在str1的首次出现的地址;如果str2不是str1的子串,则返回NULL。
char* my_strstr(const char* dest, const char* src)
{
assert(dest);
assert(src);
char* arr = (char*)dest;
char* brr = (char*)src;
char* p = NULL;
if (*brr == '\0') //子字符串如果为空,不用查找,直接返回空
return NULL;
while (*arr)
{
p = arr;
brr = src;
while (*p && *brr && (*p == *brr))
{
p++;
brr++;
}
if (*brr == '\0')
return arr;
arr++;
}
}
有几点是需要注意的哈
- 这个代码需要考虑的问题,比如 被查找的字符串中为
abbbbbbcde
而要查找的子字符为bcde
,这时我采用*p && *brr && (*p == *brr)
这个条件来控制循环。外层的大循环采用被查找的字符串是否为空。如果要查找的字符串指向空,则返回此时被查找字符串的位置。 - 指针dest, src 所指向的内容是不会变的,所以需要加上 const 修饰。
- 函数my_strstr的返回值是指针的哈,因此函数的返回值应该为 char*。
- 加上 asser 断言,谨防指针为空。
4. strchr(查找字符串 arr 中首次出现字符 c 的位置)
- 头文件:
#include <string.h>
- 功能:查找字符串
arr
中首次出现字符c
的位置 - 说明:返回首次出现
c
的位置的指针,返回的地址是被查找字符串指针开始的第 一个与c
相同字符的指针,如果arr
中不存在c
则返回NULL
。 - 返回值:成功则返回要查找字符第一次出现的位置,失败返回
NULL
char* my_strchr(const char* arr, char c)
{
assert(arr);
while (*arr)
{
if (*arr == c)
{
return arr;
}
arr++;
}
}
有几点是需要注意的哈
- 指针
arr
所指向的内容是不会变的,所以需要加上 const 修饰。 - 函数
my_strchr
的返回值是指针的哈,因此函数的返回值应该为 char*。 - 加上 asser 断言,谨防指针为空。
5. strcmp(字符串比较函数)
- 头文件:
#include <string.h>
- 功能:比较两个字符串,从两个字符串的第一个字符开始比较,如果一方的字符大,那么就不往后面比较了,直接返回字符大的那个字符串。
- 说明:设这两个字符串为str1,str2
- 若
str1==str2
,则返回零; - 若
str1<str2
,则返回负数; - 若
str1>str2
,则返回正数。
int my_strcmp(const char * src, const char * dst)
{
int ret = ;
while (!(ret = *(unsigned char *)src - *(unsigned char *)dst)
&& *dst )
++src, ++dst;
if (ret < )
ret = -;
else if (ret > )
ret = ;
return(ret);
}
有几点是需要注意的哈
- 指针
src
dst
所指向的内容是不会变的,所以需要加上 const 修饰。 - 函数
my_strcmp
的返回值是指针的哈,因此函数的返回值应该为 char*。 - 加上 asser 断言,谨防指针为空。
- 这里面只能比较字符串,即可用于比较两个字符串常量,或比较数组和字符串常量,不能比较数字等其他形式的参数。
6. memcpy(字符串拷贝函数)
- 头文件:
#include <string.h>
- 功能:从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中
- 返回值:函数返回指向dest的指针。
char* my_memcpy(void* dest, const void* src,size_t count)
{
char* p = dest;
while (count--)
{
*(char*)dest = *(char*)src;
++(char*)dest;
++(char*)src;
}
//*dst = '\0';
return p;
}
有几点是需要注意的哈
dest
和src
所指的内存区域可能重叠,但是如果dest
和src
所指的内存区域重叠,那么这个函数并不能够确保dest
所在重叠区域在拷贝之前不被覆盖。而使用memmove
可以用来处理重叠区域。函数返回指向src
的指针.- 如果目标数组
src
本身已有数据,执行memcpy()
后,将覆盖原有数据(最多覆盖n)。如果要追加数据,则每次执行memcpy
后,要将目标数组地址增加到你要追加数据的地址。 dest
和src
都不一定是数组,任意的可读写的空间均可。- 指针
src
dst
所指向的内容是不会变的,所以需要加上 const 修饰。 - 函数
my_memcpy
的返回值是指针的哈,因此函数的返回值应该为 char*。 - 加上
asser
断言,谨防指针为空。
7. memmove(字符串拷贝函数)
- 头文件:
<string.h>
- 功能:由
src
所指内存区域复制count个字节到dest
所指内存区域。 - 返回值:函数返回指向
dest
的指针。
void * my_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;
++(char *)dst;
++(char *)src;
}
}
else //当源内存的首地址小于目标内存的首地址时,实行反向拷贝
{
dst = (char *)dst + count - ;
src = (char *)src + count - ;
while (count--)
{
*(char *)dst = *(char *)src;
--(char *)dst;
--(char *)src;
}
}
return(ret);
}
写成这种形式也是可以的。
void* my_memmove(void* dst, const void* src, rsize_t count)
{
assert(dst);
assert(src);
char* tmp_dst = (char*)dst;
const char* tmp_src = (const char*)src;
if (tmp_src < tmp_dst) //当src地址小于dest地址时,从头进行拷贝
{
while (count--)
*tmp_dst++ = *tmp_src++;
}
if (tmp_src > tmp_dst) //当src地址大于dest地址时,从后进行拷贝
{
tmp_dst += count - ;
tmp_src += count - ;
while (count--)
*tmp_dst-- = *tmp_src--;
}
//if (tmp_dst == tmp_src) 此时不进行任何操作
return dst;
}
有几点是需要注意的哈
memmove
用于从src
拷贝count
个字节到dest
,如果目标区域和源区域有重叠的话,memmove
能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中。但复制后src
内容会被更改。但是当目标区域与源区域没有重叠则和memcpy
函数功能相同。- 指针
src
所指向的内容是不会变的,所以需要加上 const 修饰。 - 函数
my_memmove
的返回值是指针的哈,因此函数的返回值应该为 void*。 - 加上
asser
断言,谨防指针为空。 dest
和src
都不一定是数组,任意的可读写的空间均可。所以定义为void
类型。
我再来说几句关于strcpy
memcpy
和memmove
的异同
memcpy
与memmove
都是对内存进行拷贝可以拷贝任何内容,而strcpy
仅是对字符串进行操作。memcpy
与memmove
拷贝多少是通过其第三个参数进行控制而strcpy
是当拷贝至'\0'
停止。memcpy
函数的功能是从源src
所指的内存地址的起始位置开始拷贝N
个字节到目标dst
所指的内存地址的起始位置中。memmove
函数的功能同memcpy
基本一致,但是当src
区域和dst
内存区域重叠时,memcpy
可能会出现错误,而memmove
能正确进行拷贝。- 关于memmove是怎么拷贝的,我再来说说。
- 上述三种情况,
memcpy
可以成功对前两种进行拷贝,对第三种情况进行拷贝时,由于拷贝dst
前两个字节时覆盖了src
原来的内容,所以接下来的拷贝会出现错误。而memmove
对第三种情况进行拷贝时会从src
的最后向前拷贝N
个字节,避免了覆盖原来内容的过程。