本章重点:
求字符串长度
strlen
长度不受限制的字符串函数
strcpy strcat strcmp
长度受限制的字符串函数介绍
strncpy strncat strncmp
字符串查找
strstr strtok
错误信息报告
strerror
字符操作
内存操作函数
memcpy memmove memset memcmp
1.函数介绍:
1.1 strlen //求字符串长度
(unsigned int)== size_t strlen ( const char * str );
字符串以 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包含 '\0' )。
1.参数指向的字符串必须要以 '\0' 结束。
2.注意函数的返回值为size_t,是无符号的( 易错 )
#include <stdio.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函数是无符号整形,所以两个正数减完还是正数,所以
//是str2 > str1
}
3.模拟实现my_strlen函数:
#include<stdio.h>
#include<assert.h>
int my_strlen(const char *str)
{
int count = 0;
assert(str != NULL);
while(*str)//判断字符串是否结束
{
count++;
str++;
}
return count;
}
int main()
{
const char* p = "abcdef";
//测试
int len = my_strlen(p);
printf("len = %d\n", len);
return 0;
}
1.2 strcpy //拷贝字符串
char* strcpy(char * destination, const char * source);
1.将源指向的C字符串复制到目标指向的数组中,包括\0空字符(并在此停止)。
2.源字符串必须以 '\0' 结束。
3.会将源字符串中的 '\0' 拷贝到目标空间。
4.目标空间必须足够大,以确保能存放源字符串。
5.目标空间必须可变。
6.模拟实现strcpy
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* dest,const char* src)//10,返回了目的地初始值,符合自定义函数的规定
{
char *ret=src;//记录源文件的起始地址
assert(dest!= NULL && src!=NULL); //目的和源头地址不能为空
while (*dest++ = *src++)//内容一直赋予,直到解引用的内容为空停止
{
;
}
return ret;
}
int main()
{
char arr1[] = "*************";
char arr2[] = "bit";
my_strcpy(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
1.3 strcat //追加字符串
char * strcat ( char * destination, const char * source );
1.将源字符串的副本附加到目标字符串。目的地中的终止空字符被源文件的第一个字符覆盖,并且一个空字符被包含在由目的地中的这两个字符串联而成的新字符串的末尾。
2.源字符串必须以 '\0' 结束。
3.目标空间必须有足够的大,能容纳下源字符串的内容。
4.目标空间必须可修改。
字符串自己给自己追加,如何?先看实现功能
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strcat(char* dest,const char* str) {
assert(dest != NULL);
assert(str );
char* ret = dest;
//1. 找到目的地末尾的\0
while (*dest != '\0')
{
dest++;
}
//2. 开始一个一个追加
while (*dest++ = *str++) {
;
}
return ret;
}
int main()
{
char arr1[30] = "abcdef";//防止角标溢出
char arr2[] = "bit";
my_strcat(arr1, arr2);
printf("%s", arr1);
return 0;
}
从模拟函数可以看出来,追加函数自身从 \0 处进行追加,如果自身追加自身的话,会发现,被追加的字符串的 \0 被覆盖,导致最后追加的时候没有\0就停不下来,所以不能自身追加自身
5.用strncat函数可以实现字符串自己给自己追加
1.4 strcmp //比较字符串的大小
int strcmp ( const char * str1, const char * str2 );
这个函数开始比较每个字符串的第一个字符。如果它们相等情况下,它继续下一个对比,直到字符不同或终止null(遇到\0)字符停止。
标准规定:
1.第一个字符串大于第二个字符串,则返回大于0的数字
2.第一个字符串等于第二个字符串,则返回0
3.第一个字符串小于第二个字符串,则返回小于0的数字
模拟实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>
int my_strcmp(char* str1,const char* str2) {
assert(str1 && str2);
while (*str1 == *str2)
{
if (*str1 == '\0') //比较到\0处,也就是\0前字符串比完了
{
return 0;//相等就返回
}
str1++;
str2++;
}
if (*str1 > *str2)
{
return 1;//大于
}
else {
return - 1;//小于
}
}
int main()
{
char arr1[30] = "abcdef";
char arr2[] = "abcdef";
int ret =my_strcmp(arr1, arr2);
if (ret > 0) {
printf("arr1 > arr2");
}
else if (ret < 0) {
printf("arr1 < arr2");
}
else {
printf("arr1 == arr2");
}
return 0;
}
1.5 strncpy // 不拷贝\0
将源的第一个num字符复制到目标。如果源C字符串的结束(由空字符表示)在复制num字符之前找到,目的地被填充为零,直到总共写入了num个字符。
1.拷贝num个字符从源字符串到目标空间。
2.如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个
1.6 strncat
char * strncat ( char * destination, const char * source, size_t num );
Appends the first num characters of source to destination, plus a terminating null-character.
If the length of the C string in source is less than num, only the content up to the terminating null-character is copied.
将源的第一个num字符附加到目标,再加上一个结束的空字符。
如果源文件中的C字符串长度小于num,则只复制直到结束的空字符之前的内容。
/* strncat example */
#include <stdio.h>
#include <string.h>
int main ()
{
char str1[20];
char str2[20];
strcpy (str1,"To be ");
strcpy (str2,"or not to be");
strncat (str1, str2, 6);
puts (str1);//打印结果为:To be or not
return 0;
}
1.7 strncmp //比较字符串
int strncmp ( const char * str1, const char * str2, size_t num );比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完
![](https://i-blog.csdnimg.cn/blog_migrate/fd9dafb9ce28547d431936a15995d41d.png)
/* strncmp example */
#include <stdio.h>
#include <string.h>
int main ()
{
char str[][5] = { "R2D2" , "C3PO" , "R2A6" };
int n;
puts ("Looking for R2 astromech droids...");
for (n=0 ; n<3 ; n++)
if (strncmp (str[n],"R2xx",2) == 0)
{
printf ("found %s\n",str[n]);
}//比较R2xx,有两个相同的就打印,所以打印R2D2,R2A6
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/c955d901ed6cb2e9342fc5c75dcc9c5a.png)
1.8 strstr //查找字符串
char * strstr ( const char *str1, const char * str2);
Returns a pointer to the first occurrence of str2 in str1, or a null pointer if str2 is not part of str1.
返回一个指向str1中第一个str2的指针,如果str2不是str1的一部分,则返回一个空指针
/* strstr example */
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] ="This is a simple string";
char * pch;
pch = strstr (str,"simple");
strncpy (pch,"sample",6);
puts (str);
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/bba9aff8a373f4366ff12b01b0cc95fc.png)
1.9 strtok //切割字符串
char * strtok ( char * str, const char * sep );
sep参数是个字符串,定义了用作分隔符的字符集合.第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
如果字符串中不存在更多的标记,则返回 NULL 指针。
int main() {
char arr[] = "abcde.fg.higke.idqd.mdqw";//原字符串
char* p = "."; //指针P是分隔符的意思
char buf[1024] = { 0 };
strcpy(buf, arr);//放到新创建好的数组里
char* ret = NULL;
for (ret = strtok(arr, p); ret != NULL; ret = strtok(NULL, p)) //首先将原字符串放进去,然后执行条件是 指针指向的地址不能是空指针,如果不是空指针
printf("%s\n", ret);//,但这次位置从空指针开始,因为这个函数遇到分隔符识别完后会自动变为\0,所以就为空,所以从空的开始
}
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/b5402f44756c385f29d13e5b24058438.png)
1.10 strerror //字符串判断错误函数
char * strerror ( int errnum );/返回错误码,所对应的错误信息。
#include<errno.h>
int main()
{
FILE* pf = fopen("text.txt", "r");
if (pf == NULL) {
printf("%s\n", strerror(errno));
}
else {
printf("open file success\n");
}
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/44dd3e11aaeaec626d50d4fa6564d60f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/75285106ddb427688de1c9caf0100cd3.png)
1.11 字符转换:
int tolower ( int c );//大写转小写
int toupper ( int c );//小写转大写
/* isupper example */
#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;//字符串大写字母转小写字母
}
![](https://i-blog.csdnimg.cn/blog_migrate/b62c3cc9fb5ab6efbfa94bf93dc7dc81.png)
1.12 memcpy //内存拷贝
void * memcpy ( void * destination, const void * source, size_t num );
函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
这个函数在遇到 '\0' 的时候并不会停下来。
如果source和destination有任何的重叠,复制的结果都是未定义的。
模拟实现:
void* my_memcpy(void* dest, const void* src, size_t num) {
assert(dest != NULL);
assert(src != NULL);
void* ret = dest;//记录初始地址
while (num--)//每拷贝一个就减一
{
*(char*)dest = *(char*)src;//无符号整形不能解引用,强制转换为char*类型一个字节一个字节解引用拷贝
++(char*)src;//先转换后++
++(char*)dest;
}
return ret;
}
struct S
{
char name[20];
int age;
};
int main() {
int arr1[] = { 1,2,3,4,5 };
int arr2[5] = { 0 };
struct S arr3[] = { {"张三",24},{"李四",33} };
struct S arr4[3] = { 0 };
my_memcpy(arr2, arr1, sizeof(arr1));
my_memcpy(arr4, arr3, sizeof(arr3));
}
C语言标准规定:
memcpy:只需要处理 不重叠的内存拷贝就行了
memmove:处理重叠内存拷贝
1.12 memmove //负责重叠空间拷贝
void * memmove ( void * destination, const void * source, size_t num );
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。如果源空间和目标空间出现重叠,就得使用memmove函数处理。
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] = "memmove can be very useful......";
memmove (str+20,str+15,11);
puts (str);
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/715fd4eecbde4e7410bbe46fd55672ba.png)
模拟实现:
void* my_memmove(void* dest, void* src, size_t count) {
assert(dest != NULL);
assert(src != NULL);
#pragma warning(disable : 4996)
#include<stdio.h>
#include<assert.h>
#include<string.h>
void* my_memmove(void* dest, void* src, size_t count) {
assert(dest != NULL);
assert(src != NULL);
void* ret = dest;
if (dest < src)//从前往后拷贝
{
while (count--) {
*(char*)dest = *(char*)src;
++(char*)src;
++(char*)dest;
}
}
else//从后往前拷贝
{
while (count--)
{
*((char*)dest + count) = *((char*)src + count);//从4*n - 1 个字节往前移动 -以为char*解引用每一次只解引用一个字节
}
}
return ret;
}
struct S
{
char name[20];
int age;
};
int main() {
int i = 0;
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
struct S arr3[] = { {"张三",24},{"李四",33} };
struct S arr4[3] = { 0 };
my_memmove(arr1+3, arr1, 20);
//my_memcpy(arr4, arr3, sizeof(arr3));
for (i = 0; i < 5; i++) {
printf("%d ", arr1[i]);
}
}
1.13 memcmp //内存比较
int memcmp ( const void * ptr1,
const void * ptr2,
size_t num );-//比较的是字节的个数
比较从ptr1和ptr2指针开始的num个字节
返回值如下:
![](https://i-blog.csdnimg.cn/blog_migrate/6bad485174dad7c32f293636d3bf949c.png)
#include <stdio.h>
#include <string.h>
int main()
{
char buffer1[] = "DWgaOtP12df0";
char buffer2[] = "DWGAOTP12DF0";
int n;
n = memcmp(buffer1, buffer2, sizeof(buffer1));
if (n > 0)
{
printf("'%s' is greater than '%s'.\n", buffer1, buffer2);
}
else if (n < 0)
{
printf("'%s' is less than '%s'.\n", buffer1, buffer2);
}
else
{
printf("'%s' is the same as '%s'.\n", buffer1, buffer2);
}
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/61e04dc3e6d012d5a47b2386fa4eb6f4.png)
1.14 memset //设置缓冲区为特定的字符
void * memset ( void * dest,
int value, -设置的字符是什么
size_t num );-设置多少个字符-单位是字节
![](https://i-blog.csdnimg.cn/blog_migrate/28cd609f99919266862d92454e1ffde0.png)
如果用int型,不适当操作可能导致得不到想要的数组,因为memset是一个一个字节设定的