C语言中对字符和字符串的处理很是频繁,但是,C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。
常量字符串适用于那些对它不做修改的字符串函数。
1.字符串函数的介绍及其模拟实现
1.strlen
size_t strlen (const char* str);1.字符串是用'\0'作为结束标志的,strlen函数返回是的从给定位置开始到'\0'之间的字符个数,不包括'\0';
2.参数指向的字符串必须要以'\0'结束,否则可能会出现越界访问等情况;
3.strln函数的返回值是size_t,是无符号整型;
使用:
#include
#include int main()
{
char* arr = "abcdef";
char arr2[] = "abcdefgh";
int len = strlen(arr);
int len2 = strlen(arr2);
printf("%d\n", len);
printf("%d\n", len2);
return 0;
}
记得要引头文件
下面这种情况也要注意,因为strlen的返回值类型是size_t的,所以最后得到的是一个很大的数,而不是-2
文章图片
接下里来模拟实现一下strlen函数
模拟实现:
1.计数器的方法:
#include size_t my_strlen(const char* str)
{
assert(str);
size_t count = 0;
while (*str++)
{
count++;
}
return count;
}
2.递归的方法:
#include size_t my_strlen(const char* str)
{
assert(str);
if (*str == '\0')
{
return 0;
}
return 1 + my_strlen(++str);
}
3.指针-指针的方法:
#include size_t my_strlen(const char* str)
{
assert(str);
const char* p = str;
while (*str)
{
str++;
}
return str - p;
}
2.strcpy
char* strcpy(char* destination, const char* source);1.源字符串必须以'\0'结束;
2. strcpy函数会将源字符串中的'\0'拷贝到目标空间;
3.目标空间必须足够大,以确保放得下源字符串,否则可能会越界访问;
4.目标空间必须可以被改变,如果是常量字符串则不能被改变;
int main()
{
char* str1 = "abcdef";
char* str2 = "abcde";
char* ret = strcpy(str1, str2);
printf("%s\n", ret);
return 0;
}
像这种情况,str1所指向的字符串是不能被改变的
使用:
文章图片
strcpy返回值是个char*类型的,是为了能够实现链式访问,即一个函数的返回值作为另一个函数的参数。
如:
int main()
{
char arr1[] = "qwertyuiop";
char arr2[] = "abc";
printf("%s\n", strcpy(arr1, arr2));
return 0;
}
模拟实现:
char* my_strcpy(char* dest, const char* src)
{
assert(dest && src);
char* cur = dest;
while (*src)
{
*dest = *src;
dest++;
src++;
}
*dest = *src;
return cur;
}
可以对代码进行简化:
char* my_strcpy(char* dest, const char* src)
{
assert(dest && src);
char* cur = dest;
while (*dest++ = *src++)
{
;
}
return cur;
}
3.strcat
char* strcat(char* destination, const char* source);1.源字符串必须以'\0'结束;
2.目标空间必须足够大,可以容纳得下源字符串的内容‘
3.目标空间必须可以被改变;
使用:
文章图片
模拟实现:
从目标字符串的'\0'位置上开始追加源字符串
char* my_strcat(char* dest, const char* src)
{
assert(dest && src);
char* cur = dest;
while (*dest)
{
dest++;
}
while (*dest++ = *src++)
{
;
}
return cur;
}
4.strcmp
int strcmp(const char* str1, const char* str2);从两个字符串的给定字符位置开始进行比较,如果对应位置上的字符相同,则继续比较下一对字符,直到遇到'\0'或者两个字符不同为止;
标准规定:
1.第一个字符串大于第二个字符串,则返回大于0的数字;
2.第一个字符串等于第二个字符串,则返回0;
3.第一个字符串小于第二个字符串,则返回小于0的数字;
使用:
文章图片
在VS编译器中,strcmp只会返回-1,0,1三个数字。
模拟实现:
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 == *str2 && *str1 != '\0')
{
str1++;
str2++;
}
if (*str1 > *str2)
{
return 1;
}
else if (*str1 < *str2)
{
return -1;
}
else
{
return 0;
}
}
5.strncpy
char* strncpy(char* destination, const char* source, size_t num);1.从源字符串拷贝num个字符到目标空间;
2.如果源字符串的个数小于num,则在拷贝完源字符串后,在目标的后面追加'\0',直到字符总数达到num个为止;
使用:
文章图片
文章图片
模拟实现:
char* my_strncpy(char* dest, const char* src, size_t num)
{
assert(dest && src);
size_t len = strlen(src);
if (num <= len)
{
int i = 0;
for (i = 0;
i < num;
i++)
{
*(dest + i) = *(src + i);
}
}
else
{
int i = 0;
for (i = 0;
i < len;
i++)
{
*(dest + i) = *(src + i);
}
for (;
i < num;
i++)
{
*(dest + i) = '\0';
}
}
return dest;
}
6.strncat
char* strncat(char* destination, const char* source, size_t num);使用:
文章图片
模拟实现:
char* my_strncat(char* dest, const char* src, size_t num)
{
assert(dest && src);
char* cur = dest;
while (*dest)
{
dest++;
}
size_t len = strlen(src);
if (num < len)
{
int i = 0;
for (i = 0;
i < num;
i++)
{
*(dest + i) = *(src + i);
}
*(dest + i) = '\0';
}
else
{
while (*dest++ = *src++)
{
;
}
}
return cur;
}
7.strncmp
int strncmp(const char* str1, const char* str2, size_t num);使用:
文章图片
模拟实现:
int my_strncmp(const char* str1, const char* str2, size_t num)
{
assert(str1 && str2);
size_t i = 0;
while (*str1 == *str2 && *str1 != '\0' && i < num - 1)
{
str1++;
str2++;
i++;
}
if (*str1 > *str2)
{
return 1;
}
else if (*str1 < *str2)
{
return -1;
}
else
{
return 0;
}
}
函数内部实现方法是比较两个字符的大小,相同的话指针就向后走一个字符大小,如果num个字符中前面都相同的话,最后是要比较第num对字符的大小的,所以判断语句while (*str1 == *str2 && *str1 != '\0' && i < num - 1)中num要-1.
8.strstr
char* strstr(const char* str1, const char* str2);str1为被搜索的字符串,str2为要检索出来的子字符串,如果在str1中找到了str2,那么就返回str1中找到str2时的起始位置的地址,如果str1中找不到str2,那么返回一个空指针。
使用:
文章图片
模拟实现:
思路:用一个cur指针指向str1的第一个字符,然后再拿一个s1指针去跟随着cur指针,用s2指针去指向str2的第一个字符,当cur指向第一个字符的时候,拿s1指向的字符去和s2指针指向的字符进行一个比较,要是不同,那么就让cur指针+1,s1和cur一样,当*s1和*s2相同的时候,让s1和s2同时往后走,继续比较字符,要是一直相同,那么就会出现3种情况,第一种就是str1字符串已经走到'\0'的位置了,但是str2还没有走完,那么此时退出s1和s2不断增加的循环,cur+1,s1回退到此时cur+1的位置,s2也重新回退到str2其实字符的位置,重新开始匹配相同的字符,不过要是出现这种情况的话,大概率后面的也会不相同,第2种情况,就是s1和s2一直往后走,直到s2指向的字符是'\0',那么此时,str2已经遍历完了,说明str2是str1的子字符串,那么也会退出循环,下一步再判断*s2是否是'\0',是的话就把此时cur作为返回值返回。第3中情况是前面的字符串相同,中间的不同,那么也会退出循环,让cur+1,然后把s1的位置回退到cur的位置,s2的位置也回退到str2起始字符串的位置,重新开始匹配。
char* my_strstr(const char* str1, const char* str2)
{
assert(str1 && str2);
const char* s1 = str1;
const char* s2 = str2;
const char* cur = str1;
while (*cur)
{
s1 = cur;
s2 = str2;
while (*s1 && *s2 && (* s1 == *s2))
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return (char*)cur;
}
cur++;
}
return NULL;
}
9.strtok
char* strtok(char* str, const char* sep);1.sep参数是个字符串,定义了用作分割符的字符集合
【c语言|C语言中的字符串函数和字符函数】2.第一个参数str是个字符串,它包含了0个或者多个在sep字符串中存在的作为分割符的字符作为标记
3.strtok函数会在str中找到下一个标记,并将其用'\0'结尾,返回一个指向这个标记的指针。strtok函数会改变被操作的字符串,所以在用strtok对字符串进行分割时一般用临时拷贝的字符串,并且字符串可以被改变。
4.strtok函数的第一个标志不为NULL,函数将找到str中的第一个标记,strtok函数将会保存这个标记在字符串中的位置。
5.strtok函数的第一个参数为NULL,函数将从同一个字符串中保存的位置开始,查找下一个标记。
6.如果字符串中不存在更多的标记,则返回空指针
使用:
strtok函数将会返回分割出来的字符串的首字符地址,我们借助一个循环,就能把想要分割的字符串依次打印出来了
#include
#include int main()
{
char* arr = "0zhangsan@new.net.";
char buff[30];
strcpy(buff, arr);
char* sep = "@.0";
char* ret = NULL;
for (ret = strtok(buff, sep);
ret != NULL;
ret = strtok(NULL, sep))
{
printf("%s\n", ret);
}
return 0;
}
10.strerror
char* strerror(int errnum);strerror是用来返回错误码所对应的错误信息,平常上网有时会出现的404就是错误码。
使用:
文章图片
strerror会返回对应错误信息的首字符地址
11.memcpy
void* memcpy(void* destination, const void* source, size_t num);1.memcpy会从source的起始位置开始向后复制num个字节的数据到memcpy的内存位置
2.这个函数在遇到'\0'的时候并不会停下来
3.如果source和destination由任何的重叠,复制的结果都是未定义的
使用:
文章图片
模拟实现:
void* my_memcpy(void* dest, const void* src, size_t num)
{
assert(dest && src);
void* ret = dest;
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = https://www.it610.com/article/(char*)src + 1;
}
return ret;
}
而如果在同一块空间进行拷贝可能会出现这种情况:
文章图片
这是因为数据是从前往后一个字节一个字节拷贝过去的,后面的将要拷贝的数据会被前面的数据给覆盖了,导致拷贝的数据发生了变化,而visual studio这款编译器将memcpy的功能已经实现到和memmove的功能差不多了,因此如果用库函数提供的memcpy去在同一块空间进行拷贝的话,不会发生这种情况。
文章图片
12.memmove
void* memmove(void* destination, const void* source, size_t num);1.和memcpy的差别是源内存块和目标内存块是可以重叠的,如果源空间和目标空间出现了重叠,就可以通过memcpy函数来进行处理
使用:
文章图片
模拟实现:
思路:
两块内存空间重叠分为两种情况:
文章图片
第一种,如果src在dest的前面的话,那么就将数据从后往前依次拷贝,先将5拷到8的位置,再将4拷到7的位置,依次类推。
文章图片
第二种,当src再dest的后面,那么就将数据从前往后依次拷贝,先将6拷到4的位置,再将7拷到5的位置,依次类推。
而如果两块内存空间没有重叠,则从前往后或者从后往前都可以拷贝。
代码:
void* my_memmove(void* dest, const void* src, size_t num)
{
//stc在dest的前面
if (src < dest)
{
while (num--)
{
*((char*)dest + num) = *((char*)src + num);
}
return dest;
}
//src在dest的后面
else
{
void* ret = dest;
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = https://www.it610.com/article/(char*)src + 1;
}
return ret;
}
}
13.memcmp
int memcmp(const void* ptr1, const void* ptr2, size_t num);1.比较从ptr1指针和ptr2指针开始的num个字节的数据,和strcmp有点相似,返回值也是分为<0、==0和 >0三种。
使用:
文章图片
模拟实现:
int my_memcmp(const void* ptr1, const void* ptr2, size_t num)
{
num -= 1;
while (num-- && (*(char*)ptr1 == *(char*)ptr2))
{
ptr1 = (char*)ptr1 + 1;
ptr2 = (char*)ptr2 + 1;
}
if ((*(char*)ptr1 == *(char*)ptr2))
{
return 0;
}
else if (*(char*)ptr1 > *(char*)ptr2)
{
return 1;
}
else
{
return -1;
}
}
因为指针+num-1就到要比较最后一个字节大小空间的位置了了,所以一开始num就要-1.
2.字符函数的介绍
函数 | 如果它的参数符合下列条件就返回真(非0表示真) |
iscntrl | 任何控制字符 |
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 | 任何可打印字符,包括图形字符和空白字符 |
注意:参数会被转换成ASCII码
int main()
{
printf("%d\n", iscntrl(1));
printf("%d\n", isspace(3));
printf("%d\n", isdigit(48));
printf("%d\n", isxdigit(49));
printf("%d\n", islower('A'));
printf("%d\n", isupper('A'));
printf("%d\n", isalpha('a'));
printf("%d\n", isalnum('a'));
printf("%d\n", ispunct('.'));
printf("%d\n", isgraph('#'));
printf("%d\n", isprint('3'));
return 0;
}
字符转换:
int toupper(int c);使用:
int tolower(int c);
int main()
{
putchar(toupper('c'));
printf("\n");
putchar(tolower('C'));
return 0;
}
关于字符串函数和字符函数的内容就到这里了,今后也会不定期更新
推荐阅读
- c语言|C语言中的指针(进阶)
- 数据库|C#对Access数据库的操作
- ADO.NET|C#数据库教程7-ADO.NET三层架构和数据库DBNull问题
- C# Winform 服务器和多个客户端通讯
- 注册表限制软件使用次数
- 数值算法|分段线性插值 C#代码
- C#|C#数据库教程1-使用ADO.NET操作sql server 2012
- C#|C#数据库教程2-ADO.NET常用SQL语句
- C#|C# Udp测试工具开发