目录
2)会将源字符串中的 '\0' 拷贝到目标空间。所以源字符串里必须要有\0,否则没法正确拷贝。
2.2从目的字符串的\0开始追加,同时把源字符串末尾的\0也追加进去了
3. strcmp - 比较字符串内容是否相同(对应的字符比较)
3.1错误案例 - 在比较用户输入密码的时候很容易犯这种错误
1.2. 如果空间足够,但 拷贝的长度 > 源字符长度 ,默认补\0,拷贝上去
2.2追加超过自身长度字符时,追加完自己所有的字符之后加上\0,到此为止了,不会再追加了
C 语言中对字符和字符串的处理很是频繁,但是 C语言本身是没有字符串类型的 ,字符串通常放在 常量字符串 中或者 字符数组 中。字符串常量 适用于那些对它不做修改的字符串函数 .
一、strlen求字符串长度
size_t strlen ( const char * str );
- 字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' )。
- 参数指向的字符串必须要以 '\0' 结束。
- 注意函数的返回值为size_t,是无符号的( 易错 )
1. 函数的返回值为size_t,是无符号的( 易错 )
int main()
{
if (strlen("abcdef") - strlen("bbb") > 0)//3-6=-2 >0 不成立,为什么还会是 >
{
printf(">\n");
}
else
{
printf("<=\n");
//printf("%u\n", strlen(str2) - strlen(str1));//4294967293
}
//最后输出 > ,因为strlen()函数的返回值是 size_t(无符号整型)
//所以求出的-3 当作无符号整型来看,是一个特别大的正数
//10000000 ...00000011
//11111111 ...11111100
//11111111 ...11111101 - -3的补码
//11111111 ...11111101 看作无符号整型,值为4294967293 ,故输出的是 >
return 0;
}
2.模拟实现strlen函数 (三种方法)
//模拟实现strlen
#include<assert.h>
//1.计数器(创建count变量)
int my_strlen_count(const char* str)//因为只是计算字符串长度,不希望指针指向的内容改变,所以加上const安全
{
assert(str);//断言str不为空指针时,才可以使用,否则警告
int count = 0;
while (*str != '\0')//解引用指针如果不是\0则计数,否则跳出循环
{
count++;
str++;
}
return count;
}
//2.递归 - (如果题目要求不创建临时变量)
int my_strlen_digui(const char* str)
{
assert(str);//断言str不为空指针时,才可以使用,否则警告
if (*str != '\0') //没有遇到\0,1+ 函数(指针后移一位)
{
return 1+my_strlen_digui(str + 1);
}
else//遇到\0,返回0
{
return 0;
}
}
//3.指针-指针 (求长度)
int my_strlen_point(const char* str)
{
assert(str);//断言str不为空指针时,才可以使用,否则警告
char* p = str;
while (*str)//当*str不为\0时,str一直往后走,直到\0结束
{
str++;
}
return str - p;//指针str此时指向字符串结束标志\0,p指向字符串起始位置,
//str - p得到指针之间的距离,也就是字符串的长度
}
int main()
{
char arr[] = "bit";
printf("%d\n", my_strlen_count(arr));
printf("%d\n", my_strlen_digui(arr));
printf("%d\n", my_strlen_point(arr));
return 0;
}
二、长度不受限制的字符串函数
1.strcpy - 拷贝字符串
char* strcpy ( char * destination , const char * source );
- 源字符串必须以 '\0' 结束。
- 会将源字符串中的 '\0' 拷贝到目标空间。
- 目标空间必须足够大,以确保能存放源字符串。
- 目标空间必须可变。
1)使用
#include<string.h>
int main()
{
char arr1[] = "abcdef";
char arr2[20] = { 0 };
strcpy(arr2, arr1);//将 arr1 的内容,拷贝到 arr2 中
printf("%s\n", arr2);//abcdef
return 0;
}
2)会将源字符串中的 '\0' 拷贝到目标空间。所以源字符串里必须要有\0,否则没法正确拷贝。
int main()
{
char arr1[] = "abc\0def";//在abcdef中加了\0,观察拷贝了什么
//char arr1[] = { 'a','b','c' };//err,源字符串没有\0,当拷贝时,找不到\0,没法正确结束,会一直拷贝,直到找到\0为止
//这里输出的结果就是 abc烫烫?S?根X
char arr2[20] = "xxxxxx";
strcpy(arr2, arr1);//将 arr1 (包含\0)前的内容,拷贝到 arr2 中
printf("%s\n", arr2);//abc
//拷贝之后:数组里的内容是 abc\0xx
//但是因为打印的字符串%s,所以到\0之前就结束了,故指看的了abc
return 0;
}
1.2.模拟实现strcpy
//模拟实现strcpy
//strcpy返回的目标空间的起始地址(所以是char*)
char* my_strcpy(char* dest, const char* src)//源空间的内容是不能改变的,所以加上const防止被篡改
{
assert(dest && src);//断言,当dest和src都是有效指针才能使用,否则(空指针)无法使用
char* ret = dest;//记录dest起始地址
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[] = "hehe";
char arr2[20] = { 0 };
my_strcpy(arr2, arr1);
printf("%s\n", arr2);
printf("%s\n", my_strcpy(arr2, arr1));
//因为返回值是char*,可以直接打印这个函数结果,可以不创建变量存起来
return 0;
}
1.3.所有字符串函数需要注意的点!!
1)目标空间必须足够大
错误示范
int main()
{
char arr1[] = "abcdef";//7个字符
char arr2[3] = { 0 };//3个空间
strcpy(arr2, arr1);//将7个字符放入3个空间,根本不够,会报错
printf("%s\n", arr2);
return 0;
}
2)目标空间必须可变。
错误示范
int main()
{
char* p = "abcdefgh";//p指向的是一个常量字符串,空间是不可变的
char arr2[] = "hehe";
strcpy(p, arr2);//err.欲把arr2的内容,放入一个不可变的空间里,是行不通的
return 0;
}
2.strcat 追加字符串
char * strcat ( char * destination , const char * source );
- 源字符串必须以 '\0' 结束。
- 从目的字符串的\0开始追加(覆盖掉\0)
- 目标空间必须有足够的大,能容纳下源字符串的内容。
- 目标空间必须可修改。
- 字符串自己给自己追加,如何?- 死循环
2.1使用
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
strcat(arr1, arr2);//将 arr2 的内容,追加到 arr1 里
printf("%s\n", arr1);//hello world
return 0;
}
2.2从目的字符串的\0开始追加,同时把源字符串末尾的\0也追加进去了
说明:strcat,源目字符串数据里必须都要有\0
目的字符串\0,决定了追加开始的位置;
源字符串的\0决定了追加结束的位置
int main()
{
char arr1[20] = "hello \0xxxxxxx";//后面加上\0xxxxxxx方便测试和观察
char arr2[] = "world";
strcat(arr1, arr2);//
printf("%s\n", arr1);//hello world
//追加后,目的数组arr1的元素应该为:hello world\0xx
//打印字符串,得到hello world
return 0;
}
2.3模拟实现strcat
//模拟实现strcat
char* my_strcat(char* dest, const char* src)//源字符串是不能被修改的,加上const
{
assert(dest && src);//断言,两个指针为有效指针
char* ret = dest;//记录dest的起始位置
//先让指针找到目标字符串的第一个\0
while (*dest)//直到dest指向\0结束循环
{
dest++;
}
//追加(strcpy拷贝复制源字符串)
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
my_strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
2.4strcat自己追加自己?不行!!
虽然在这个编译器,没有问题,但无法保证在其他编译器上不会出现问题,尽量不要使用这个函数
若要自己追加自己可以使用strncat,稍微安全一些
int main()
{
char arr1[20] = "bit";
strcat(arr1, arr1);
printf("%s\n", arr1);
return 0;
}
3. strcmp - 比较字符串内容是否相同(对应的字符比较)
int strcmp ( const char * str1 , const char * str2 );
第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字
那么如何判断两个字符串? - strcmp
3.1错误案例 - 在比较用户输入密码的时候很容易犯这种错误
int main()
{
//比较两个字符串内容的时候,不能用==,应该用strcmp
if ("abcdef" == "qwertyu")//这里比较的是首元素地址,不是字符串的内容
{ //这里比较的是a和q地址
;
}
return 0;
}
3.2使用
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abq";
int ret = strcmp(arr1, arr2);
//比较对应字符的ASCII码的大小,相等往后比较,直到某个字符不相等,开始比较
//大于返回大于0的数,小于返回小于0的数,相等返回0
printf("%d", ret);//c < q ,返回小于0的数 -1
}
3.3模拟实现strcmp
//模拟实现strcmp
int my_strcmp(const char* str1,const char* str2)//两个字符串比较,两个都不能改变,所以都加上了const
{
assert(str1 && str2);//断言,两个指针有效才能使用
while (*str1 == *str2)
{
if (*str1 == '\0')//两个字符串对应字符比较完之后都相等,一直到\0的时候,返回0
{
return 0;
}
str1++;
str2++;
}
return *str1 - *str2;//如果大于相减的数就是个正数,相反就是个负数
}
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abq";
int ret = my_strcmp(arr1, arr2);
printf("%d", ret);//c < q ,返回小于0的数 -1
}
4.总结
strcpy,strcat,strcmp这三个都是长度不受限制的函数,使用时容易出错
所以可以使用长度受限制的函数:strncpy,strncat,strncmp
例如:即使目标空间不够放入这么多个字符,也报了警告,但依然把多余的字符拷贝进去了
int main()
{
char arr1[] = "abcdef";//7个字符
char arr2[5] = { 0 };//5个空间
strcpy(arr2, arr1);//将7个字符放入5个空间,根本不够,会报错
printf("%s\n", arr2);
return 0;
}
三、长度受限制的字符串函数介绍
1.strncpy
char * strncpy ( char * destination , const char * source , size_t num );
因为有了长度限制,在编写的时候,可能会多想一下拷贝几个符,相对strcpy还是安全一些;
但是硬要超过限度拷贝,那还是会出错的。(报警并且强制拷贝)1.如果 拷贝的长度 > 目标空间(不够),强硬拷贝会出错,请三思后行
2.如果空间足够,但 拷贝的长度 > 源字符长度 ,默认补\0,拷贝上去
int main()
{
char arr1[] = "abcdef";//7个字符
char arr2[5] = { 0 };//5个空间
//char arr2[5] = "xxxxx";//可以观察是否只拷贝了3个
strncpy(arr2,arr1,3);
//strncpy拷贝,多了一个参数,拷贝几个字符
//拷贝3个字符,就拷贝arr1的abc
printf("%s\n", arr2);
return 0;
}
1.2. 如果空间足够,但 拷贝的长度 > 源字符长度 ,默认补\0,拷贝上去
虽然最终打印的字符串结果没有变化,但是,调试可以看到这个细节
int main()
{
char arr1[] = "abcdef";//7个字符
char arr2[20] = "xxxxxxxxxxxxxxxxxxx";//20个空间
strncpy(arr2, arr1, 10);
//2.如果空间足够,但 拷贝的长度 > 源字符长度 ,默认补\0,拷贝上去
//a b c d e f \0 \0 \0 \0 x x x x x x x x x
printf("%s\n", arr2);
//打印字符串,得到abcdef
return 0;
}
2.strncat
char * strncat ( char * destination, const char * source, size_t num );
2.1使用
int main()
{
char arr1[20] = "hello \0xxxxx";
char arr2[] = "abcdef";
strncat(arr1, arr2, 3);//将arr2的3个字符,追加到arr1里
printf("%s\n", arr1);//hello abc
//数组里是:hello abc\0xx\0\0\0\0\0\0\0\0
// 追加完abc三个字符之后,会补上一个\0
//打印字符串得到hello abc
return 0;
}
2.2追加超过自身长度字符时,追加完自己所有的字符之后加上\0,到此为止了,不会再追加了
虽然最终打印的字符串结果没有变化,但是,调试可以看到这个细节
int main()
{
char arr1[20] = "hello \0xxxxxxxxxxxxx";
char arr2[] = "abcdef";
strncat(arr1, arr2, 10);//追加10个字符(超过arr2的范围)
printf("%s\n", arr1);//hello abcdef
//数组里是:hello abcdef\0xxxxxxx
// 追加超过自身长度字符时,追加完自己所有的字符之后加上\0,到此为止了,不会再追加了
//打印字符串得到hello abc
return 0;
}
2.3strncat可以自己追加自己
//strcat不能自己给自己追加,有可能会修改掉字符串最后的\0,可能导致死循环
//如果要自己给自己增加,可以使用strncat
3.strncmp
int strncmp ( const char * str1, const char * str2, size_t num );
//********** strncmp
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abc";
int ret = strncmp(arr1, arr2, 3);//比较 arr1 和 arr2 前3个字符
printf("%d\n", ret);
return 0;
}