char str1[] = { 'a','b','c' }; char str2[] = { "abc" }; char str3[] = "abc";
可见在不写数组元素个数,只写[ ]时,str1不会在后面自动补充\0
在写成字符串时可自动补\0
char str1[3] = { 'a','b','c' }; char str2[4] = { 'a','b','c' }; char str3[3] = { "abc" }; char str4[4] = { "abc" }; char str5[3] = "abc"; char str6[4] = "abc";
可见在输入元素后大家表现的都非常一致,当元素仅仅够字符不够加上\0时都不会主动加\0,当有元素多出时会在字符串后面出现\0
但要注意,str2这种情况有\0是因为多出的元素默认为\0
str4和str6这种情况有\0是因为字符串末尾会自己追加\0
str3和str5之所以没有追加上\0是因为元素不足
char* str1 = "abc"; char* str2 = { "abc" }; printf("%s\n", str1); printf("%s\n", str2);
这个里用字符指针接收的都是常量字符串,在内存中你看不到到底后面是否有\0
所以利用%s遇到\0停止打印这一特性,会发现str1和str2都可以正常打印创建的常量字符串,所以常量字符串后面会自动追加\0
输入“abc”这种字符串时,只要开辟的空间够多就可以自动追加\0,如果不够也就是少了\0的空间,那就无法追加\0
“abc”和{“abc”}的情况相同
{ ‘a’,‘b’,‘c’ }这种情况不论什么时候都不会自动追加\0
在开辟字符串时多出的内存会默认变为\0
size_t strlen ( const char * str );
功能:记录字符串\0前有多少个字符
1.字符串已经 '\0’作为结束标志,strlen函数返回的是在字符串中 '\0’前面出现的字符个数(不包含 ‘\0’)
2.参数指向的字符串必须要以 '\0’结束
只所以len1是15是因为str1中没有\0,strlen在str1中一直没找到\0,终于在第15个字符后找到了,所以记录15,实则是随机值,而str2中有\0,所以返回3也就是记录了3个字符
3.注意函数的返回值为size_t是无符号的
strlen(“abc”)应该是3而strlen(“qwerty”)应该是6,结果输出的是>,这时为什么呢,是因为strlen返回的是一个size_t的无符号整形,3-7虽然是-4但是size_t会认为它是一个很大的正数,所以输出的>而不是<=
修改:
strlen函数的模拟实现
计数器方法
size_t my_strlen(const char* str) { assert(str); int count = 0; while (*str++ != '\0') { count++; } return count; }
递归方法
size_t my_strlen(const char* str) { if (*str == '\0') { return 0; } else { return my_strlen(str+1) + 1; } }
指针-指针方法
size_t my_strlen(const char* str) { assert(str); char* p = str; while (*str) { str++; } return str - p; }
char* strcpy(char * destination, const char * source );
功能:将源字符串的内容拷贝到目标字符串(同时拷贝源字符串的\0)
1.源字符串必须以 '\0’结束。
此时str2是“abcdef\0”后面是有\0的,所以可以将str2拷贝给源字符串
当str2后面没有\0,将str2拷贝给str1就会失败
2.会将源字符串中的 '\0’拷贝到目标空间。
我们创建一个XXXXXXXXX字符数组,故意比str2大一些,然后让str2拷贝到str1中看看在众多X前会不会出现\0,出现了就说明strcpy会将源字符串中的 '\0’拷贝到目标空间
3.目标空间必须足够大,以确保能存放源字符串。
当要把“abcef\0”拷贝到只有4个字符空间的str1时,你会发现直接报错,是因为必须要目标空间足够大,才能确保能存放源字符串
4.目标空间必须可变。
因为str1指向的常量字符串,常量是不能修改的
strcpy函数的模拟实现
#include<stdio.h> #include<assert.h> char* my_strcpy(char*dest,const char*src) { assert(dest && src); char* ret = dest; while (*dest++=*src++) { ; } return ret; } int main() { char str1[20] = {0}; char*str2="hello world"; printf("%s", my_strcpy(str1, str2)); }
这里写的*dest++=*src++,需要多理解理解
他是先执行++,但是是后置++,放在了最后执行
再去去将src中的值赋值给dest所指向的内容,然后执行++,各自地址跳一位
然后当src指向\0时,先赋值给dest,因为\0=0,被赋值0后,dest此时表示为假了,所以可以while跳出循环
这样就能做到,将源字符串的所有字符串包括\0赋值到目标字符串
strcpy函数的返回值要返回目标字符串被替换的首地址,所以用ret先存储一下首地址,然后最后返回ret就行,这样在printf中可以直接调用函数,输出的就是被更改完毕的目标字符串,做到链式访问的效果
char * strcat ( char * destination, const char * source );
功能:从目标字符串\0处开始追加源字符串的内容(追加源字符串的\0)
1.源字符串必须以’\0’ 结束。
我们不知道是否追加源字符串的\0,所以我们可以根据,strcat函数时要从目标字符串的\0处开始追加源字符串内容,这一特点来设计一个内容去验证一下源字符串是否要追加自己的\0
可见strcat函数确实能让源字符串中的\0追加到目标字符串,所以说源字符串还必须要有\0,不然目标字符串中大概率会没有\0,是的字符串不完整,所以源字符串必须以’\0’ 结束
2.目标字符串必须以’\0’ 结束。
因为strcat函数要寻找到\0后才能追加,所以目标字符串必须要出现\0
之所以后面又追加到world是因为strcat在后面的随机值中找着找着,找到了\0最后追加在它的后面了
3.目标空间必须有足够的大,能容纳下源字符串的内容。
4.目标空间必须可修改。
目标字符串不能是常量字符串这种无法修改的字符串
strcat函数的模拟实现
#include<stdio.h> #include<assert.h> char* my_strcat(char* dest, const char* src) { assert(dest && src); char* ret = dest; while (*dest)//找个目标字符串中的\0 { dest++; } while (*dest++ = *src++)//和strcpy的实现差不多,继续拷贝 { ; } return ret; } int main() { char str1[20] = "hello"; char*str2 = " world"; my_strcat(str1, str2); printf("%s", str1); }
strcat函数的模拟实现的问题
你会发现我们实现的strcat函数后,字符串自己追加自己时就会出问题,当dest找到\0后将第一个字符赋值到\0的位置是,src指向的字符串整体就没有\0了,这样while (*dest++ = *src++),这句话就不会停止,因为解引用src不会在出现\0了,这样就会发现我们模拟实现的strcat无法让字符串自己追加自己,而我们调用的strcat是可以做到本字符串自己追加自己的
但有些版本调用strcat库函数可以实现本字符串追加到自己末尾!!!版本不同,编译器不同结果不同
上面是库函数里面提供的参考实现strcat的代码
int strcmp ( const char * str1, const char * str2 );
功能:挨个比较字符串中每个字符的ASCII码值
标注规定:strcmp比较的不是字符串的长度,而是比较字符串中对应位置上的字符大小,如果相同,就比较下一个,直到不用或者都遇到\0
第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字
vs2019编译器中的库函数大于时返回1,等于返回0,小于返回-1
如果字符串的长度不相同,且前面的字符大小都一样的情况下,短字符串会跳到字符串最后\0然后去和长字符串对应的字符比较,这样长的字符串此时会比短字符串大
strcmp函数的模拟实现
这里我们返回的时候直接返回两个字符ASCII差值,更利用实现和使用,返回值虽然不和vs只返回1或者-1一样,但是也是返回>0或者<0
#include<stdio.h> #include<assert.h> int my_strcmp(const char* str1, const char* str2) { assert(str1 && str2); while (*str1 == *str2) { if (str1 == 0) { return 0; } str1++; str2++; } return *str1 - *str2; } int main() { char str1[5]="abcde"; char str2[5] = "abc"; int i = my_strcmp(str1, str2); if (i > 0) { printf(">\n"); } else { if (i == 0) { printf("=\n"); } else { printf("<\n"); } } printf("%d",my_strcmp(str1, str2)); }
char * strncpy ( char * destination, const char * source, size_t num );
功能:拷贝num个字符从源字符串到目标空间
1.拷贝num个字符从源字符串到目标空间。
2.如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加\0,直到num个
还要记住其他情况都要遵循上面所写的strcpy的情况,比如说目标字符串空间必须充足,长度超出会报错
char * strncat ( char * destination, const char * source, size_t num );
功能:从目标字符串\0处开始追加num个源字符串的内容(追加源字符串的\0)
追加完毕后会在后面放入一个\0
目标字符串开辟的内存必须足够多,不然会报错
int strncmp ( const char * str1, const char * str2, size_t num );
功能:挨个比较前num个字符串中每个字符的ASCII码值
返回值和strcmp相同
制定了比较字符串的长度
对比长度要在制定范围内,不要超出字符串长度,否则返回内容不可预料
char * strstr ( const char *str1, const char * str2);
功能:寻找目标字符串中是否有源字符串,有的话返回出现源字符串的起始地址,没有返回NULL
strstr函数的模拟实现
#include<stdio.h> #include<string.h> char* my_strstr(const char* str1, const char* str2) { char* s1 = str1; char* s2 = str2; char* cur = str1; while (*cur) { s1 = cur; while (*s1 && *s2 && (*s1 == *s2)) { s1++; s2++; } if (*s2 == '\0') { return cur; } cur++; } return NULL; } int main() { char str1[20] = "abcdefghijk"; char str2[5] = "ghi"; char* ret = my_strstr(str1, str2); if (ret==NULL) { printf("找不到子串\n"); } else { printf("%s", ret); } }