前言: “字符串是一种重要的数据类型,但是C语言并没有显式的字符串数据类型,因为字符串以字符串常量的形式出现或者存储与字符数组中。” ——摘自《C和指针》
1. strlen
1.1 函数介绍
函数原型:size_t strlen( const char *string );
函数功能:获取字符串长度,即返回该字符串中'\0'前的字符个数。
如下所示,使用strlen函数计算字符串abcd的长度。
int main()
{
char array[] = "abcd";
int ret = strlen(array);
printf("%d", ret);
return 0;
}
1.2 函数复刻
方法1:使用指针相减得到字符串中字符个数。
//方法1:指针相减
size_t my_strlen(const char* string)
{
char* ptr = string;
while (*string != '\0')
{
string++;
}
return string - ptr;
}
方法2:使用递归求解。
//方法2:递归
size_t my_strlen(const char* string)
{
if (*string != '\0')
{
return 1 + my_strlen(++string);
}
return 0;
}
方法2:使用迭代计数。
//方法3:迭代计数 while循环
size_t my_strlen(const char* string)
{
int count = 0;
while (*string != '\0')
{
string++;
count++;
}
return count;
}
//迭代计数 for循环
size_t my_strlen(const char* string)
{
int count = 0;
for(count = 0; *string++ != '\0'; )
count++;
return count;
}
2. strcpy
2.1 函数介绍
函数原型:char *strcpy( char *strDestination, const char *strSource );
函数功能:实现字符串的拷贝,并返回拷贝后字符串的地址。
int main()
{
char strDestination[] = "abcd";
char strSource[] = "fk";
char* ret = strcpy(strDestination, strSource);
printf("%s", ret);
return 0;
}
通过调试可以清晰的看到字符串strDestination中的内容是被字符串strSource完全覆盖了,以此达到了字符串拷贝的效果。
2.2 函数复刻
方法:使用两个字符指针分别指向源字符串和目标字符串,将源字符串的内容赋值给目标字符串,直到源字符串'\0'时赋值完毕后结束循环。
char* my_strcpy(char* strDestination, const char* strSource)
{
char* ptr_strDestination = strDestination;
char* ptr_strSource = strSource;
while (*ptr_strDestination++ = *ptr_strSource++);
return strDestination;
}
3. strcat
3.1 函数介绍
函数原型:char *strcat( char *strDestination, const char *strSource );
函数功能:实现字符串的追加,并返回追加后字符串的地址。
int main()
{
char strDestination[20] = "abcd";
char strSource[5] = "fk";
char* ret = strcat(strDestination, strSource);
printf("%s", ret);
return 0;
}
通过调试可知字符串追加功能是从被追加字符串的'\0'位置处开始追加,再将追加字符串拷贝至被追加字符串,这就要求被追加字符串需要拥有足够大的空间,否则会导致越界。
3.2 函数复刻
方法:使用一个字符指针指向被追加字符串,指针不断指向下一位置,直到指针所指向位置的值为'\0',停止循环,而后重复字符拷贝的过程,实现字符追加。
char* my_strcat(char* strDestination, const char* strSource)
{
char* ptr_strDestination = strDestination;
while (*strDestination != '\0')
{
strDestination++;
}
while (*strDestination++ = *strSource++);
return ptr_strDestination;
}
4. strcmp
4.1 函数介绍
函数原型:int strcmp( const char *string1, const char *string2 );
函数功能:实现字符串的比较(比较字符串中每个字符的ASCII码值)。string1 < string2 返回小于0的值,string1 = string2 返回0,string1 > string2 返回大于0的值。
4.2 函数复刻
方法:当两个字符串字符相等时,指向两个字符串的指针分别向后移动一次,当碰到'\0'时表示两个字符串相等,返回0,若两个字符串中字符不等,则返回两个字符串中字符ASCII的差值。
int my_strcmp(const char* string1, const char* string2)
{
while (*string1 == *string2)
{
if (*string1 == '\0')
{
return 0;
}
string1++;
string2++;
}
return *string1 - *string2;
}
5. strstr
5.1 函数介绍
函数原型:char *strstr( const char *string, const char *strCharSet );
函数功能:实现字符串中子串的查找,如果没有找到返回空指针,找到了返回子串在字符串中第一个位置的地址。
例如:查找字符串abbcd中的子串bcd,返回位置应为第二个b的地址,下方代码结果应打印bcd。
int main()
{
char string[20] = "abbcd";
char strCharSet[5] = "b";
char* ret = strstr(string, strCharSet);
printf("%s", ret);
return 0;
}
5.2 函数复刻
方法:使用三个指针,ptr_strCharSet指向子串,ptr_string指向源字符串, cur用以标记查找位置。具体查找过程如图所示。
char* my_strstr(const char* string,const char* strCharSet)
{
char* ptr_string;
char* ptr_strCharSet;
char* cur = string;
if (*strCharSet == '\0')
{
return string;
}
while (*cur)
{
ptr_string = cur;
ptr_strCharSet = strCharSet;
while (*ptr_strCharSet && *ptr_string && *ptr_string == *ptr_strCharSet)
{
ptr_string++;
ptr_strCharSet++;
}
if (*ptr_strCharSet == '\0')
{
return cur;
}
cur++;
}
return NULL;
}
6. strncpy
6.1 函数介绍
函数原型:char *strncpy( char *strDest, const char *strSource, size_t count );
函数功能:实现指定数量的字符拷贝。strncpy函数将strSource的初始计数字符复制到strDest并返回strDest。如果count小于或等于strSource的长度,则不会自动将空字符追加到复制的字符串中。如果count大于strSource的长度,目标字符串将被填充到长度count之前的空字符。
6.2 函数复刻
char* my_strncpy(char* strDest, const char* strSource, size_t count)
{
int i = 0;
char* ptr_strDest = strDest;
while (i++ < count && (*strDest++ = *strSource++) != '\0');
if (*(strDest) != '\0')
*strDest = '\0';
return ptr_strDest;
}
7. strncat
7.1 函数介绍
函数原型:char *strncat( char *strDest, const char *strSource, size_t count );
函数功能:追加指定个数的字符。在追加指定个数的字符后,末尾自动添加'\0'。
7.2 函数复刻
char* my_strncat(char* strDest, const char* strSource, size_t count)
{
char* ptr_strDest = strDest;
while (*strDest++ != '\0');
while (count--)
{
if((*strDest = *strSource) != '\0');
}
*strDest = '\0';
return ptr_strDest;
}
8. strncmp
8.1 函数介绍
函数原型:int strncmp( const char *string1, const char *string2, size_t count );
函数功能:实现指定字符个数的字符大小比较。
8.2 函数复刻
int my_strncmp(const char * str1, const char * str2, size_t num)
{
while (num--)
{
if ((*str1 == *str2) && (*str1 != 0) && (*str2 != 0))
{
str1++;
str2++;
}
}
return *str1 - *str2;
}
9. memcpy
9.1 函数介绍
函数原型:void *memcpy( void *dest, const void *src, size_t count );
函数功能:以字节为单位拷贝内存中存储的任意类型数据。需注意的是,如果源操作数与目标操作数重叠,那么拷贝结果是未定义的。
按以下代码执行,由于只从数组src中拷贝了4个字节的内容,所以只拷贝了100,打印结果应为100和0。
int main()
{
int dest[20] = {0};
int src[] = {100, 200};
int* ret = (int*)memcpy(dest, src, 4);
printf("%d %d", *ret, *(ret + 1));
return 0;
}
9.2 函数复刻
方法:将void*类型强转成char*的类型,确保可按字节进行拷贝,适应任何类型数据的拷贝。按需要拷贝的字节数进行循环,将源操作数拷贝至目标操作数。
void* my_memcpy(void* dest, const void* src, size_t count)
{
void* ret = dest;
while (count--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
void* my_memcpy(void* dest, const void* src, size_t count)
{
void* ret = dest;
while (count--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
int main()
{
int dest[20] = {1,2,3,4,5,6,7,8,9};
my_memcpy(dest + 2, dest, sizeof(int) * 5);
for (size_t i = 0; i < sizeof(int) * 5; i++)
{
printf("%d ", dest[i]);
}
return 0;
}
上述代码欲将数组dest中1,2,3,4,5拷贝至数组dest第三个位置,打印出1,2,1,2,3,4,5,7,8,9。然而实际效果如下,表明memcpy函数源操作数与目标操作数重叠时拷贝顺序是从前往后拷贝,导致拷贝结果被不停覆盖。
10. memmove
10.1 函数介绍
函数原型:void *memmove( void *dest, const void *src, size_t count );
函数功能:与memcpy功能类似,解决了memcpy源操作数与目标操作数重叠时产生的内存覆盖问题。
10.2 函数复刻
方法:首先需区分两种情况即dest在src之后或之前。若dest在src之前,拷贝顺序为从前向后;若dest在src之后,拷贝顺序从后向前依次拷贝。
void * my_memmove(void * destination, const void * source, size_t num)
{
void* ptr_destination = destination;
if (destination < source)
{
while (num--)
{
*(char*)destination = *(char*)source;
destination = (char*)destination + 1;
source = (char*)source + 1;
}
}
else
{
while (num--)
{
*((char*)destination + num) = *((char*)source + num);
}
}
return ptr_destination;
}
END