在学习这篇文档之前,我相信大家没有了解过strcat strstr strtok strerror
等这样的函数吧,而在我们日常学习的过程中,这些函数或多或少都会给我们起到很大帮助,那么我们接下来一起来学习一下各种字符串与内存相关的函数吧!
重点介绍处理字符和字符串的库函数的使用和注意事项
参数:size_t strlen(const char* str);
字符串以'\0'
作为结束标志,strlen函数返回的是在字符串中 '\0'
前面出现的字符个数(不包
含 '\0'
)。
参数指向的字符串必须要以 '\0'
结束。
注意函数的返回值为size_t
(unsigned int),是无符号的( 易错 )
#include
#include
int main()
{
if ((int)strlen("abc") - (int)strlen("abcdef") > 0)
printf(">");
else
printf("<=");//right
if (strlen("abc") - strlen("abcdef") > 0)
printf(">");//前-后=-3,strlen函数返回类型是size_t,-3会变成一个超级大的数>0
else
printf("<=");
return 0;
}
使用
#include
#include
int main()
{
int len = strlen("abcdef");
printf("%d\n", len);
printf("%d\n", strlen("abcdef"));//链式访问
return 0;
}
模拟实现
#include
#include
#include
int my_strlen(const char* str)//字符串不想被修改:加const
{
assert(str);//断言:判断指针是否有效
int count = 0;
while (*str)//*str!='\0'
{
count++;
str++;
}
return count;
}
int main()
{
int len = my_strlen("abcdef");
printf("%d\n", len);//6
return 0;
}
参数:char* strcpy(char * destination, const char * source );
将源字符串指向的字符串复制到目标指向的数组中,包括\0字符(并在该点停止)。
源字符串必须以 ‘\0’ 结束。
会将源字符串中的 ‘\0’ 拷贝到目标空间。
目标空间必须足够大,以确保能存放源字符串。
目标空间必须可变。
使用
#include
#include
int main()
{
char arr1[] = { 'a', 'b', 'c', 'd', 'e', 'f', '\0' };
char arr2[20] = "xxxxxxxxxxxx";
printf("%s\n", strcpy(arr2, arr1));//返回arr2的起始地址
return 0;
}
模拟实现
#include
#include
//方法一
char* my_strcpy(char* dest, const char* src)
{
char* ret = dest;
assert(dest && src);
//当*src为\0时,赋值过去\0,整个表达式为0,退出
while (*src)
{
*dest = *src;
dest++;
src++;
}
*dest = *src;
//⽅法⼆
return ret;//返回⽬标字符串,模拟实现就是这么做的,⽅便进⾏链式访问
}
//方法二
char* my_strcpy(char* dest, const char* src)
{
char* ret = dest;
assert(dest && src);
while (*dest++ = *src++)
{
;
}
return ret;//返回⽬标字符串,模拟实现就是这么做的,⽅便进⾏链式访问
}
int main()
{
//char arr1[] = "abcdef";
char arr1[] = { 'a', 'b', 'c', 'd', 'e', 'f', '\0' };//没有\0拷贝失败
char arr2[20] = "xxxxxxxxxxxx";
//const char* p = "xxxxxxxxxx";//⽬标空间必须可变。
//my_strlen(p, arr1);//err 不能是const->⽬标空间必须可变
my_strcpy(arr2, arr1);//\0也应该拷⻉过去
printf("%s\n", arr2);//abcdef 后⾯的*不打印->因为把\0拷⻉过去了,遇到\0就不打印后⾯的东⻄了
return 0;
}
注意事项:尽量不能返回局部变量的地址,返回局部变量可以
#include
//尽量不能返回局部变量的地址,返回局部变量可以
int Add(int x, int y)
{
int z = x + y;
return z;
}
//空间销毁并不是地址被销毁
int* test()
{
int a = 10;//0x0012ff40
return &a;//不能返回局部变量的地址
}
int main()
{
// p = 0x0012ff40
int* p = test();
*p = 20;//⾮法访问内存
return 0;
}
参数:char * strcat ( char * destination, const char * source );
将源字符串的副本追加到目标字符串,目标字符串中的\0会被源字符串的第一个字符覆盖,并且在新拼接的两个字符串末尾会有\0
源字符串必须以 ‘\0’ 结束。
目标空间必须有足够的大,能容纳下源字符串的内容。
目标空间必须可修改。
字符串自己给自己追加,不会输出任何结果
#include
#include
int main()
{
char arr1[30] = "hello";
strcat(arr1, arr1);
printf("%s\n", arr1);
return 0;
}
使用
#include
#include
int main()
{
char arr1[30] = "hello";//⽬标空间必须⾜够⼤
char arr2[] = "world";
//char arr2[] = { 'h','e','l','l','o' };//err
//char arr2[] = { 'h','e','l','l','o','\0' };//原字符串必须以\0结束
strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
模拟实现
#include
#include
char* my_strcat(char* dest, const char* src)
{
char* ret = dest;
assert(dest && src);//判断空指针,不能判断是否是野指针
//1. ⽬标空间中的\0
while (*dest)//*dest!=‘\0’
{
dest++;
}
//2. 追加内容到⽬标空间-strcpy
while (*dest++ = *src++)
{
;
}
return ret;//规定返回char*,⽬标空间起始地址
}
int main()
{
char arr1[30] = "hello";
char arr2[] = "world";// {'w', 'o', 'r', 'l', 'd', '\0'};
printf("%s\n", my_strcat(arr1, arr2));
return 0;
}
注意:两种错误方法
//到\0的位置,后置++就把\0跳过了
while(*dest++)
{
;
}
//当arr1为空字符串(只有\0)的时候,上来就先把\0跳过去了
while(*++dest)
{
;
}
参数:int strcmp ( const char * str1, const char * str2 );
这个函数刚开始会比较每个字符串的第一个字符的ASCII码值,之后继续比较,直到字符不同,或直到\0返回空指针。
标准规定:
模拟实现
#include
#include
#include
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 == *str2)//\0==\0也会进⼊循环
{
if (*str1 == '\0')
return 0;
str1++;
str2++;
}
return *str1 - *str2;
/*if (*str1 > *str2)//>0
return 1;
else//<0
return -1;*/
}
int main()
{
//strcmp - 字符串⽐较
//⽐较是对应位置上的字符⼤⼩-⽐的是ASCII码值
char arr1[] = "abc";
char arr2[] = "abc";
int ret = my_strcmp(arr1, arr2);
if (ret < 0)//ret==-1 err
{
printf("arr1 0)//ret==1 err
{
printf("arr1>arr2");
}
else
{
printf("arr1==arr2");
}
return 0;
}
判断字符串相等不能用==
if("abc"=="abq")
//字符串的首地址一定不同->错误
参数:char * strncat ( char * destination, const char * source, size_t num );
拷贝num个字符从源字符串到目标空间。
如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加\0,直到num个。
使用:
#include
#include
int main()
{
char arr1[] = "xxxxxxxxxxxxxxxxx";
char arr2[] = "hello world";
strncpy(arr1, arr2, 5);//helloxxxxxxxxxxxx
printf("%s\n", arr1);
}
模拟实现
#include
#include
#include
char* my_strncpy(char* dest, const char* src, size_t n)
{
assert(dest&&src);
char* ret = dest;
while (n--)
{
*dest++ = *src++;
}
return ret;
}
int main()
{
char arr1[] = "xxxxxxxxxxxxxxxxx";
char arr2[] = "hello";
my_strncpy(arr1, arr2, 5);
printf("%s\n", arr1);
}
参数:char * strncat ( char * destination, const char * source, size_t num );
使用:
#include
#include
int main()
{
char arr1[20] = "hello";
char arr2[] = "world";
strncat(arr1, arr2, 3);
printf("%s\n", arr1);//hellowor
return 0;
}
模拟实现:
#include
#include
#include
void my_strncat(char* arr1, const char* arr2, int n)
{
assert(arr1 && arr2);
char* p = arr1;
char* q = arr2;
while (*p)
{
p++;
}
while (n--)
{
*p = *q;
p++;
q++;
}
*p = '\0';
}
int main()
{
char arr1[30] = "hello";
const char arr2[] = "world";
int num = 0;
scanf("%d", &num);
my_strncat(arr1, arr2, num);
printf("%s\n", arr1);
return 0;
}
参数:int strncmp ( const char * str1, const char * str2, size_t num );
比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。
返回值 | 说明 |
---|---|
<0 | arr1 |
>0 | arr1>arr2 |
=0 | arr1==arr2 |
使用:
#include
#include
int main()
{
char arr1[] = "abcwef";
char arr2[] = "abcqqqqqq";
int ret = strncmp(arr1, arr2, 4);
if (ret < 0)
{
printf("arr1 0)
{
printf("arr1>arr2");//w>q
}
else
{
printf("arr1==arr2");
}
return 0;
}
模拟实现:
#include
#include
#include
int my_strncmp(const char* str1, const char* str2, int n)
{
assert(str1 && str2);
while (n--)
{
if (*str1 == *str2)
{
str1++;
str2++;
}
else if (*str1 - *str2 < 0)
{
return -1;
}
else
{
return 1;
}
}
return 0;
}
int main()
{
char arr1[] = "abcwef";
char arr2[] = "abcqqqqqq";
int ret = my_strncmp(arr1, arr2, 4);
if (ret < 0)
{
printf("arr1 0)
{
printf("arr1>arr2");//w>q
}
else
{
printf("arr1==arr2");
}
return 0;
}
参数:char * strstr ( const char *str1, const char * str2);
返回一个指向str1中首次出现str2的指针,如果str2不是str1的一部分,则返回一个空指针。
使用:
#include
#include
int main()
{
char arr1[] = "abbbcdef";
char arr2[] = "bbcq";
char* ret = strstr(arr1, arr2);
if (NULL == ret)
printf("没找到\n");
else
printf("%s\n", ret);
return 0;
}
模拟实现:
#include
#include
#include
char* my_strstr(const char* str, const char* substr)//找substr
{
const char* s1 = str;//原来的字符串的起始地址
const char* s2 = substr;//目标字符串起始字符串地址
const char* cur = str;
assert(str && substr);
if (*substr == '\0')//判断子字符串是否是空字符串-没法查找
{
return (char*)str;//规定:返回str
}
while (*cur)//*cur!=‘\0’ 后⾯还有元素可以寻找
{
s1 = cur;
s2 = substr;
while (*s1 && *s2 && *s1 == *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')//把⼦串找到了,返回它的地址
return (char*)cur;
cur++;//这个字符不相等,向后移
}
return NULL;//未找到
}
int main()
{
char arr1[] = "abbbcdef";
char arr2[] = "bbcq";
char* ret = my_strstr(arr1, arr2);
if (NULL == ret)
printf("没找到\n");
else
printf("%s\n", ret);
return 0;
}
参数:char * strtok ( char * str, const char * sep );
sep参数是个字符串,定义了用作分隔符的字符集合
第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标
记。
strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:
strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容
并且可修改。)
strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串
中的位置。
strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标
记。
如果字符串中不存在更多的标记,则返回 NULL 指针。
使用:
#include
#include
int main()
{
const char* p = "@.#";//要先加分隔符
char arr[] = "[email protected]#hehe";
char buf[50] = { 0 };
strcpy(buf, arr);
char* str = NULL;//先初始化
for (str = strtok(buf, p); str != NULL; str = strtok(NULL, p))
{
printf("%s\n", str);
}
return 0;
}
//注意事项:
//1.strtok函数找第⼀个标记的时候,函数的第⼀个参数不是NULL
//2.strtok函数找⾮第⼀个标记的时候,函数的第⼀个参数是NULL
参数:char * strerror ( int errnum );
在C语言中,规定了一些信息—错误码(错误信息)
//strerror - 可以把错误码翻译成错误信息
int main()
{
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%s\n", strerror(i));
}
return 0;
}
使用:
//C语⾔可以操作⽂件
//打开⽂件 - fopen
//
//当库函数使⽤的时候,发⽣错误会把errno这个全局的错误变量设置为本次执⾏库函数产⽣的错误码
//errno是C语⾔提供的⼀个全局变量,可以直接使⽤,放在errno.h⽂件中的
#include
#include
int main()
{
//打开⽂件
FILE* pf = fopen("test.txt", "r");
if (NULL == pf)
{
//出错误的原因是什么
printf("%s\n", strerror(errno));
return 0;//打开失败直接结束
}
//读⽂件
//...
//关闭⽂件
fclose(pf);
pf = NULL;
return 0;
}
函数 | 如果他的参数符合下列条件就返回真 |
---|---|
iscntrl | 任何控制字符 |
isspace 空 | 空白字符:空格‘ ’,换页‘\f’,换行’\n’,回车‘\r’,制表符’\t’或者垂直制表符’\v’ |
isdigit | 十进制数字 0~9 |
isxdigit | 十六进制数字,包括所有十进制数字,小写字母af,大写字母AF |
islower | 小写字母a~z |
isupper | 大写字母A~Z |
isalpha | 字母az或AZ |
isalnum | 字母或者数字,az,AZ,0~9 |
ispunct | 标点符号,任何不属于数字或者字母的图形字符(可打印) |
isgraph | 任何图形字符 |
isprint | 任何可打印字符,包括图形字符和空白字符 |
举例:
#include
#include
//判断是否是空⽩字符
int main()
{
//printf("%d\n", isspace(' '));//8-打印看不⻅的字符时返回⼀个⾮0值
//printf("%d\n", isspace(' '));//0-是随机的
char ch = 'w';
if (isspace(ch))
{
//空⽩字符
}
else
{
//⾮空⽩字符
}
return 0;
}
//判断是不是数字
int main()
{
char ch = '0';
//if (ch >= '0' && ch <= '9')//效率低
//{
//}
if (isdigit(ch))
{
}
//转换函数:⼤写字⺟⼩写字⺟互换
char ch = 0;
ch = getchar();
if (islower(ch))//判断⼩写
{
ch = toupper(ch);//⼩写->⼤写
}
else//判断⼤写
{
ch = tolower(ch);//⼤写->⼩写
}
printf("%c\n", ch);
return 0;
}
参数:void * memcpy ( void * destination, const void * source, size_t num );
函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
这个函数在遇到 ‘\0’ 的时候并不会停下来。
如果source和destination有任何的重叠,复制的结果都是未定义的。
使用:
#include
#include
int main()
{
//char arr1[] = "abcdef";
//char arr2[20] = { 0 };
//strcpy(arr2, arr1);//只能拷⻉字符串的
int arr3[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr4[5] = { 0 };
memcpy(arr4, arr3, 20);
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d\n", arr4[i]);//1 2 3 4 5
}
return 0;
}
模拟实现
#include
#include
#include
void* my_memcpy(void* dest, const void* src, size_t num)
{
void* ret = dest;
assert(dest && src);
while (num--)
{
*(char*)dest = *(char*)src;//(char*)dest 一个一个字节操作起来会更加细致
dest = (char*)dest + 1;//dest++;err 强制类型转换都是临时的,需要每次都强转
src = (char*)src + 1;//src++;err
}
return ret;//要求规定:返回一个void*类型的指针
}
int main()
{
int arr3[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr4[5] = { 0 };
my_memcpy(arr4, arr3+5, 5*sizeof(arr3[0]));
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d\n", arr4[i]);//6 7 8 9 10
}
return 0;
}
反例:当原字符串和目标字符串重叠的时候->memmove可以解决
参数:void * memmove ( void * destination, const void * source, size_t num );
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
如果源空间和目标空间出现重叠,就得使用memmove函数处理。
举例:
#include
#include
#include
//反例:当原字符串和⽬标字符串重叠的时候->memmove可以解决
//memmove
void* my_memcpy(void* dest, const void* src, size_t num)
{
void* ret = dest;
assert(dest && src);
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
//memmove实现
void* my_memmove(void* dest, const void* src, size_t num)
{
void* ret = dest;
assert(dest && src);//保证两个指针的有效性
if (dest < src)
{
//前->后
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
else
{
//后->前
while (num--)
{
*((char*)dest + num) = *((char*)src + num);
}
}
return ret;
}
void test1()
{
int arr3[] = { 1,2,3,4,5,6,7,8,9,10 };
//my_memcpy(arr3+2, arr3, 5 * sizeof(arr3[0]));//1 2 1 2 1 2 1 8 9 10
//my_memmove(arr3, arr3 + 2, 20);//3 4 5 6 7 6 7 8 9 10
memcpy(arr3+2, arr3, 20);//1 2 1 2 3 4 5 8 9 10
//库⾥⾯的memcpy⽀持,但是C语⾔只要求:
//memcpy能拷⻉不重叠的内存空间就可以了
//memmove去处理那些重叠内存拷⻉
//但是发现VS上memcpy也能实现重叠拷⻉(超常发挥了)
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr3[i]);
}
}
int main()
{
test1();
return 0;
}
使用
int main()
{
//char arr[20] = { 0 };
//memset(arr, 'x', 10);
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
memset(arr, 0, 10);
//00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 ...
return 0;
}
memcpy(arr3+2, arr3, 5 * sizeof(arr3[0]));//1 2 1 2 1 2 1 8 9 10
//my_memmove(arr3, arr3 + 2, 20);//3 4 5 6 7 6 7 8 9 10
memcpy(arr3+2, arr3, 20);//1 2 1 2 3 4 5 8 9 10
//库⾥⾯的memcpy⽀持,但是C语⾔只要求:
//memcpy能拷⻉不重叠的内存空间就可以了
//memmove去处理那些重叠内存拷⻉
//但是发现VS上memcpy也能实现重叠拷⻉(超常发挥了)
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr3[i]);
}
}
int main()
{
test1();
return 0;
}
到这里,C语言中常见的字符串就已经讲解完毕了,希望你能理解的透彻!
喜欢的话一件三连熬~