自我介绍 : 正在学习c语言,每天都在努力进步
˙☆`·.·˙˙`··˙˙`··˙☆`·.·˙˙`··˙☆˙˙☆`·.·˙˙`··˙˙`··˙☆`·.·˙˙`··˙☆˙˙☆`·.·˙˙`··˙˙`··˙☆`·.·˙˙`··˙☆˙
专栏
c语言Knowledge
每日一题
记得点个关注和博主一起进步
˙☆`·.·˙˙`··˙˙`··˙☆`·.·˙˙`··˙☆˙˙☆`·.·˙˙`··˙˙`··˙☆`·.·˙˙`··˙☆˙˙☆`·.·˙˙`··˙˙`··˙☆`·.·˙˙`··˙☆˙
在C语言里有string.h这个头文件,但是C语言里没有字符串这个类型,字符串通常放在常量字符串中或者字符数组中,字符串常量适用于那些对她不做修改的字符串函数,string.h这个头文件里声明的函数原型也全是对char数组的操作,接下来这篇文章就简单介绍一下常用的函数
size_t strlen( const char* str)
功能:计算字符串长度,不包含’\0’
返回值:返回字符串的长度
#include
#include
int main()
{
char arr1[] = { "abcdef" };
char arr2[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(arr1));
printf("%d\n", strlen(arr2));
return 0;
}
【结果】:6 22(随机值)
方法一:
//计数器方式
int my_strlen(const char * str)
{
int count = 0;
while(*str)
{
count++;
str++;
}
return count;
}
方法二:
//不能创建临时变量计数器
int my_strlen(const char * str)
{
if(*str == '\0')
return 0;
else
return 1+my_strlen(str+1);
}
方法三:
//指针-指针的方式
int my_strlen(char *s)
{
char *p = s;
while(*p != ‘\0’ )
p++;
return p-s;
}
char* strcpy(char* dest,char* src)
功能:将参数src字符串拷贝至参数dest所指的地址
返回值: 返回参数dest的字符串起始地址
char *my_strcpy(char *dest, const char*src)
{
char *ret = dest;
assert(dest != NULL);
assert(src != NULL);
while((*dest++ = *src++))
{
;
}
return ret;
}
【解释】:assert的作用是检验指针dest和src,防止其为空指针,*dest++ = *src++,当*src=‘ \0 ’时表达式为假,循环结束,但也会把‘\0’赋值给*dest
char * strncpy ( char * destination, const char * source, size_t num );
功能:将source指针所指字符串的前num个字符赋值给destination所指空间
返回值:返回参数destination的字符串起始地址
举例:
#include
#include
int main()
{
char str[] = "abcdef";
//切记目标空间大小必须可修改
char arr[10] = { 0 };
strncpy(arr,str,3);
printf("%s", arr);
return 0;
}
char* strcat(char* dest,const char* src)
功能: 将src所指字符串接到dest所指字符串后边 (找到dest末尾再把src加进去)
返回值:返回dest所指字符串起始地址
char *my_strcat(char *dest, const char*src)
{
char *ret = dest;
assert(dest != NULL);
assert(src != NULL);
//找到目标空间末尾
while(*dest)
{
dest++;
}
//把源字符串加进去
while((*dest++ = *src++))
{
;
}
return ret;
}
char* strncat (char* dest,const char* src,size_t num)
功能:将src所指字符串前n个字符追加到dest所指字符串后
返回值:返回dest字符串的起始地址
int strcmp (const char* str1,const char* str2)
功能:字符串比较
返回值:若字符串str1和字符串str2相同则返回0,str1若大于str2则返回大于0的值,str1若小于str2则返回小于0的值
int my_strcmp(const char* str1, const char* str2)
{
while (*str1== *str2)
{
if (*str1 == '0') //表示两字符串都走到了\0的位置
return 0;
str1++;
str2++;
}
if (*str1 > *str2)
return 1;
else
return -1;
}
char * strncpy ( char * str1, const char * str2, size_t num );
功能:比较两字符串前num个字符
返回值:若字符串str1和字符串str2相同则返回0,str1若大于str2则返回大于0的值,str1若小于str2则返回小于0的值
char * strstr ( const char *str1, const char * str2);
功能: 查找str2所指字符串在str1所指字符串中首次出现的位置
返回值:返回字符串str1中第一次出现子串str2的地址,如果没有查找到子串,则返回NULL
例如:
#include
#include
int main ()
{
char str[] ="This is a simple string";
char * pch;
pch = strstr (str,"simple");
puts (str);
return 0;
}
char* my_strstr(const char* str1, const char* str2)
{
assert(str1 && str2);//检查传过来的指针是空指针
const char* ret; //记录开始匹配的位置
const char* arr1; //遍历str1所指字符串
const char* arr2; //遍历str2所指字符串
ret = str1;
while (*ret)
{
arr1 =ret;
arr2 =str2;
while (*arr1 && *arr2 && (*arr1 == *arr2))
{
arr1++;
arr2++;
}
if (*arr2 == 0)
return ret;
ret++;
}
return NULL;
}
char * strtok ( char * str, const char * sep );
功能:根据分隔符将字符串分隔成一个个片段
返回值:返回下一个分割后的字符串指针,如果已无从分割则返回NULL
举例:
#include
#include
int main()
{
char p[] = "[email protected]";
const char* sep = ".@";
char arr[30];
char* str = NULL;
strcpy(arr, p);//将数据拷贝一份,处理arr数组的内容
str = strtok(arr, sep);
printf("%s\n", str);
str = strtok(NULL, sep);
printf("%s\n", str);
str = strtok(NULL, sep);
printf("%s\n", str);
return 0;
}
但是,若要分割整个字符串,一般不会像上述代码那样写,因为如果目标字符串很长,分隔字符足够多,该调用多少次strtok函数呢,下面解锁一下正确的方式:
#include
#include
int main()
{
char p[] = "[email protected]";
const char* sep = ".@";
char arr[30];
char* str = NULL;
strcpy(arr, p);//将数据拷贝一份,处理arr数组的内容
for(str=strtok(arr, sep); str != NULL; str=strtok(NULL, sep))
{
printf("%s\n", str);
}
return 0;
}
char * strerror ( int errnum );
功能:返回错误码,所对应的错误信息。
举例:
打开文件的例子:
fopen 以读的方式打开文件
如果文件存在,打开成功
如果文件不存在,打开失败
#include
#include
#include //必须包含的头文件
int main ()
{
FILE * pFile;
pFile = fopen ("add.txt","r");
if (pFile == NULL)
printf ("打开失败,原因是%s\n",strerror(errno));
else
printf("打开文件成功\n");
return 0;
}
void * memcpy ( void * destination, const void * source, size_t num );
例如:
#include
#include
int main()
{
int arr[10] = { 0 };
int str[] = { 1,2,3,4,5 };
memcpy(arr, str, 20);
for (int i = 0; i < 5; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
void* my_memcpy(void* dest, const void* src, size_t sz)
{
assert(dest && src);
void* ret = dest;
while (sz)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
sz--;
}
return ret;
}
【注】:由于两个形参都是void* ,不可以随意赋值和加减运算,由于本函数是以字节为单位进行覆盖,所以可以把两指针强转为char *类型,每循环一次就覆盖一个字节的内存,循环sz次就覆盖了sz个字节,这里还要注意,不能写成dest++,这时dest又变成了void *类型,写成(char*)dest++, 也是不行的,因为++优先级比强制转换高,会先执行dest++,此时在把指针强制转换为char *类型毫无意义,
void * memmove ( void * destination, const void * source, size_t num );
引例:
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
memcpy(arr1+2, arr1, 20);
for (int i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
上述代码我们的目的是想把1~5的数字共20个字节拷贝到原来3~7的数字的位置上,我们来看一下结果:
这个结果并不是我们预期的1 2 1 2 3 4 5 8 9 10,那是什么原因呢?
是由于我们把1 2拷贝到了3 4的位子,当我们想用3和4的值时他们已经被改变了,所以才会导致这样的结果,接下来我们看看memcpy和memmove的区别
#include
#include
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
memmove(arr1 + 2, arr1, 20);
for (int i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
可见memmove可以成功将1~5的数赋值给3~7的数
这个函数的覆盖分为两种情况
我们先看dest>src的情况:
可以发现,若src从前往后覆盖dest,还是会发生上述错误,这时这种情况从后往前覆盖就可以完美避开这种错误
再看dest 这种情况从前向后覆盖就没问题 喜欢的话欢迎关注我,收藏、点赞、转发本文喔 (◍•ᴗ•◍)ゝ~原创不易感谢支持 void* my_memmove(void* dest, const void* src, size_t sz)
{
assert(dest && src);
void* ret = dest;
if (dest < src)
{
//从前往后
while (sz)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
sz--;
}
}
else
{
//从后往前
while (sz--)
{
*((char*)dest + sz) = *((char*)src+sz);
}
}
return ret;
}
字符分类函数: