C语言进阶学习日志:字符串和内存函数(一)

目录

strlen函数

模拟strlen函数

长度不受限制的字符串函数 

strcpy函数:

在使用 函数之前要注意这几点

① 源字符串必须以'\0'结尾

strcmp函数:

长度受限制的字符串函数

strncpy函数:

strncat函数:

strncmp函数:

strstr函数:

strtok函数:

strerror函数:

字符分类函数:

字符转换函数:


strlen函数

strlen函数 用于求字符串的的长度不包括'\0' 注意strlen函数指向的字符串必须有'\0'结尾

C语言进阶学习日志:字符串和内存函数(一)_第1张图片

这个是MSDN的讲解  头文件是

模拟strlen函数

模拟strlen函数有三种方法是 计数法 递归法 指针运算法

计数法:

#define _CRT_SECURE_NO_WARNINGS 1
#include 
#include 
int my_strlen(const char* arr)//因为我们只是去计算一个字符串中的字符个数 并不会通过它的地址改变
{                                来改变它的内容 所以我们这里用 const 表示由arr指针指向的内容
	assert(arr != NULL);        不可改变 当然只是不能由arr来改变
	int count = 0;
	while (*arr!='\0')
	{
		arr++;
		count++;
	}
	return count;
}

int main()
{
	char arr[] = "adsdffafs";
	printf("%d", my_strlen(arr));
	return 0;
}

 这里面的assert起的是一个断言的作用 因为我们也不清楚 传进来的地址是否是空地址 所以在学习了指针这一块后 对于传址操作 我们应该更加的敏感 以防出现出现错位 而断言就可以做到这一点 就相当于是一个长官对他手下的命令一样 是不能违背的 

他的头文件是


递归法:

#define _CRT_SECURE_NO_WARNINGS 1
#include 
#include 
int my_strlen(const char* arr)
{
    assert(arr);
	if (*arr == '\0')
	{
		return 0;
	}
	else
	{
		return 1 + my_strlen(arr + 1);
	}
}
int main()
{
	char arr[] = "adsdffafs";
	printf("%d",my_strlen(arr));
	return 0;
}

递归法的原理就是 我们将arr字符串向后递归直到遇到'\0'返回 0 如果不是则加一向后递归下一个字符并加1 最后,递归一次就会加1 从而返回 字符串的长度


指针运算法:

#define _CRT_SECURE_NO_WARNINGS 1
#include 
#include 
int my_strlen(const char* arr)
{
	assert(arr != NULL);
	const char* sert = arr;
	while (*arr != '\0')
	{
		arr++;
	}
	return arr - sert;
}

int main()
{
	char arr[] = "adsdffafs";
	printf("%d", my_strlen(arr));
}

要使用这种方法我们要知道指针的运算是怎样的

C语言进阶学习日志:字符串和内存函数(一)_第2张图片

指针运算中的减法减出来的 就是这个之前到被减指针中间的元素个数 了解这个特性后 就已经有思路了吧

#define _CRT_SECURE_NO_WARNINGS 1
#include 
#include 
int my_strlen(const char* arr)
{
	assert(arr != NULL);
	const char* sert = arr;
	while (*arr != '\0')
	{
		arr++;
	}
	return arr - sert;
}

int main()
{
	char arr[] = "adsdffafs";
	printf("%d", my_strlen(arr));
}

这样我们模拟strlen函数的三种方法就说完了

最后有个这样的问题

 #include 

 int main()
 {
     if( strlen("abc") - strlen("abcdef") > 0)
     {
         printf(">0"); 
     }
     else
     {
         printf("<=0");
     }
 }

大家觉得这个代码最后打印出来的是什么内 注意strlen函数的返回值是一个无符号数哦

如果给他们强制类型转换成 int 会是什么结果内

长度不受限制的字符串函数 

  1. strcpy
  2. strcat
  3. strcmp

strcpy函数:

在使用 函数之前要注意这几点

① 源字符串必须以'\0'结尾

② 目标空间必须可变 且足够大来储存拷贝过来的字符串

③ 会将源字符串的'\0'拷进去

C语言进阶学习日志:字符串和内存函数(一)_第3张图片


#include 

int main()
{
   char arr[]={1,2,3,4,5,6};//这样的源字符串就是不可取的
   
   char ret[]="1234567";
   char tmp[]="xxx";
   strcpy(tmp,ret);//像这样也是不可以的 因为目标空间不够

   char ch1[]="1234567";
   char ch2[]="xxx";
   printf("%s", strcpy(ch1,ch2));//链式访问
   //打印出来是xxx因为'\0'也被拷进去了
}
#define _CRT_SECURE_NO_WARNINGS 1
#include 
#include 

char* my_strcpy(char* arr, const char* tmp)
{
	char* ret = arr;
	assert(arr && tmp);
	while (*arr++ = *tmp++)//每次赋值完后加加
	{
		;
	}
	return ret;
}

int main()
{
	char arr1[30] = "asd";
	char arr2[] = "xxxxxx";
	printf("%s",my_strcpy(arr1, arr2));
}

strcat函数:

这个是一个字符串连接函数 就好像"hello" "world" 连接后"helloworld"

其实模拟实现的方法很简单就是找到目标字符串末尾的 ‘\0’ 找到后 重复上面strcpy的步骤

C语言进阶学习日志:字符串和内存函数(一)_第4张图片


① 源字符串也是要有 '\0' 的结尾 不然函数不知道从哪里开始追加

②目标空间必须可修改 且足够大来存储追加的字符串

#include 
#include 

char* my_strcat(char* tmp, const char* ret)
{
	assert(tmp && ret);
	char* p = tmp;
	while (*tmp != '\0')  不能写成while(*tmp++) 因为在判断到'\0'的时候虽然会跳出循环 但在这之前
	{                     会++一次 跳过末尾的'\0'
		tmp++;
	}
	while (*tmp++ = *ret++)
	{
		;
	}
	return p;
}

int main()
{
	char arr1[30] = "xxxxxx";
	char arr2[] = "abcd";
	printf("%s",my_strcat(arr1, arr2));
}

strcmp函数:

比较字符串中每个对应位置的字符的ASCLL码值 

C语言进阶学习日志:字符串和内存函数(一)_第5张图片

要注意的是strcmp函数返回的值是 小于0 等于0 大于0 所以我们在模拟的时候 要注意返回的值不一定就是1,0,-1

#define _CRT_SECURE_NO_WARNINGS 1
#include 
#include 

int my_strcmp(const char* tmp, const char* ret)
{
	assert(tmp && ret);
	while (*tmp == *ret)
	{
		if (*tmp == '\0')
		{
			return 0;
		}
		tmp++;
		ret++;
	}
	return *tmp - *ret;
}

int main()
{
	char arr1[] = "abc";
	char arr2[] = "abc";
	printf("%d",my_strcmp(arr1, arr2));
}

长度受限制的字符串函数


strncpy函数:

C语言进阶学习日志:字符串和内存函数(一)_第6张图片

第三个参数 size_t count 是要拷贝的元素个数 

要注意的是当我们拷贝的元素个数大于我们被拷贝的字符串的长度时他会自动往不够的那片空间添加'\0'

#include 

int main()
{
  char string1[]="xxxxxxxxxxx";
  char string2[]="123";
  strncpy(string1,string2,5);
  这时string1就会变成123'\0'\0'xxxxxxxxx;
}

同时strncpy函数也更加安全一些 因为这样可以避免你拷进去的字符串过长 以至于目标的字符串不足以存储拷贝进来的字符串

#define _CRT_SECURE_NO_WARNINGS 1
#include 
#include 

char* my_strncpy(char* rest, const char* tmp, unsigned int num)
{
	assert(rest && tmp);
	char* ret = rest;
	while (num)
	{
		if (*tmp == '\0')
		{
			*rest++ = *tmp;
			num--;
		}
		while (*tmp)
		{
			*rest++ = *tmp++;
			num--;
			if (!num) return ret;
		}
	}
	return ret;
}

int main()
{
	char rest[] = "xxxxxxxxxx";
	char tmp[] = "123";
	printf("%s",my_strncpy(rest, tmp, 5));//这里打印出来也就是123 前面已经给大家说过了
}

这个是库函数里的写法

char * __cdecl strncpy (
        char * dest,
        const char * source,
        size_t count
        )
{
        char *start = dest;

        while (count && (*dest++ = *source++) != '\0')    /* copy string */
                count--;

        if (count)                              /* pad out with zeroes */
                while (--count)
                        *dest++ = '\0';

        return(start);
}

strncat函数:

这个是往目标字符串追加 size_t num 的字符个数的字符串

C语言进阶学习日志:字符串和内存函数(一)_第7张图片

要注意的点除了前面说的 追加字符串要足够长 可变以外

这个函数还有一个要注意的点 在指定链接的字符个数后 如果我们原函数的长度大于字符串的长度 就好像这样 "hello"world"这时如果链接的字符个数 num为3 那我们链接wor后

后面应该是什么样呢

#include 

int main()
{
  char string1[]="hello";
  char string2[]="world";
  strncpy(string1,string2,3);
  这样操作后string1的结果是什么内 留给大家思考一下

}

 提示 我们既然时链接两个字符串 那我们链接后肯定也是一个字符串 即使链接的元素个数中没有'\0' 大家想想字符串不可少的是什么 就知道答案了

#define _CRT_SECURE_NO_WARNINGS 1
#include 
#include 

char* my_strncat(char* rest,const char* tmp,unsigned int num)
{
	assert(rest && tmp);
	char* ret = rest;
	while (*rest)
	{
		rest++;
	}
	while (*tmp)
	{
		*rest++ = *tmp++;
		num--;
		if (!num)break;
	}
	*rest = '\0';
	return ret;
}

int main()
{
	char string1[20] = "hello";
	char string2[] = "world";
	printf("%s",my_strncat(string1, string2, 4));
}

这个是库函数里的写法

char * __cdecl strncat (
        char * front,
        const char * back,
        size_t count
        )
{
        char *start = front;

        while (*front++)
                ;
        front--;

        while (count--)
                if ((*front++ = *back++) == 0)
                        return(start);

        *front = '\0';
        return(start);
}

strncmp函数:

还是一样的道理 这个也是指定了比较的元素的个数

C语言进阶学习日志:字符串和内存函数(一)_第8张图片

#define _CRT_SECURE_NO_WARNINGS 1
#include 
#include 

int my_strncmp(const char* rest, const char* tmp, unsigned int num)
{
	assert(rest && tmp);
	while (*rest == *tmp)
	{
		num--;
		if (!num ||(* rest == '\0'))break;
		rest++;
		tmp++;
	}
	return *rest - *tmp;
}
int main()
{
	char string1[] = "abczefg";
	char string2[] = "abcqqq";
	printf("%d",my_strncmp(string1, string2, 4));
}

这个是库函数的里的写法 

int __cdecl strncmp
(
    const char *first,
    const char *last,
    size_t      count
)
{
    size_t x = 0;

    if (!count)
    {
        return 0;
    }

    /*
     * This explicit guard needed to deal correctly with boundary
     * cases: strings shorter than 4 bytes and strings longer than
     * UINT_MAX-4 bytes .
     */
    if( count >= 4 )
    {
        /* unroll by four */
        for (; x < count-4; x+=4)
        {
            first+=4;
            last +=4;

            if (*(first-4) == 0 || *(first-4) != *(last-4))
            {
                return(*(unsigned char *)(first-4) - *(unsigned char *)(last-4));
            }

            if (*(first-3) == 0 || *(first-3) != *(last-3))
            {
                return(*(unsigned char *)(first-3) - *(unsigned char *)(last-3));
            }

            if (*(first-2) == 0 || *(first-2) != *(last-2))
            {
                return(*(unsigned char *)(first-2) - *(unsigned char *)(last-2));
            }

            if (*(first-1) == 0 || *(first-1) != *(last-1))
            {
                return(*(unsigned char *)(first-1) - *(unsigned char *)(last-1));
            }
        }
    }

    /* residual loop */
    for (; x < count; x++)
    {
        if (*first == 0 || *first != *last)
        {
            return(*(unsigned char *)first - *(unsigned char *)last);
        }
        first+=1;
        last+=1;
    }

    return 0;
}

strstr函数:

这是一个在字符串中寻找目标字符串的函数 如果找到则返回被寻找字符串首元素在目标字符串的地址

C语言进阶学习日志:字符串和内存函数(一)_第9张图片

#include 

int main()
{
  char string1[]="abbcqwer";
  char string2[]="bbc"
  printf("%s",strstr(string1,string2));
  打印出来的就是 bbcqwer

 
}

 我们来理一下模拟这个函数的思路  首先 我们是要在目标字符串寻找 肯定需要几个指针来帮我们判断是不是我们要寻找的字符串

C语言进阶学习日志:字符串和内存函数(一)_第10张图片

#include 
#include 

char* my_strstr(const char* tmp, const char* ret)
{
	assert(tmp && ret);
	const char* suter = tmp;//被寻找字符串的枚举地址
	const char* cstr = tmp;//被寻找字符串的起始地址
	const char* sup = ret;
	if (*ret == '\0')
	{
		return tmp;
	}
	while (*cstr)
	{
		suter = cstr;
		sup = ret;
		while (*suter&&*suter == *sup)//当是这种"afbbc" "bbc" 最后'\0'和'\0'相同会一直循环随 
                                         会跳出但结果就会出问题
		{                              //所以suter和sup指向不能为'\0'
			suter++;
			sup++;
		}                       //上面那种循环我的if语句sup要放前面 我是找到了的而且两个都 
                                 是'\0'要把bbc打印出来才行
		if (*sup == '\0')      //像是这种"afbbcfgh" "bbc" 这时发现不相等我的sup会指向'\0'就 
                                  可以结束了
		{
			return (char*)cstr;
		}
		if (*suter == '\0')//"afbbbc" "bbcd"这种就是我往后寻找suter已经到'\0'了就可以结束了
		{                   //虽然没有也行 但是就要等到cstr到'\0'才停止 这样就可以早点结束
			return NULL;
		}
		cstr++;
	}
	return NULL;
}

int main()
{
	char arr1[] = "afbbbcdfgh";
	char arr2[] = "bbc";
	printf("%s",my_strstr(arr1, arr2));
}

 我们把那个我所说的s1s2的赋值放在循环的前面 只要不相等我们跳出循环就会来到开始的位置 s1s2自然就恢复开始的位置了


strtok函数:

C语言进阶学习日志:字符串和内存函数(一)_第11张图片

这个函数的第二个参数我们这里简称 s2 是一个字符串 里面存储的是多个分隔符的集合

第一个参数指定一个字符串,它包含了0个或多个由s2字符串中一个或者多个分隔符分割的标记

strtok函数找到s1中的下一个标记 并将其'\0'结尾  返回一个指向该标记的指针

strtok函数的第一个参数不为NULL 函数将找到str的第一个标记 strtok函数将保存他在字符串中的位置

strtok函数的第一个参数为NULL 函数将在同一个字符串中被保存的位置开始查找下一个标记

不存在更多标记则返回NULL

#include 

int main()
{
   const char* p="@.";
   char arr[] = "[email protected]";
   char buf[] = { 0 };
   strcpy(arr,buf);  //因为我们的这个函数会改变原本字符串的内容
                       所以要先拷贝一份
   char* str=strtok(arr,p);//luck++
   printf("%s",str);
   
   str=strtok(arr,p);//8023
   printf("%s",str);
   
   str=strtok(arr,p);//sg
   printf("%s",str);
   //strtok 这里返回NULL
}

但是这种写法是不可取的 这样才是正确的用法

const char* p="@.";
char *arr="[email protected]";

for(char* str=strtok(arr,p);str!=NULL;str=strtok(NULL,p))
{
   printf("%s\n",str);
}

strerror函数:

这是一个错误信息的解释 报告函数 返回错误信息字符串的首地址

c语言中:

错误码-错误信息

    0     -“No error”

C语言进阶学习日志:字符串和内存函数(一)_第12张图片

 其中errno一般与strerror配合着使用

当库函数使用的时候,发生错误会把errno这个全局的错误变量设置为本次执行库函数产生的错误码

errno是c语言提供的一个全局变量,可以直接使用,放在errno.h文件中

字符分类函数:

C语言进阶学习日志:字符串和内存函数(一)_第13张图片

字符转换函数:

int tolower(int c);将字符转换成小写

int toupper(int c); 将字符转换成大写


到这里字符相关的函数就 弄完了 祝大家都能收到自己心仪的大厂的offer!!!!

你可能感兴趣的:(c语言,学习,开发语言)