主要介绍处理字符和字符串的库函数的使用和注意事项
- 求字符串长度
- strlen
- 长度不受限制的字符串函数
- strcpy strcat strcmp
- 长度受限制的字符串函数介绍
- strncpy strncat strncmp
- 字符串查找
- strstr strtok
- 错误信息报告
- strerror
- 字符操作 内存操作函数
- memcpy memmove memset memcmp
- …还有许多
前言
C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在
常量字符串中或者字符数组中。
字符串常量则适用于那些对它不做修改的字符串函数。
函数介绍
1.1 strlen(计算字符串长度)
size_t strlen ( const char * str );
- 字符串已经 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包 含 ‘\0’ )
- 参数指向的字符串必须要以 ‘\0’ 结束。
- 注意函数的返回值为size_t,是无符号的( 易错 )
- 函数的使用,以及模拟实现
#include <stdio.h>
#include <string.h>
#include <assert.h>
/*strlen计算字符串长度*/
int my_strlen(const char* str)
{
int count = 0;
assert(str != NULL); //p
while (*str != '\0')
{
count++;
str++; //这里不用解引用,我们只是让这里的地址+1指向下一个地址的内容。
}
return count;
}
int main()
{
/*系统函数的使用*/
// int len = strlen("abcdef");
// printf("%d\n" ,len);
/*自己模拟实现strlen函数*/
int len = my_strlen("abcdef");
printf("%d\n" ,len);
/*strlen函数的返回类型是size_t == unsigned int*/
//3 - 6 = -3; 但由于strlen函数的返回值是无符号整型。-3会被当成3,输出“hehe”。
if (strlen("abc") - strlen("abcdef") > 0)
{
printf("hehe");
}
else
{
printf("haha");
}
return 0;
}
- 一道有趣的题目
主要考察对strlen函数返回类型的理解
#include <stdio.h>
#include <string.h>
int main()
{
/*strlen函数的返回类型是size_t == unsigned int*/
//3 - 6 = -3; 但由于strlen函数的返回值是无符号整型。-3会被当成3,输出“hehe”。
if (strlen("abc") - strlen("abcdef") > 0)
{
printf("hehe");
}
else
{
printf("haha");
}
return 0;
}
1.2strcpy(拷贝字符串)
定义函数:
char *strcpy(char *dest, const char *src);
char * strncpy(char *dest, const char *src, size_t n);
参数分析:
dest --> 目标地址(数据拷贝到这里)
src --> 源地址,需要拷贝的数据所在的地址
n --> 指定拷贝 str 的前N 字节
返回值:
返回dest 的地址
- 源字符串必须以 ‘\0’ 结束。 会将源字符串中的 ‘\0’ 拷贝到目标空间。
- 目标空间必须足够大,以确保能存放源字符串。
- 目标空间必须可变。
- 函数的使用以及模拟实现
#include <stdio.h>
#include <string.h>
#include <assert.h>
char* my_strcpy(char* dest ,const char* src)
{
assert(dest != NULL);
assert(src != NULL);
char* ret = dest; //把源头的地址先存起来。
//拷贝src指向的字符到dest指向的空间
/*写法一*/
while (*src != '\0')
{
*dest = *src;
dest++;
src++;
}
*dest = *src; //拷贝'\0'
/*写法二*/
// while (*src != '\0')
// {
// *dest++ = *src++; //先进行解引用,再进行++
// }
// *dest = *src; //拷贝'\0'
/*写法三*/
// while (*dest++ = *src++)
// {
// ;
// }
// return ret; //返回目的空间的起始地址
}
int main()
{
char arr1[] = "abcdef";
char arr2[] = "bit";
/*函数的使用*/
// strcpy(arr1 ,arr2); //把arr2的内容拷贝到arr1里面去。
/*模拟实现*/
my_strcpy(arr1 ,arr2); //把arr2的内容拷贝到arr1里面去。
printf("%s\n" ,arr1);
return 0;
}
注意:其中拷贝是把’\0’也拷贝过去!!!
1.3strcat(字符串追加)
定义函数:
char *strcat(char *dest, const char *src);
char * strncat(char *dest, const char *src, size_t n);
参数:
dest --> 目标地址
src --> 源数据的地址
n --> 控制需要拷贝的字节数
返回值:
返回参数 dest 的字符串起始地址
- 源字符串必须以 ‘\0’ 结束。
- 目标空间必须有足够的大,能容纳下源字符串的内容。
- 目标空间必须可修改。
- 函数的使用以及模拟实现
#include <stdio.h>
#include <string.h>
#include <assert.h>
char* my_strcat(char* dest ,const char* src)
{
char* ret = dest;
assert(dest != NULL); //写法一
assert(src); //写法二
while (*dest != '\0')
{
dest++;
}
while (*dest++ = *src++)
{
;
}
}
int main()
{
char arr[30] = "hello"; //在'\0'处进行追加,完了自动补上\0
char arr1[] = "bit";
/*函数的使用*/
// strcat(arr ,arr1);
/*模拟实现*/
my_strcat(arr ,arr1);
printf("%s\n" ,arr);
return 0;
}
1.4strcmp(字符串比较函数)
定义函数:
int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, size_t n);
参数分析:
s1 --> 需要比较的字符串1
s2 --> 需要比较的字符串2
n --> 需要比较的前N个字节
返回值:
第一个字符串大于第二个字符串,则返回大于0的差值数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的差值数字
- 函数的使用以及模拟实现
#include <stdio.h>
#include <string.h>
#include <assert.h>
int main()
{
char* p = "abc";
char* p1 = "qbc";
int ret = strcmp(p ,p1);
// if (ret > 0)
// {
// printf("p > p1\n");
// }
// else if(ret == 0)
// {
// printf("p == p1\n");
// }
// else if(ret < 0)
// {
// printf("p < p1\n");
// }
printf("%d\n" ,ret);
return 0;
}
/*模拟实现strcmp函数*/
int my_strcmp(const char* dest ,const char* src)
{
assert(dest && src); //判断两个指针不能等于空指针
while (*dest == *src) //判断是否相等
{
if (*dest == '\0') //再判断是否是\0
{
return 0;
}
dest++;
src++;
}
//写法一:直接返回1,或者-1
// if(*dest > *src)
// return 1; //大于
// else
// return -1; //小于
//写法二:大小不相同,则直接返回相差字符的个数
return (*dest - *src);
}
int main()
{
char* p = "abc";
char* p1 = "qbc";
int ret = my_strcmp(p ,p1);
printf("自己实现%d\n" ,ret);
return 0;
}
1.5strstr(字符串查找函数)
在一字符串中查找指定的字符串。
定义函数:
char *strstr(const char *haystack, const char * needle);
参数分析:
haystack --> 需要查找的源字符串
needle --> 需要查找的内容
返回值:
成功 返回needle第一次出现的地址
失败 返回NULL
注意:查找成功,则返回的是要查找的字符串在被查找的字符串中第一次出现的位置!!!
- 字符串查找函数的使用
#include <stdio.h>
#include <string.h>
#include <assert.h>
int main()
{
char* p = "abcdefabcdef";
char* p1 = "deff";
char* ret = strstr(p, p1); //查找的是p1在p中第一次出现的位置
if (ret == NULL)
{
printf("查找的子串不存在\n");
}else
{
printf("%s\n" ,ret);
}
return 0;
}
- 模拟实现strstr函数
char* my_strstr(const char* p1 ,const char* p2)
{
//先确定两个字符串不能为空指针
assert(p1 != NULL);
assert(p2 != NULL);
char* s1 = (char*)p1;
char* s2 = (char*)p2;
char* cur = (char*)p1; //把被查找的子串地址赋值给cur
//2、如果要查找的子串是'\0',那么就返回地址
if (*s2 == '\0')
{
return (char*)p1;
}
while (*cur) //1、如果cur不是等于'\0',则可以继续查找
{
s1 = cur ;
s2 = (char*)p2;
//3、如果s1与s2所指向的值都不等于\0并且s1 == s2,那么指针偏移到下一位继续找
while ((*s1 != '\0') && (*s2 != '\0') && (*s1 == *s2))
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return cur; //找到子串,则返回之前记录的位置
}
cur++;
}
return NULL; //找不到子串
}
int main()
{
char* p1 = "abbbcdef"; //将要被查找的子串
char* p2 = "bcd"; //要查好的子串
char* ret = my_strstr(p1, p2); //查找的是p1在p中第一次出现的位置
if (ret == NULL)
{
printf("查找的子串不存在\n");
}else
{
printf("%s\n" ,ret);
}
return 0;
}
1.6strtok(字符串分割)
定义函数:
char * strtok ( char * str, const char * str );
参数分析:
str --> 需要分割的字符串
str --> 分割的标记
返回值:
成功 返回下一个分割后的字符串指针
如果已无从分割则返回 NULL.
-
sep参数是个字符串,定义了用作分隔符的字符集合 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标 记。
-
strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注: strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。)
-
strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串 中的位置。
-
strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标 记。 如果字符串中不存在更多的标记,则返回 NULL 指针。
-
分割的操作其实原理就是在字符串中找到该字符然后把他替换成\0结束符
-
函数的使用
#include <stdio.h>
#include <string.h>
#include <assert.h>
int main()
{
char arr[] = "sjf@bitedu.com";
char arr2[] = "192.168.14.225";
char* p = "@."; //用作分隔符的标记集合
char arr1[50] = {0};
/*strok函数会改变被操作的字符串,
所以在使用字符串操作函数切割的字符串一般都是临时拷贝的内容,并且可以被修改.*/
strcpy(arr1 ,arr);
/*写法一:*/
// char* ret = strtok(arr ,p);
// printf("%s\n" ,ret);
// ret = strtok(NULL ,p); //第二次调用时,传空指针进去即可
// printf("%s\n" ,ret);
// ret = strtok(NULL ,p); //第二次调用时,传空指针进去即可
// printf("%s\n" ,ret);
/*写法二:巧妙利用for语句来实现strtok函数的使用*/
char* ret = NULL;
for (ret = strtok(arr2 ,p); ret != NULL; ret = strtok(NULL ,p))
{
printf("%s\n" ,ret);
}
return 0;
}
1.7strerero(错误信息报告函数)
返回错误原因的描述字符串
`char* strerror ( int errnum );
errnum --> 错误号码
返回值:
错误原因的描述(字符串)`
strerror 是通过错误号码来获得错误信息的字符串指针返回,我们可以把该字符串在保存到想要的地方。
- 函数的使用
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <errno.h> //包含此头文件
int main()
{
FILE* Pf = fopen("test.txt" ,"r"); //以只读的方式打开test.txt文件
if (Pf == NULL)
{
printf("%s\n" ,strerror(errno));
}
else
{
printf("open file success\n");
}
return 0;
}
1.8memcpy(内存拷贝函数)
memccpy ( 拷贝内存内容 )
memcpy ( 拷贝内存内容 )
定义函数:
void * memcpy (void * dest, const void *src, size_t n);
void * memccpy(void *dest, const void * src, int c, size_t n);
参数分析:
dest --> 目标地址
src --> 源地址
n --> 需要拷贝的字计数
返回值:
返回指向 dest 的指针.(memcmp)
找到字符c 则返回c下一个字节的指针,否则与memcpy 一致(memccpy)
-
函数memcpy从src的位置开始向后复制num个字节的数据到dest的内存位置。
-
这个函数在遇到 ‘\0’ 的时候并不会停下来。
-
如果src和dest有任何的重叠,复制的结果都是未定义的。
-
memccpy : 在拷贝的过程中遇到 c 则会停止拷贝,并返回目标地址中c的下一个位置
-
函数的使用
struct
{
char name[20];
int age;
}s;
/*size_t 用做sizeof 操作符的返回值类型,同时也是很多函数的参数类型,包括malloc 和strlen。*/
void* my_memcpy(void* dest, void* src ,size_t num)
{
assert(dest != NULL);
assert(src != NULL);
void* ret = dest;
while (num--)
{
*(char*)dest = *(char*)src;
//++(char*)dest; //这样会报错“表达式必须是可以修改的左值”
dest = (char*)dest + 1;
src = (char*)src + 1 ;
}
return ret;
}
int main()
{
char name[20] ="hello world";
/*memcpy-内存拷贝函数的使用*/
// memcpy(s.name ,name ,strlen(name)+1);
/*模拟实现一个内存拷贝函数*/
my_memcpy(s.name ,name ,strlen(name)+1);
printf("%s\n" ,s.name);
return 0;
}
1.9memmove(内存拷贝)
void* memmove ( void* destination, const void* source, size_tnum );
-
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
-
如果源空间和目标空间出现重叠,就得使用memmove函数处理。
-
但是源空间和目标空间没有重叠则和memcpy函数功能相同
-
函数的使用以及模拟实现
#include <stdio.h>
#include <string.h>
#include <assert.h>
void* my_memmove(void* dest, void* src ,size_t num) //size_t 用做sizeof 操作符的返回值类型
{
assert(dest != NULL);
assert(src != NULL);
void* ret = dest;
//从前向后拷贝
// if ((dest < src) || (dest > (char*)src + num)) //写法二
// {
// }
if (dest < src) //写法一
{
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1; //++(char*)dest; //这样会报错“表达式必须是可以修改的左值”
src = (char*)src + 1 ;
}
}else //从后向前拷贝
{
while (num--)
{
*((char*)dest + num) = *((char*)src + num);
}
}
return ret;
}
int main()
{
int arr[] = {1,2,3,4,5,6,7,8,9,10};
int i = 0;
/*memmove库函数的使用*/
//memmove(arr + 5, arr ,12);
/*模拟实现memmove函数*/
my_memmove(arr, arr + 2, 20);
for (i = 0; i < 10; i++)
{
printf("%d" ,arr[i]);
}
printf("\n");
return 0;
}
2.0memcmp(内存比较函数)
按字节比较大小
定义函数:
int memcmp (const void *s1, const void *s2, size_t n);
参数说明:
s1 --> 需要比较的内存区1
s2 --> 需要比较的内存区2
n --> 需要比较的字节数
返回值:
返回差值 , 0 表示相同非零表示有不同
- 函数的使用
#include <stdio.h>
#include <string.h>
#include <assert.h>
int main()
{
//01 00 00 00 02 00 00 00 03 00 00 00
//01 00 00 00 02 00 00 00 05 00 00 00
int arr[] = {1, 2, 3, 4, 5, 6};
int arr1[] = {1, 2, 5, 8, 10, 12};
int ret = memcmp(arr ,arr1 ,9); //按字节比较大小
printf("%d\n" ,ret);
return 0;
}
总结:
以上总结的函数相对来说还算常用吧。还是需要能熟练掌握使用方法,以及它们的使用注意事项。