🐵本文会将会对剩余的字符串库函数和内存函数进行讲解
strstr函数原型:
strstr用于在字符串中找子串,strstr会返回str1中出现str2的起始地址,如果在str1中没有找到str2,则返回空指针
- #include<stdio.h>
- #include<string.h>
-
- int main()
- {
- char arr1[] = "i like chess";
- char arr2[] = "like";
-
- char* ret = strstr(arr1, arr2);
- if (ret != NULL)
- {
- printf("%s", ret);
- }
- else
- {
- printf("没找到");
- }
-
- return 0;
- }
strstr会返回arr1中第一次出现arr2的地址,这里就是'l'的地址,将其传给指针ret,在打印时会从'l'处依次向后打印,结果为like chess;如果arr2是字符串"abc"的话,strstr就会返回空指针,打印结果为没找到
让str1和str2从第一个地址开始比较,如果相等那str1和str2都向后走一个字节,如果str1和str2一直相等,就应该返回str1中出现str2的起始位置,所以这里应该再来一个指针p用来保存str1和str2比较时str1的起始位置
如果str1和str2进行比较时遇到不相等的字符,就说明从p位置处开始不可能找到str2,所以应先让p向后走一个字节,再让str1回到p处,比如上图中,str1和str2从起始位置开始比较,'a'和'b'不相等,那就说明从p位置处开始不可能出现str2,那么p应该向后走一个字节,再让str1指向p所指的地址;str2应该回到它的起始地址,这里应再定义两个个指针s1,s2,让str1和str2不变,让s1和s2进行比较
总结:如果相等s1和s2一直想后走,当s2遇到'\0'时说明str1里有str2,返回p,如果不相等,p向后走一个字节,s1指向p,s2指向str2,直到相等返回p
代码:
- #include<stdio.h>
- #include<assert.h>
-
- char* my_strstr(const char* str1, const char* str2)
- {
- assert(str1 && str2);
- char* s1 = str1;
- char* s2 = str2;
- char* p = str1;
- while (*p)
- {
- if (*s1 == *s2)
- {
- s1++;
- s2++;
- if (*s2 == '\0')
- {
- return p;
- }
- }
- else
- {
- s2 = str2;
- p++;
- s1 = p;
- }
- }
- return NULL;
- }
-
- int main()
- {
- char arr1[] = "abbcde";
- char arr2[] = "bcd";
- char* ret = my_strstr(arr1, arr2);
- if (ret != NULL)
- {
- printf("%s\n", ret);
- }
- else
- {
- printf("没找到\n");
- }
- return 0;
- }
strtok函数原型:
strtok用于分割字符串,delimiters指定一个字符串,该字符串中包含分割str的分隔符,而str也指定一个字符串,在该字符串中,包含一个或多个分隔符分割的标记
strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针
strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串
中的位置。比如:str指定的字符串是12345@qq.com,分隔符为@和.,第一个参数不为NULL,所以strtok会将第一个标记@改为\0,并记住这个位置,在第二次使用strtok时,可以将第一参数设为NULL,这样就会从上一次保存的位置开始找第二个标记
strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标
记。
如果字符串中不存在更多的标记,则返回 NULL 指针。
由于strtok函数回改变源字符串,所以在使用strtok函数前,会临时拷贝一份源字符串,对拷贝后的字符串使用strtok函数
- #include<stdio.h>
- #include<string.h>
-
- int main()
- {
- char arr[] = "1234@qq.com";
- char buf[20] = { 0 };
- strcpy(buf, arr);
- char* p = "@.";
- char* ret = NULL;
- for (ret = strtok(buf, p); ret != NULL; ret = strtok(NULL, p))
- {
- printf("%s\n", ret);
- }
-
- return 0;
- }
for循环的初始化部分只会进行一次,这样第一次使用strtok时第一个参数是buf,之后第一个参数都是NULL,这样就能分割整个字符串
打印结果:
strerror函数原型:
strerror用于返回错误码所对应的错误信息
sterror函数通常是在出现错误码时使用该函数查看错误码所对应的错误信息,并不会特意使用,这里只是举个例子:比如查看1~9所对应的错误信息:
- #include<stdio.h>
- #include<string.h>
-
- int main()
- {
- int i = 0;
- for (i = 0; i < 10; i++)
- {
- char*ret = strerror(i);
- printf("%d: %s\n", i, ret);
- }
- return 0;
- }
以下是打印结果:
接下来会讲解memcpy,memmove,memcmp函数,字符串函数比如strcmp只能比较两个字符串,strcpy也只能拷贝字符串,但是在内存中的数据并非只有字符串,所以引入内存函数可以操作任意类型的数据
memcpy函数原型:
用于拷贝内存数据,destination指定目标字符串,source指定源字符串,num是要拷贝的字节数
- #include<stdio.h>
- #include<string.h>
-
- int main()
- {
- int arr1[] = { 1,2,3,4,5 };
- int arr2[10] = { 0 };
- size_t num = 20; //将5个数全部拷贝到arr2,所以是20个字节
- memcpy(arr2, arr1, num);
-
- return 0;
- }
通过调试观察,拷贝前:
拷贝后:
由于memcpy再拷贝时是一个字节一个字节拷贝的,所以应将des和src强转为字符指针,每完成一次拷贝就再强转为字符指针后+1,这样就能达到一个字节一个字节拷贝的目的
- #include<stdio.h>
- #include<assert.h>
- void* my_memcpy(void* des, const void* src, size_t n)
- {
- assert(des && src);
- void* ret = des;
- while (n--)
- {
- *(char*)des = *(char*)src;
- des = (char*)des + 1;
- src = (char*)src + 1;
- }
- return ret;
- }
memmove函数原型:
可以发现memmove和memcpy的函数原型一致,其实memmove和memcpy用法基本一样,差别就是当源空间和目标空间重叠时可以使用memmove;
比如说一个整形数组int arr[] = {1,2,3,4,5,6,7,8,9};将3~7的位置拷贝到1~5的位置时可以memmove
- #include<stdio.h>
- #include<string.h>
-
- int main()
- {
- int arr[] = { 1,2,3,4,5,6,7,8 };
- size_t num = 16;
- memmove(arr + 2, arr, num);
-
- return 0;
- }
通过调试观察,拷贝前:
拷贝后:
如果按照memcpy函数模拟实现的方法来模拟实现的话,无法达到预期目的(假如是将2~5拷贝到4~7的位置处):
当将2和3分别拷贝到4和5的位置后,在接下来要将4拷贝到6的位置时就已经无法达到目的了,因为此时4的位置已经被2拷贝过,再去拷贝到6的位置时,实际上是将2拷贝6的位置,那么整个拷贝操作结束后该数组会变为1 2 3 2 3 2 3 8,在这种des在src之后的情况下可以将src从后往前拷贝,5->7,4->6,3->5,2->4,这样就能达到拷贝的目的
反之当des在src之前时,应将src从前往后拷贝,这种情况下的模拟实现和memcpy一致
- #include<stdio.h>
- #include<assert.h>
- void* my_memmove(void* des, const void* src, size_t n)
- {
- assert(des && src);
- void* ret = des;
- if (des > src)
- {
- while (n--)
- {
- *((char*)des + n) = *((char*)src + n);
- }
- }
- else
- {
- while (n--)
- {
- *(char*)des = *(char*)src;
- des = (char*)des + 1;
- src = (char*)src + 1;
- }
- }
- return ret;
- }
memcmp函数原型:
用于比较两个内存数据,ptr1和ptr2分别指定两个内存数据,num代表要比较的前num个字节
注意:这里比较的是前num个字节
- #include<stdio.h>
- #include<string.h>
-
- int main()
- {
- int arr1[] = { 1,2,3,4,5 };
- int arr2[] = { 1,2,3,5,6 };
- size_t num = 0;
- scanf("%zd", &num);
-
- int cmp = memcmp(arr1, arr2, num);
- if (cmp > 0)
- {
- printf("arr1 > arr2\n");
- }
- else
- {
- printf("arr1 <= arr2\n");
- }
-
- return 0;
- }
memset函数原型:
memset用来设置内存的,ptr指向一个内存数据,value是要设置的值,num是要设置的字节数
- #include<stdio.h>
- #include<string.h>
-
- int main()
- {
- char arr[] = "hello world";
- memset(arr + 6, 'x', 5);
- printf("%s", arr); 打印结果:hello xxxxx
-
- int arr[10] = { 0 };
- memset(arr, 1, 40); //这里并不会将arr里面变为10个1,因为他是一个字节一个字节设置的
- //所以它将每个字节设为了1,那么就变成了10个非常大的数
- //综上所述,memset适合于字符数组,或者将整形数组全部设为0
- return 0;
- }
🙉至此,字符串函数和内存函数全部讲解完毕!后续将会讲解结构体、联合、枚举相关内容