c语言深入浅出,玩爆常见字符串,内存操作库函数(爆肝最长时间之作)
Posted 小海浪.
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c语言深入浅出,玩爆常见字符串,内存操作库函数(爆肝最长时间之作)相关的知识,希望对你有一定的参考价值。
妈呀,我终于写完博客了!!!
c语言深入浅出,玩爆常见字符串,内存操作库函数(爆肝最长时间之作)
文章简介
我们在学习c语言,学习编程的过程中,对于其中的许多算法,知识点既要知其然,也要知其所以然,既要知道用这些算法,知识点写程序,实现相应的功能,也要知道为什么要这样写,这样的优点是什么,原理是什么。我们在学习过程中学习的是思想,方法,而不是代码。c语言有许多库函数,方便程序员的使用,但对于初学者为了很好的使用这些库函数,一方面我们要查看c语言使用手册了解其用法,另一方面我们也可以通过模拟实现这些库函数或者看其源码,从原理上来看它们是怎么实现的。因此本篇博文的主要内容就是字符串库函数,内存操作函数的介绍及其模拟实现
字符串库函数的介绍及其模拟实现
strlen
函数原型说明
size_t strlen ( const char * str );
- 字符串已经以’\\0’ 作为结束标志,strlen函数返回的是在字符串中’\\0’ 前面出现的字符个数(不包含’\\0’ )。
- 参数指向的字符串必须要以’\\0’ 结束。 注意函数的返回值为size_t,是无符号的( 易错)
- 函数参数是字符指针,也就是传过要计算长度的字符串首字符的首地址,并用const修饰,表示这个参数只能读,不希望被修改,不能修改原来的字符串,使代码更安全。
库函数的实现
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "abc";
char arr2[] = { 'a', 'b', 'c' };
char arr3[] = { 'a', 'b', 'c', '\\0'};
int len1 = strlen(arr1);
int len2= strlen(arr2);
int len3 = strlen(arr3);
printf("%d\\n", len1);
printf("%d\\n", len2);
printf("%d\\n", len3);
return 0;
}
C语言中对字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。 上面创建了三个字符数组,arr1里面存放了4个元素包括‘\\0’,是一个标准的字符串,arr2分别放了a,b,c三个元素没有\\0,strlen函数在统计完数组元素个数后,继续在后面的内存空间里面寻找,随机的找到\\0结束,因此打印结果也是随机值,arr3里面四个元素,后面那个\\0是主动添加来进行验证的。程序运行结果如图:
结下来我们来看下面的代码结果是什么的????小心陷阱哦。
#include <stdio.h>
int main()
{
char str1 []= "hello";
char str2 []= "boy";
if (strlen(str2) - strlen(str1) > 0)
{
printf("str2>str1\\n");
}
else
{
printf("srt1>str2\\n");
}
return 0;
}
解释:
库里面的strlen的返回值为size_t,返回的是一个无符号数,也就是没有正负之分,因此对于数据的存储打印很像正数(原反补相同),之后原码打印。首先求得的字符个数相减,还是转换为补码相减,减得一个负数,最高位的符号位为1,之后转化为无符号的数字就是一个很大的数字了,大概为2的30次方左右,大于0,所以程序的结果为str2>str1。这里的借用这个代码其实是为强调strlen函数返回值的类型
模拟实现:
这里的模拟实现思路和库函数原理基本一样,就是找\\0之前的字符,找到一个字符,计数器count的值加一,找到\\0就结束寻找,之后count 的值就是字符的个数。
在这里插入代码片#define _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<assert.h>
size_t my_strlen(const char *str)
{
assert(NULL);//断言一下防止是空指针,为空指针程序自动崩溃
int count = 0;
while (*str)
{
count++;
str++;
}
return count;//返回值打印
}
int main()
{
int ret = 0;
char arr[] = "abcde";
ret = my_strlen(arr);//函数调用
printf("%d", ret);
return 0;
}
strcpy
函数原型说明
char* strcpy(char * destination, const char * source );
Copies the C string pointed by source into the array pointed by
destination, including the terminating null character (and stopping at
that point).
- 源字符串必须以’\\0’ 结束。
- 会将源字符串中的’\\0’ 拷贝到目标空间。
- 目标空间必须足够大,以确保能存放源字符串。
- 目标空间必须可变。
通过以下代码,我们可以简单的使用strcpy函数,并且拿捏一下细节,放开解释中文后面的代码,运行下,探讨一下小细节。
//#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
//源字符串必须以'\\0' 结束
//会将源字符串中的'\\0' 拷贝到目标空间。
char arr[20] = "##########";
strcpy(arr, "hello");//string copy,通过调试观察字符串末尾的\\0是否被拷贝到目标空间
//char arr2[] = { 'a', 'b', 'c' };//\\0作为拷贝结束的标志,arr2里面没有\\0,导致strcpy一直向arr2后面的内存空间拷贝字符
//拷贝了许多字符到arr里面,撑爆了arr数组,导致数组越界,程序崩溃
//strcpy(arr, arr2);//string copy
printf("%s\\n", arr);
_______________________________________________________________________________________
//目标空间必须足够大,以确保能存放源字符串。
//char arr[5] = "####";
//char p[] = "hello world";//源字符串太大,不能拷贝到目标空间里面,强行可以烤进去,但程序也挂了
//strcpy(arr, p);//
//printf("%s\\n", arr);
_______________________________________________________________________________________
//目标空间必须可变
//char str [15]="$$$$$$$$$$$$$$$"//字符数组,创建字符变量,正常运行
//char *str="$$$$$$$$$"//常量字符串,不可以修改(常量),复制过去,如果运行程序奔溃
//char p[] = "hello world";
//strcpy(str, p);
//
//
// return 0;
//}
如放开横线上第一段代码,调试结果如下;其余的大家自己去试试吧,说多少不练都是扯蛋。
模拟实现
基本思路就是定义两个指针变量,一个指向源字符串首地址,一个指向目标字符串\\0的位置,从源字符串,通过指针方式,拷贝一个字符到目标地址,之后两颗指针都向后移指向后面的一个位置,继续拷贝,直到把源字符串的\\0拷过去为止。形成一个标准的字符串。
#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* src)//两个指针分别接收两个数组首元素的地址,
{
assert(dest != NULL);
assert(src != NULL);
char* p = dest;
//方法一:
//while (*src != '\\0')
//{
// *dest = *src;
// dest++;
// src++;
//}
//*dest = *src;
//方法二:
while (*dest++ = *src++)//后置++用再加,拷完\\0结束
{
;
}
return p;
}
int main()
{
char arr1[] = "##########";
char arr2[] = "hello";
my_strcpy(arr1, arr2);//模拟实现strcpy函数
printf("%s", arr1);
return 0;
}
strcat
函数原型说明: char * strcat ( char * destination, const char * source );
- 源字符串必须以’\\0’ 结束。
- 目标空间必须有足够的大,能容纳下源字符串的内容。
- 目标空间必须可修改。
库函数实现代码及细节探究
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[30] = "hello";
//arr1空间需要足够大来接收追加过来的内容
//否则会造成越界访问
char arr2[] = "world";
//将字符串arr2内容追加给arr1
strcat(arr1, arr2);
printf("%s", arr1);
return 0;
}
是否追加\\0探究代码
在这里插入代码片#include<stdio.h>
#include<string.h>
int main()
{
char arr1[30] = "hello\\0########";//调试看是否追加\\0, 因为追加是从\\0的位置开始的,主动放个\\0之后调试好观察,
//否则从最后一个#追加你如何观察呢?
char arr2[] = "world";
strcat(arr1, arr2);
printf("%s", arr1);
return 0;
}
调试截图
strcat函数进行字符串追加的算法分析及my_strcat的模拟实现:
1.定义两个指针变量分别指向,目标数组,源数组
2.dest指针找到目的空间中的’\\0地址’
3.追加源字符串,包含\\0 拷贝成功,返回的目标空间的起始地址,即可打印拷贝的字符串了。
模拟实现代码:
#include<stdio.h>
#include<assert.h>
char* my_strcat(char* dest, const char* src)
{
assert(dest != NULL);
assert(src != NULL);
char* dest_start = dest;
//1.找到目的空间中的'\\0'
while (*dest != '\\0')//跳过不是'\\0'的字符
{
dest++;
}
//2追加源字符串,包含\\0
while (*dest++ = *src++)
{
;
}
return dest_start;//3返回的目标空间的起始地址
}
int main()
{
char arr1[30] = "hello";
char arr2[] = "world";
my_strcat(arr1, arr2);//模拟实现strcat
printf("%s", arr1);
return 0;
}
算法动图演示:
strcmp
函数原型说明:
int strcmp ( const char * str1, const char * str2 ); This function
starts comparing the first character of each string. If they are equal
to each other, it continues with the following pairs until the
characters differ or until a terminating null-character is reached.
标准规定: 第一个字符串大于第二个字符串,则返回大于0的数字 第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字
vs编译器里面strcmp函数的使用,vs编译器里面设计的是,第一个字符串大于第二个字符串,则返回1的数字,第一个字符串等于第二个字符串则返回0,第一个字符串小于第二个字符串,则返-1数字,不同编译器有所差异,我们知道就行,之后写代码最好按得标准写就行。下面代码的结果为-1,0,1。
在这里插入代码片#include<stdio.h>
#include<string.h>
int main()
{
//if ("obc" > "abcdef")//不能这样比较,实际在比较两个字符的地址
//{
//}
//strcmp - 字符串比较大小的
int ret1 = strcmp("abbb", "abq");//<0
int ret2 = strcmp("aaa", "aaa");//=0
int ret3 = strcmp("aab", "aaa");//>0
printf("%d\\n", ret1);
printf("%d\\n", ret2);
printf("%d\\n", ret3);
return 0;
}
my_strcmp的模拟实现计算法分析:
方法:
1.传过两个指针s1,s2分别指向两个字符串的首地址字符,从首字符向后一一比较,比较的是字符的ASCALL码值,以第一个字符串为比较标准。
2.两个字符要么相等,要么不等,不等,很好可以比较大小了,两个字符相减得到一个数字判断字符串的大小,如果某一字符为\\0,结束比较(此时\\0和一个字符也可以比较大小),如果不为\\0两个指针分别向后移动比较,如此循环下去。
#include<stdio.h>
#include<string.h>
int my_strcmp(const char* s1, const char* s2)
{
assert(s1 && s2);
while (*s1 == *s2)
{
if (*s1 == '\\0')
{
return 0;
}
s1++;
s2++;
}
return *s1 - *s2;//两个字符不相等肯定有大小之分,根据减出数字大小判断两个字符串大小
}
int main()
{
char* p = "abcd";
char* q = "abc";
int ret = my_strcmp(p, q);
if (ret > 0)
{
printf("p > q\\n");//象征性的表示p指向的字符串比q指向的字符串大
}
else if (ret < 0)
{
printf("p < q\\n");
}
else
{
printf("p == q\\n");
}
return 0;
}
动图算法分析:
strstr
函数原型说明: char * strstr ( 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.
【参数1】str1用于查找字符串的母串。
【参数2】str2待查找字符串的子串。
- *函数返回类型:char ,返回查找到的地址
- 函数功能:查找一个字符串是否属于另一个字符串
- 如果在str1中找到了字符串str2,则返回str1中字串str2的起始地址 如果查找不到,返回空指针NULL
- 如果str1中含有多个str2字符串中字符内容,返回的是str2中第一个被查找到的字符地址起,从那个位置开始打印后面的字符串。 库函数的实现: 横线上面的结果为abcabbc,下面的结果为(null)空指针。**
在这里插入代码片#define _CRT_SECURE_NO_WARNINGS
#include<string.h>
#include<stdio.h>
int main()
{
char* p = "abcabbc";//
//char* q = "abcbbc";
char* q = "abc";
strstr( p, q);
//_________________________________________
//char* p = "abcabbc";//
//char* q = "abcbbc";
// strstr(p, q);
printf("%s\\n", strstr(p, q));
return 0;
}
my_strstr的模拟实现及算法分析:
一个字符串判断是否属于另一个字符串,首先这个字符串要不为空字符串,如果为空字符串,直接返回母字符串的内容,否则比较字符内容。在母字符串字符比较内容不为0的情况下比较(你想要是一个母字符串的内容比要比较的子串的内容都小这个字符串是绝对不会属于那个母字符串的内容的),之后比较,如果比较的字符串第一个字符相同,则都向后移动比较,相同继续下去,如果比较的子字符串为\\0证明比完了,这个字符也属于母字符串,返回母字符串比较元素的起始位置开始打印字符。比较内容不相等,重新下一乱比较,母字符串返回到开始比较的下一个位置,而子字符串每次都是从起始位置比较的。因此该模拟代码定义了五个指针变量,s1,s2用于向后依次移动比较字符,cp用于记录母字符串的比较位置,str2用于子字符串与母字符串比较时,某一位置比较不成功,回退下一轮比较时,重新指向下一个比较位置。这是对模拟代码的大致解释。
#include<stdio.h>
#include<assert.h>
char* my_strstr(const char* str1, const char* str2)
{
assert(str1 != NULL);
assert(str2 != NULL);
char* cp = str1;
char* s1 = NULL;
char* s2 = NULL;
if (*str2 == '\\0')
return (char*)str1;
while (*cp != '\\0')
{
s1 = cp;
s2 = str2;
while ((*s1 == *s2) && (*s1 != '\\0') && (*s2!= '\\0'))
{
s1++;
s2++;
}
if (*s2 == '\\0')
{
return cp;//找到字串的情况
}
cp++;
}
return NULL;//找不到字符的情况
}
动图模拟:
后面带n的字符串操作函数,在使用时要考虑比较,追加,复制等的长度,更加完善一点,但实现思路基本一致。
strncpy
- 函数原型说明: char * strncpy ( char * destination, const char * source, size_t num );
- 拷贝num个字符从源字符串到目标空间。
- 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个
- 如果拷贝的字符长度大于目的地空间的所能容纳的字符程序崩溃。
库函数实现代码:
#define _CRT_SECURE_NO_WARNINGS
#include<string.h>
#include<stdio.h>
int main()
{//情况一
//char arr1[20] = "abcdef";
//char arr2[] = "qwer";
//strncpy(arr1, arr2, 6);
//printf("%s\\n", arr1);//qwer
//________________________________________________________
//情况二
char arr1[20] = "abcdef";
char arr3[] = "qwer";
strncpy(arr1, arr3, 4);
printf("%s\\n", arr1); //结果qwerabcdef
return 0;
}
如图:
my_strncpy函数的模拟实现
在这里插入代码片#include<stdio.h>
#include<assert.h>
//模拟实现strncpy
char* my_strncpy(char* dest, const char* src, size_t n)
{
char* dest_start = dest;
while ((n > 0) && (*src != '\\0'))
{
*dest = *src;
dest++;
src++;
n--;
}
while (n > 0)
{
*dest = '\\0';
dest++;
n--;
}
return dest_start;
}
int main()
{
char arr1[10] = "abcdefg";
char arr2[] = "1234";
my_strncpy(arr1, arr2, 4);
printf("%s", arr1);
return 0;
}
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 nullcharacter is
copied.strncmp
这个函数和strcat很相似,其他参数基本一样只是增加了一个追加参数的限制num,当strncat追加时,分两种情况追加:
1.如果追加的字符数大于源字符串,追加完源字符串的\\0即可,
2.如果追加的字符串小于源字符串,追加完需要追加的字符后,编译器自己在后面再追加一个\\0即可。下面的代码就大致解释了它的功能。 strncat库函数的代码实现:
在这里插入代码片#define _CRT_SECURE_NO_WARNINGS
#include<string.h>
#include<stdio.h>
int main()
{//情况一
/*char arr1[20] = "hello ";
char arr2[] = "world";
strncat(arr1, arr2, 10);
printf("%s\\n", arr1);*//结果hello world
//_______________________________________________________________________
//情况二
char arr1[20] = "hello ";
char arr2[] = "world";
strncat(arr1, arr2, 3);
printf("%s\\n", arr1);//结果hello wor
return 0;
}
算法简单分析:
如图:
情况一
情况二
my_strnca的模拟实现
#include<stdio.h>
#include<assert.h>
char* my_strncat(char* dest, const char* src能列举些C语言中比较常见重要库函数的用法吗?