1、C语言中是否存在字符串?
2、函数介绍
2.1 strlen()
2.1.1模拟实现strlen()(三种方法实现)
2.1.2 注意点
2.1.3 strlen()函数介绍
2.2 strcpy()
2.2.1模拟实现strcpy()
2.2.2 注意事项
2.3 strcat()
2.3.1 模拟实现strcat()
2.3.2 注意事项
2.4 strcmp()
2.4.1 模拟实现strcmp()
2.4.2 注意事项
2.5 strncpy
2.5.1 strncpy()的模拟实现
2.5.2 注意事项
2.6 strncat()
2.6.1 模拟实现strncat()
2.7 strncmp()
2.7.1 模拟实现
2.8 strstr()
2.8.1 使用说明及示例
2.8.2 模拟实现strstr()
2.9 strtok()
2.9.1 使用说明及示例
2.10 strerror()
2.10.1 使用说明及示例
2.11 其它函数
2.11.1 字符分类函数
2.11.2 字符转换函数
3.1 memcpy()
3.1.1 使用说明及示例
3.1.2 模拟实现
3.2 memmove()
3.2.1 使用说明及示例
3.2.2 模拟实现
3.3 memcmp()
3.3.1 使用说明及示例
3.4 memset()
3.4.1 使用说明及示例
C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在 常量字符串中或者字符数组中。 字符串常量适用于那些对它不做修改的字符串函数!
strlen()函数的作用是求字符串的长度。
(1)计数器方法
#include<assert.h>
int my_strlen(const char* str)
{
assert(str != NULL);
int count = 0;
while (*str != '\0')//while(*str)---'\0'的ascii码值为0
{
count++;
str++;
}
return count;
}
(2)递归实现(不定义临时变量的方法)
#include<stdio.h>
int my_strlen(const char* str)
{
if (*str != '\0')
{
return 1+my_strlen(++str);
}
else
{
return 0;
}
}
(3)尾指针-头指针
#include<stdio.h>
#include<assert.h>
int my_strlen(const char* str)
{
const char* ret = NULL;
assert(*str != NULL);
ret = str;
while (*ret != "\0")
{
ret++;
}
return ret - str;
}
代码:
#include<stdio.h>
#include<string.h>
int main()
{
const char* str1 = "abcdef";
const char* str2 = "bbb";
if (strlen(str2) - strlen(str1) > 0)
{
printf("str2>str1\n");
}
else
{
printf("srt1>str2\n");
}
return 0;
}
运行截图:
原因:
strlen()函数的返回类型是size_t即unsigned int类型,在操作符两侧的数据类型都是unsigned int ,运算结果类型也必然是unsigned int,这个值永远是大于0的,所以会出现上面的结果!
结论
自己实现的时候将返回类型写成size_t和int各有利弊。
int可以有效的应对上面的这种特殊情况,但是不容易理解,因为int包括负数。
size_t更容易理解,但是无法有效的应对像上面的这种情况。
(1)字符串以 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' )。
(2)参数指向的字符串必须要以 '\0' 结束。
(3)注意函数的返回值为size_t,是无符号的( 易错 )
strcpy()函数的作用是将一个字符串的内容拷贝到另一个字符串中去(包括'\0')。
#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{
assert(dest != NULL);
assert(src != NULL);
char* ret = dest;
//拷贝src指向的字符串到dest指向的空间,包含'\0'
while (*dest++ = *src++)
{}
//返回目的空间的起始地址
return ret;
}
(1)源字符串必须以'\0'结束,比如源字符串像这样定义的就无法正常拷贝了:
char src[] = {'m','i','n'};
(2)要将'\0'拷贝到目标空间
(3)目标空间必须足够大,以确保能存放源字符串
(4)目标空间必须可变,比如目标字符串这样定义就不行了:
*char dest = "abcde";
因为此时的目标字符串所存放的空间是在字符串常量区,此处的字符串是无法进行修改的,此时程序会崩溃。
strcpy()的作用是追加字符串。
#include<stdio.h>
#include<assert.h>
char* my_strcat(char* str1, const char* str2)
{
assert(str1&&str2);
char* ret = str1;
//1、找到目的字符串中'\0'的位置
while (*str1 != '\0')
{
str1++;
}
//2、追加字符串(其实就是拷贝字符串)
while (*str1++ = *str2++)
{
;
}
return ret;
}
(1)源字符串必须以'\0'结束
(2)目标空间必须有足够大的空间能容纳下源字符串的内容
(3)目标空间必须可修改
(4)不能自己给自己追加,那样程序会崩溃。
比较对应字符对应的ASCII码值的大小,而不是比较字符串的长度,逐个字符进行比较,如果所有字符都相同就返回一个等于0的数组,如果前面的小于后面的,就返回一个小于0的数字,大于后面的就返回一个大于0的数字。
下面是VS2019的模拟实现方式
#include<stdio.h>
#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 == *str2)
{
if (*str1 == '\0')
{
return 0;//相等
}
str1++;
str2++;
}
if (*str1 > *str2)
{
//大于
return 1;
}
else
{
//小于
return -1;
}
}
int main()
{
char *str1 = "abcde";
char *str2 = "abcfg";
int ret = my_strcmp(str1, str2);
printf("%d", ret);
return 0;
}
下面是linux的模拟实现方式
#include<stdio.h>
#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 == *str2)
{
if (*str1 == '\0')
{
return 0;//相等
}
str1++;
str2++;
}
return (*str1-*str2);
}
int main()
{
char *str1 = "abcde";
char *str2 = "abcfg";
int ret = my_strcmp(str1, str2);
printf("%d", ret);
return 0;
}
标准规定
(1)第一个字符串大于第二个字符串,则返回大于0的数字
(2)第一个字符串等于第二个字符串,则返回0
(3)第一个字符串小于第二个字符串,则返回小于0的数字
注意
strcpy(),strcat(),strcmp()都是与长度无关的字符串函数,相对来说不是那么的安全,只受'\0'的限制。
#include<assert.h>
char* my_strncpy(char* str1, const char* str2,int count)
{
assert(str1 && str2);
char* ret = str1;
int i = 0;
for (i = 0; i < count; i++)
{
if (*str2 != '\0')
{
*str1++ = *str2++;
}
else
{
*str1++ = '\0';
}
}
return ret;
}
(1)拷贝num个字符从源字符串到目标空间。
(2)如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。
#include<stdio.h>
#include<assert.h>
char* my_strncat(char* str1, const char* str2, int count)
{
assert(str1 && str2);
char* ret = str1;
while (*str1++ != '\0');
str1--;
int i = 0;
for (i = 0; i < count; i++)
{
if (*str2 != '\0')
{
*str1++ = *str2++;
}
else
{
*str1 = '\0';
return ret;
}
}
*str1 = '\0';
return ret;
}
#include<stdio.h>
#include<assert.h>
int my_strncmp(const char* str1, const char* str2, int count)
{
assert(str1 && str2);
int i = 0;
for (i = 0; i < count; i++)
{
if (*str1 < *str2)
{
return -1;
}
else if (*str1 > *str2)
{
return 1;
}
else
{
str1++;
str2++;
}
}
return 0;
}
注意:在读文档时出现NULL一般就是空指针,NUL或者Null就是'\0'。
(1)前面是被查找的字符串,后面是要查找的字符串。
(2)如果能够找到,就返回后面的字符串在前面字符串中首次出现的地址,如果未出现,就返回空指针。
#include<stdio.h>
#include<string.h>
int main()
{
char* str1 = "abcdefghijk";
char* str2 = "def";
char*ret = strstr(str1, str2);
if (ret == NULL)
{
printf("字串不存在\n");
}
else
{
printf("%s\n",ret);
}
return 0;
}
运行截图:
#include<stdio.h>
#include<assert.h>
char* my_strstr(const char* str1,const char* str2)
{
assert(str1 && str2);
char* p1 = NULL;
char* p2 = NULL;
char* cur = str1;
if (*str2 == '\0')
{
return (char*)str1;
}
while (*cur)
{
p1 = cur;
p2 = (char*)str2;
while ((*p1 != '\0') && (*p2 != '\0') && *p1 == *p2)
{
p1++;
p2++;
}
if (*p2 == '\0')
{
return cur;//找到子串
}
if(*p1=='\0')
{
return NULL;
}
cur++;
}
return NULL;//找不到子串
}
char * strtok ( char * str, const char * sep );
(1)sep参数是个字符串,定义了用作分隔符的字符集合
(2)第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标 记。
(3)strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注: strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。)
(4)strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串 中的位置。
(5)strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标 记。
(6)如果字符串中不存在更多的标记,则返回 NULL 指针。
#include<stdio.h>
#include<string.h>
int main()
{
char* arr = "123@456@abc.def.com";
char* p = "@.";
char str[500] = { 0 };
strcpy(str, arr);
char* ret = NULL;
for (ret = strtok(str, p);ret != NULL;ret = strtok(NULL,p))
{
printf("%s\n", ret);
}
return 0;
}
运行截图:
返回错误码,所对应的错误信息。
示例:
#include<stdio.h>
#include<string.h>
int main()
{
char* str0 = strerror(0);
printf("%s\n", str0);
return 0;
}
运行截图:
0:No error
1:Operation not permitted
2:No such file or directory
3:No such process
……
下面是较为完整的用法:
#include<stdio.h>
#include<errno.h>
#include<string.h>
int main()
{
//errno是一个全局的错误码的变量
//当C语言的库函数在执行过程中,发生了错误,就会把对应的错误码,赋值到errno这个变量中
char* str = strerror(errno);
printf("%s\n", str);
return 0;
}
注意引头文件<ctype,h>
| <br>函数<br> | <br>如果他的参数符合下列条件就返回真<br> |
| <br>iscntrl<br> | <br>任何控制字符<br> |
| <br>isspace<br> | <br>空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t'或者垂直制表符'\v'<br> |
| <br>isdigit<br> | <br>十进制数字 0~9<br> |
| <br>isxdigit<br> | <br>十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F<br> |
| <br>islower<br> | <br>小写字母a~z<br> |
| <br>isupper<br> | <br>大写字母A~Z<br> |
| <br>isalpha<br> | <br>字母a~z或A~Z<br> |
| <br>isalnum<br> | <br>字母或者数字,a~z,A~Z,0~9<br> |
| <br>ispunct<br> | <br>标点符号,任何不属于数字或者字母的图形字符(可打印)<br> |
| <br>isgraph<br> | <br>任何图形字符<br> |
| <br>isprint<br> | <br>任何可打印字符,包括图形字符和空白字符<br> |
判定结果为是的话返回一个非零值,如果不是返回一个零值。
int tolower ( int c );
int toupper ( int c );
注意:只能一个字符一个字符的进行转换,不能一次转换一个字符串!
使用示例:
#include <stdio.h>
#include <ctype.h>
int main()
{
int i = 0;
char str[] = "Test String.\n";
char c;
while (str[i])
{
c = str[i];
if (isupper(c))
c = tolower(c);
putchar(c);
i++;
}
return 0;
}
运行截图:
说明
(1)函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
(2)这个函数在遇到 '\0' 的时候并不会停下来。
(3)如果source和destination有任何的重叠,复制的结果都是未定义的。
示例:
#include<stdio.h>
#include<string.h>
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[5] = {0};
memcpy(arr2, arr1, sizeof(arr1));
for (int i = 0; i < 5; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}
#include<stdio.h>
#include<assert.h>
void *my_memcpy(void* dest, const void* src, size_t num)
{
assert(dest && src);
void* ret = dest;
int i = 0;
while(num--)
{
*(char*)dest = *(char*)src;
((char*)dest)++;
((char*)src)++;
}
return dest;
}
(1)和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
(2)如果源空间和目标空间出现重叠,就得使用memmove函数处理。
示例:
#include<stdio.h>
#include<string.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
memmove(arr + 2, arr, 20);
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
C语言标准:
memcpy()只要能处理不重叠的内存拷贝就可以了。
memmove处理重叠内存的拷贝。
#include<stdio.h>
#include<assert.h>
void *my_memmove(void* dest, void* src, size_t num)
{
//dest<src:从前向后拷贝
//dest>src&&dest<src+count:从后向前拷贝
//dest>src+count:从后向前和从前向后均可
assert(dest && src);
void* ret = dest;
if (dest < src)
{
while (num--)
{
*(char*)dest = *(char*)src;
((char*)dest)++;
((char*)src)++;
}
}
else
{
while (num--)
{
*((char*)dest+num )= *((char*)src+num);
}
}
return dest;
}
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
示例:
#include<stdio.h>
#include<string.h>
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 1,2,4,5,6 };
int ret = memcmp(arr1, arr2, sizeof(arr1));
printf("%d", ret);
return 0;
}
void* memset(void* dest, int c, size_t count);
作用:设置缓冲区为一个特定的字符。
dest:目的地空间的起始位置。
c:要设定的字符(无论c是都看成字符)。
count:要设定字符的数目(注意是字符的数目。此处都是以字节为单位的,因为一个字符所占的内存空间是一个字节)
使用示例:
#include<stdio.h>
#include<string.h>
int main()
{
char arr[10] = "";
memset(arr, '#', 10);
for (int i = 0; i < 10; i++)
{
printf("%c ", arr[i]);
}
return 0;
}
运行截图:
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/m0_57304511/article/details/122435397
内容来源于网络,如有侵权,请联系作者删除!