Error[8]: Undefined offset: 80, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

目录

C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。


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

1.1 strlen

1.2 三种方式模拟实现strlen库函数

1.3 strcpy 字符串拷贝

1.4 模拟实现strcpy

strcpy函数返回的是目标空间的起始地址

strcpy函数的返回类型的设置是为了实现链式访问

1.5 strcat 字符串追加

1.6 模拟实现strcat

1.7 strcmp 比较字符串

1.8 模拟实现strcmp

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

2.1 strncpy

2.2 strncat

2.3 strncmp

2.4 strstr  在一个字符串中找另一个字符串

2.5 模拟实现strstr

2.6 strtok

2.7 strerror  perror  返回错误码,所对应的错误信息

2.8 字符分类函数

3. 内存 *** 作函数

3.1 memcpy 内存空间数据拷贝

3.2 模拟实现memcpy

 在同一块内存拷贝,目标和源头数据空间有交叉,我们应该用memmove

3.3 memmove  可以实现重叠内存拷贝

3.4  模拟实现memmove

3.5 memcpy  内存字节对应比较

3.6  memset  以字节为单位进行内存设置


C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。


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

1.1 strlen

size_t strlen ( const char * str );//函数原型,注意函数的返回值为size_t

字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' )。


char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };//无法用strlen求字符串长度

char arr[10] = { 'a', 'b', 'c', 'd', 'e', 'f' };//限定长度,可以求

 下面结果是什么?

 if ((int)strlen("abc") - (int)strlen("qwerty") > 0)
	{
		printf(">\n");
	}
	else
	{
		printf("<=\n");
	}

答案:>,两个无符号数相减结果是正数


1.2 三种方式模拟实现strlen库函数

通常写法

 #include 
 #include 

size_t my_strlen(const char* str)
{
	int count = 0;//统计字符的个数
    assert(str);
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}
 
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

递归

 #include 
 #include 

//my_strlen("abcdef")
//1+my_strlen("bcdef")
//1+1+my_strlen("cdef")
//1+1+1+ my_strlen("def")
//1+1+1+1+ my_strlen("ef")
//1 + 1 + 1 + 1 +1+my_strlen("f")
//1 + 1 + 1 + 1 + 1 + 1+ my_strlen("")
//1 + 1 + 1 + 1 + 1 + 1 + 0 = 6
 
size_t my_strlen(const char* str)
{
    assert(str);
	if (*str != '\0')
		return 1 + my_strlen(str+1);
	else
		return 0;
}
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

指针-指针方法

 #include 
 #include 


size_t my_strlen(const char* arr,int sz)//指针-指针
{
    assert(arr);
    char* right = arr + sz - 1;
    return right - arr;
}
 
int main()
{
    char arr[] = "hello";
    int sz = sizeof(arr) / sizeof(arr[0]);
    int len=my_strlen(arr,sz);
    printf("%d", len);
    return 0;
}

1.3 strcpy 字符串拷贝

字符串拷贝,把源字符串拷贝放到目标空间字符串中,其中需要注意的事项

1.源字符串必须以 '\0' 结束。


char arr1[20] = {0};
char arr2[] = {'a','b','c'};//程序崩溃,没有char arr1[4] = "x";
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

2.会将源字符串中的 '\0' 拷贝到目标空间。


#include 
#include 
#include 

int main()
{
	
	char arr1[] = "XXXXXXXXXXXX";//arr1指向的是常量字符串,常量是不可修改的
	char arr2[] = "abcdef";
	strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

3.目标空间必须足够大,以确保能存放源字符串。


strcpy不管空间够不够,只要你敢写它就敢放,哪怕程序崩溃

char* arr1 = "qwertyuiop";//arr1指向的是常量字符串,常量是不可修改的
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

4.目标空间必须可变。


1.4 模拟实现strcpy

char* my_strcpy(char*dest, const char* src) { assert(src && dest); char* ret = dest; while(*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = { 0 }; char* arr2 = "hello bit"; printf("%s\n", my_strcpy(arr1, arr2)); return 0; } strcpy函数返回的是目标空间的起始地址 strcpy函数的返回类型的设置是为了实现链式访问
1.5 strcat 字符串追加

int main() { char arr1[10] = "hello " ; char* arr2 = "bit"; printf("%s\n", strcat(arr1, arr2)); return 0; }

int main()
{
	char arr1[20] =  "hello
1.6 模拟实现strcat
XXXXX" ; char arr2[] = "bit"; printf("%s\n", strcat(arr1, arr2)); return 0; }

注意事项:

1.源字符串必须以 '\0' 结束。


从\0位置开始追加

char* my_strcat(char* dest, char* src)
{
	assert(dest && src);
	char* ret = dest;
	//找目标空间中的
1.7 strcmp 比较字符串
while (*dest) { dest++; } //拷贝 while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = "hello"; char arr2[] = " bit"; printf("%s\n", my_strcat(arr1, arr2)); return 0; }

2.目标空间必须有足够的大,能容纳下源字符串的内容。


3.目标空间必须可修改。


strcmp函数比较的不是字符串的长度

而是比较字符串中对应位置上的字符的大小(ASCII码值),如果相同,就比较下一对儿,直到不同或者都遇到
1.8 模拟实现strcmp
2  长度受限制的字符串函数
的ASCII码值是0
2.1 strncpy

字符串自己给自己追加,如何?

我们所写的模拟函数无法完成自己追加自己,也不建议这么使用


char *strncpy( char *strDest, const char *strSource, size_t count ); int main() { char arr1[] = "abcdef"; char arr2[] = "qwewwwwww"; strncpy(arr1, arr2, 5); printf("%s\n", arr1); return 0; }

 标准规定:

第一个字符串大于第二个字符串,则返回大于0的数字

第一个字符串等于第二个字符串,则返回0

第一个字符串小于第二个字符串,则返回小于0的数字


int main() { char arr1[] = "abcdef"; char arr2[] = "qwe"; strncpy(arr1, arr2, 5);//不够默认补
2.2 strncat
printf("%s\n", arr1); return 0; }
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 arr1[] = "abcd";
	char arr2[] = "abdc";
	int ret = my_strcmp(arr1, arr2);
	if (ret >0)
	{
		printf(">\n");
	}
	else if (ret == 0)
	{
		printf("== \n");
	}
	else
	{
		printf("<\n");
	}
	printf("%d\n", ret);
	return 0;
}

int main() { char arr1[20] = "abcdef
2.3 strncmp
XXXXX"; char arr2[] = "qwe"; strncat(arr1, arr2, 3);//追加三个,还会再把int main() { char arr1[] = "abcdef"; char arr2[] = "abcdq"; int ret = strncmp(arr1, arr2, 4);//相等 printf("%d\n", ret); return 0; } 放进去 printf("%s\n", arr1); return 0; }
2.4 strstr  在一个字符串中找另一个字符串

 拷贝num个字符从源字符串到目标空间。


如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。


char * strstr ( const char *str1, const char * str2);
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "de";
	char * p=strstr(arr1, arr2);
	if (p == NULL)//strstr找不到返回NULL指针,我们需要判断
	{
		printf("找不到");
	}
	else
	{
		printf("%s ", p);
	}
	return 0;
}

2.5 模拟实现strstr


char* my_strstr(const char* str1, const char* str2) { assert(str1 && str2); const char* s1 = str1; const char* s2 = str2; const char* cur = str1; while (*cur)//cur不等于
2.6 strtok
进来 { s1 = cur;//判断失败返回cur指向的位置 s2 = str2;//判断失败回到起始位置 while (*s1 && *s2 && (*s1 == *s2))//两个字符串都被查找完,没有数据了 { s1++; s2++; } if (*s2 == 'char * strtok ( char * str, const char * sep );')//字串找到,返回记录地址 { return (char*)cur; } cur++;//匹配不成功,指向下一步 } return NULL;//找不到 } int main() { char arr1[] = "abbbcdef"; char arr2[] = "bbc"; char* ret = my_strstr(arr1, arr2); if (NULL == ret) { printf("找不到子串\n"); } else { printf("%s\n", ret); } return 0; }

 追加num个字符从源字符串到目标空间。


int main()
{
	char arr[] = "lanyangyang@landawang.cunba";
  //char arr[] = "lanyangyang
2.7 strerror  perror  返回错误码,所对应的错误信息
landawangchar * strerror ( int errnum );cunba"; strtok函数会把数组变成这样 char buf[50] = { 0 }; const char* sep = "@. "; strcpy(buf, arr); //printf("%s\n", strtok(buf, sep));//只找第一个标记 //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找 //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找 优化 char* str = NULL; for (str=strtok(buf, sep); str!=NULL; str=strtok(NULL, sep)) { printf("%s\n", str); } return 0; }


2.8 字符分类函数

函数


如果他的参数符合下列条件就返回真
iscntrl

 判断str2是不是str1的子串,如果str2在str1中出现,返回在str1中第一次出现的地址

如果没有出现,返回空指针

任何控制字符

sspace

 思路:只需要找的子串如果复杂,我们需要三个指针协助

s1指针指向str1,s2指针指向str2。


cur指针指向str1,作用是记录开始匹配的地址

如果两个字符串对应位置不相等,str1往后走

如果相等,开始匹配,我们应该记住开始匹配的str1位置,因为有可能相等,有可能不相等

如果一直到\0结束,str2就是str1子串

如果不相等,重新找到记录位置的地址,往后+1.往后走重新开始匹配,其中str2指针重新指向数组起始位置地址

空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t'或者垂直制表符'\v'

isdigit

作用:指定分隔符,让数组分段

十进制数字 0~9

 sep参数是个字符串,定义了用作分隔符的字符集合

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


strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。


(注: strtok函数会改变被 *** 作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。


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


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


如果字符串中不存在更多的标记,则返回 NULL 指针。


isxdigit


十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F
islower

 库函数在使用失败的时候,会留下错误码errno(全局变量),类似网站404错误码

strerror就是翻译错误信息

#include 
#include 
#include 
#include 

int main()
{
	printf("%s\n", strerror(0));
	printf("%s\n", strerror(1));
	printf("%s\n", strerror(2));
	printf("%s\n", strerror(3));
	int* p = (int*)malloc(INT_MAX);//想堆区申请内存的
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		perror("Malloc");//perror是打印错误信息,strerror是把错误码转换成错误信息
 //只需要写字符串,然后输出错误码对应得错误信息,更加方便
		return 1;
	}
	return 0;
}

返回错误码所对应错误信息字符串首字符地址

 


小写字母a~z int tolower ( int c ); int toupper ( int c ); int main() { char ch = 'A'; putchar(toupper(ch)); putchar(tolower(ch)); return 0; }
3. 内存 *** 作函数
3.1 memcpy 内存空间数据拷贝
void * memcpy ( void * destination, const void * source, size_t count );int main() { int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 }; int arr2[5] = { 0 }; return 0; memcpy(arr2,arr1,20);//拷贝20个字节 }
3.2 模拟实现memcpy
3.3 memmove  可以实现重叠内存拷贝
void *memmove( void *dest, const void *src, size_t count );1
3.4  模拟实现memmove
3.5 memcpy  内存字节对应比较
isupper大写字母A~Z
isalpha字母a~z或A~Z
isalnum字母或者数字,a~z,A~Z,0~9
ispunct标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph任何图形字符
sprint任何可打印字符,包括图形字符和空白字符

 

 举例:isdigit接收字符的ASCII码值,返回int类型(如果是数字字符返回非0,不是数组字符返回0)

#include 

int main()
{
int ret = isdigit('5');//5
int ret = isdigit('Q');//0
printf("%d\n", ret);

return 0;
}
char ch = 'A';

	if (ch >= 'a' && ch <= 'z')
	{

	}

这样写很麻烦,我们一个函数搞定

int ret = islower(ch);//判断是否小写,是小写字母返回非0,否则返回0,快速判断

字符转换:

int memcmp ( const void * ptr1, 
 const void * ptr2, 
 size_t num );//比较的字节个数
 int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3,4,0x11223305 };
	int ret = memcmp(arr1, arr2, 18);
	printf("%d\n", ret);

	return 0;
}


3.6  memset  以字节为单位进行内存设置
void *memset( void *dest, int c, size_t count );//目的空间,设置的字符,字符个数
 int main()
{
	int arr[] = { 0x11111111,0x22222222,3,4,5 };
	memset(arr, 6, 20);//memset是以字节为单位来初始化内存单元的
	return 0;
}

注意事项:

1.函数memcpy从source的位置开始向后复制count个字节的数据到destination的内存位置。


2.这个函数在遇到 '\0' 的时候并不会停下来。


3.如果source和destination有任何的重叠,复制的结果都是未定义的。


4.返回目的地的起始地址

[+++]

[+++]

思路:  

1. 作者实现memcpy函数的时候,作者不知道你要拷贝什么数据,类似qsort 

2.拷贝时应该根据数据类型强制类型转换,一个字节一个字节拷贝

#include 
#include 
#include 

//void* my_memcpy(void* dest, const void* src, size_t count)
//{
//	assert(src && dest);
//	while (count--)
//	{
//		*(char*)dest = *(char*)src;
//		dest = (char*)dest + 1;
//		src = (char*)src + 1;
//	}
// }

void* my_memcpy(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	while (count--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}

	return ret;
}


int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[5] = { 0 };
	my_memcpy(arr2, arr1, 20);



    int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };   
    // 1 2 1 2 3 4 5 8 9 10 期望的结果
	my_memcpy(arr1+2, arr1, 20);

	return 0;
}

但是当我们想拷贝同一块空间的数据时,数据却发生错误

 原因在于数据覆盖了我们想要拷贝的空间

 在同一块内存拷贝,目标和源头数据空间有交叉,我们应该用memmove


[+++]
[+++]

#include 

int main()
{
		int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
		memmove(arr1 + 2, arr1, 20);
	return 0;
}

[+++]

[+++]

思路:

把34567拷贝放到12345,正着放不会覆盖数据(dest

 而想把34567拷贝放到45678,正着放会覆盖数据,我们可以先把7放到8上,6放到7,5放到6...从后往前拷贝数据,这样数据就不会被覆盖

 总结:当需要拷贝的地址dest > src地址,从后向前拷贝 ;dest < src时,从前向后拷贝

当dest和src空间无交集,前后关系无所谓,我们这里默认从后向前拷贝(方便)

{
	if (dest > src)
	{
		;  //从后向前拷贝
	}
	else
	{
		;  //从前向后拷贝
	}
}

也可以换成另一种写法

if (dest > src && dest<((char*)src+count))
	{
		;//从后向前拷贝
	}
	else
	{
		;//从前向后拷贝
	}

代码思路:从前向后的代码就是memcpy模拟,从后向前,我们需要+20个字节来到dest和src末尾

void* my_memmove(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	if (dest > src)
	{
		while (count--)
		{
	*((char*)dest + count) =  *((char*)src + count);  //count=19,正好指向最后一个字节
		}
		//从后向前拷贝
	}
	else
	{
			void* ret = dest;
			while (count--)
			{
				*(char*)dest = *(char*)src;
				dest = (char*)dest + 1;
				src = (char*)src + 1;
			}
		 //从前向后拷贝
	}
	return ret;
}


	int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr1 + 2, arr1, 20);
	//my_memmove(arr1 , arr1+2, 20);
	return 0;
}

[+++]
[+++]

 

[+++]


[+++]
[+++]

[+++]

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 81, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

目录

C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。


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

1.1 strlen

1.2 三种方式模拟实现strlen库函数

1.3 strcpy 字符串拷贝

1.4 模拟实现strcpy

strcpy函数返回的是目标空间的起始地址

strcpy函数的返回类型的设置是为了实现链式访问

1.5 strcat 字符串追加

1.6 模拟实现strcat

1.7 strcmp 比较字符串

1.8 模拟实现strcmp

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

2.1 strncpy

2.2 strncat

2.3 strncmp

2.4 strstr  在一个字符串中找另一个字符串

2.5 模拟实现strstr

2.6 strtok

2.7 strerror  perror  返回错误码,所对应的错误信息

2.8 字符分类函数

3. 内存 *** 作函数

3.1 memcpy 内存空间数据拷贝

3.2 模拟实现memcpy

 在同一块内存拷贝,目标和源头数据空间有交叉,我们应该用memmove

3.3 memmove  可以实现重叠内存拷贝

3.4  模拟实现memmove

3.5 memcpy  内存字节对应比较

3.6  memset  以字节为单位进行内存设置


C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。


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

1.1 strlen

size_t strlen ( const char * str );//函数原型,注意函数的返回值为size_t

字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' )。


char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };//无法用strlen求字符串长度

char arr[10] = { 'a', 'b', 'c', 'd', 'e', 'f' };//限定长度,可以求

 下面结果是什么?

 if ((int)strlen("abc") - (int)strlen("qwerty") > 0)
	{
		printf(">\n");
	}
	else
	{
		printf("<=\n");
	}

答案:>,两个无符号数相减结果是正数


1.2 三种方式模拟实现strlen库函数

通常写法

 #include 
 #include 

size_t my_strlen(const char* str)
{
	int count = 0;//统计字符的个数
    assert(str);
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}
 
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

递归

 #include 
 #include 

//my_strlen("abcdef")
//1+my_strlen("bcdef")
//1+1+my_strlen("cdef")
//1+1+1+ my_strlen("def")
//1+1+1+1+ my_strlen("ef")
//1 + 1 + 1 + 1 +1+my_strlen("f")
//1 + 1 + 1 + 1 + 1 + 1+ my_strlen("")
//1 + 1 + 1 + 1 + 1 + 1 + 0 = 6
 
size_t my_strlen(const char* str)
{
    assert(str);
	if (*str != '\0')
		return 1 + my_strlen(str+1);
	else
		return 0;
}
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

指针-指针方法

 #include 
 #include 


size_t my_strlen(const char* arr,int sz)//指针-指针
{
    assert(arr);
    char* right = arr + sz - 1;
    return right - arr;
}
 
int main()
{
    char arr[] = "hello";
    int sz = sizeof(arr) / sizeof(arr[0]);
    int len=my_strlen(arr,sz);
    printf("%d", len);
    return 0;
}

1.3 strcpy 字符串拷贝

字符串拷贝,把源字符串拷贝放到目标空间字符串中,其中需要注意的事项

1.源字符串必须以 '\0' 结束。


char arr1[20] = {0};
char arr2[] = {'a','b','c'};//程序崩溃,没有char arr1[4] = "x";
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

2.会将源字符串中的 '\0' 拷贝到目标空间。


#include 
#include 
#include 

int main()
{
	
	char arr1[] = "XXXXXXXXXXXX";//arr1指向的是常量字符串,常量是不可修改的
	char arr2[] = "abcdef";
	strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

3.目标空间必须足够大,以确保能存放源字符串。


strcpy不管空间够不够,只要你敢写它就敢放,哪怕程序崩溃

char* arr1 = "qwertyuiop";//arr1指向的是常量字符串,常量是不可修改的
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

4.目标空间必须可变。


1.4 模拟实现strcpy

char* my_strcpy(char*dest, const char* src) { assert(src && dest); char* ret = dest; while(*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = { 0 }; char* arr2 = "hello bit"; printf("%s\n", my_strcpy(arr1, arr2)); return 0; } strcpy函数返回的是目标空间的起始地址 strcpy函数的返回类型的设置是为了实现链式访问
1.5 strcat 字符串追加

int main() { char arr1[10] = "hello " ; char* arr2 = "bit"; printf("%s\n", strcat(arr1, arr2)); return 0; }

int main()
{
	char arr1[20] =  "hello
1.6 模拟实现strcat
XXXXX" ; char arr2[] = "bit"; printf("%s\n", strcat(arr1, arr2)); return 0; }

注意事项:

1.源字符串必须以 '\0' 结束。


从\0位置开始追加

char* my_strcat(char* dest, char* src)
{
	assert(dest && src);
	char* ret = dest;
	//找目标空间中的
1.7 strcmp 比较字符串
while (*dest) { dest++; } //拷贝 while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = "hello"; char arr2[] = " bit"; printf("%s\n", my_strcat(arr1, arr2)); return 0; }

2.目标空间必须有足够的大,能容纳下源字符串的内容。


3.目标空间必须可修改。


strcmp函数比较的不是字符串的长度

而是比较字符串中对应位置上的字符的大小(ASCII码值),如果相同,就比较下一对儿,直到不同或者都遇到
1.8 模拟实现strcmp
2  长度受限制的字符串函数
的ASCII码值是0
2.1 strncpy

字符串自己给自己追加,如何?

我们所写的模拟函数无法完成自己追加自己,也不建议这么使用


char *strncpy( char *strDest, const char *strSource, size_t count ); int main() { char arr1[] = "abcdef"; char arr2[] = "qwewwwwww"; strncpy(arr1, arr2, 5); printf("%s\n", arr1); return 0; }

 标准规定:

第一个字符串大于第二个字符串,则返回大于0的数字

第一个字符串等于第二个字符串,则返回0

第一个字符串小于第二个字符串,则返回小于0的数字


int main() { char arr1[] = "abcdef"; char arr2[] = "qwe"; strncpy(arr1, arr2, 5);//不够默认补
2.2 strncat
printf("%s\n", arr1); return 0; }
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 arr1[] = "abcd";
	char arr2[] = "abdc";
	int ret = my_strcmp(arr1, arr2);
	if (ret >0)
	{
		printf(">\n");
	}
	else if (ret == 0)
	{
		printf("== \n");
	}
	else
	{
		printf("<\n");
	}
	printf("%d\n", ret);
	return 0;
}

int main() { char arr1[20] = "abcdef
2.3 strncmp
XXXXX"; char arr2[] = "qwe"; strncat(arr1, arr2, 3);//追加三个,还会再把int main() { char arr1[] = "abcdef"; char arr2[] = "abcdq"; int ret = strncmp(arr1, arr2, 4);//相等 printf("%d\n", ret); return 0; } 放进去 printf("%s\n", arr1); return 0; }
2.4 strstr  在一个字符串中找另一个字符串

 拷贝num个字符从源字符串到目标空间。


如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。


char * strstr ( const char *str1, const char * str2);
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "de";
	char * p=strstr(arr1, arr2);
	if (p == NULL)//strstr找不到返回NULL指针,我们需要判断
	{
		printf("找不到");
	}
	else
	{
		printf("%s ", p);
	}
	return 0;
}

2.5 模拟实现strstr


char* my_strstr(const char* str1, const char* str2) { assert(str1 && str2); const char* s1 = str1; const char* s2 = str2; const char* cur = str1; while (*cur)//cur不等于
2.6 strtok
进来 { s1 = cur;//判断失败返回cur指向的位置 s2 = str2;//判断失败回到起始位置 while (*s1 && *s2 && (*s1 == *s2))//两个字符串都被查找完,没有数据了 { s1++; s2++; } if (*s2 == 'char * strtok ( char * str, const char * sep );')//字串找到,返回记录地址 { return (char*)cur; } cur++;//匹配不成功,指向下一步 } return NULL;//找不到 } int main() { char arr1[] = "abbbcdef"; char arr2[] = "bbc"; char* ret = my_strstr(arr1, arr2); if (NULL == ret) { printf("找不到子串\n"); } else { printf("%s\n", ret); } return 0; }

 追加num个字符从源字符串到目标空间。


int main()
{
	char arr[] = "lanyangyang@landawang.cunba";
  //char arr[] = "lanyangyang
2.7 strerror  perror  返回错误码,所对应的错误信息
landawangchar * strerror ( int errnum );cunba"; strtok函数会把数组变成这样 char buf[50] = { 0 }; const char* sep = "@. "; strcpy(buf, arr); //printf("%s\n", strtok(buf, sep));//只找第一个标记 //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找 //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找 优化 char* str = NULL; for (str=strtok(buf, sep); str!=NULL; str=strtok(NULL, sep)) { printf("%s\n", str); } return 0; }


2.8 字符分类函数

函数


如果他的参数符合下列条件就返回真
iscntrl

 判断str2是不是str1的子串,如果str2在str1中出现,返回在str1中第一次出现的地址

如果没有出现,返回空指针

任何控制字符

sspace

 思路:只需要找的子串如果复杂,我们需要三个指针协助

s1指针指向str1,s2指针指向str2。


cur指针指向str1,作用是记录开始匹配的地址

如果两个字符串对应位置不相等,str1往后走

如果相等,开始匹配,我们应该记住开始匹配的str1位置,因为有可能相等,有可能不相等

如果一直到\0结束,str2就是str1子串

如果不相等,重新找到记录位置的地址,往后+1.往后走重新开始匹配,其中str2指针重新指向数组起始位置地址

空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t'或者垂直制表符'\v'

isdigit

作用:指定分隔符,让数组分段

十进制数字 0~9

 sep参数是个字符串,定义了用作分隔符的字符集合

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


strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。


(注: strtok函数会改变被 *** 作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。


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


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


如果字符串中不存在更多的标记,则返回 NULL 指针。


isxdigit


十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F
islower

 库函数在使用失败的时候,会留下错误码errno(全局变量),类似网站404错误码

strerror就是翻译错误信息

#include 
#include 
#include 
#include 

int main()
{
	printf("%s\n", strerror(0));
	printf("%s\n", strerror(1));
	printf("%s\n", strerror(2));
	printf("%s\n", strerror(3));
	int* p = (int*)malloc(INT_MAX);//想堆区申请内存的
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		perror("Malloc");//perror是打印错误信息,strerror是把错误码转换成错误信息
 //只需要写字符串,然后输出错误码对应得错误信息,更加方便
		return 1;
	}
	return 0;
}

返回错误码所对应错误信息字符串首字符地址

 


小写字母a~z int tolower ( int c ); int toupper ( int c ); int main() { char ch = 'A'; putchar(toupper(ch)); putchar(tolower(ch)); return 0; }
3. 内存 *** 作函数
3.1 memcpy 内存空间数据拷贝
void * memcpy ( void * destination, const void * source, size_t count );int main() { int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 }; int arr2[5] = { 0 }; return 0; memcpy(arr2,arr1,20);//拷贝20个字节 }
3.2 模拟实现memcpy
3.3 memmove  可以实现重叠内存拷贝
void *memmove( void *dest, const void *src, size_t count );1
3.4  模拟实现memmove
3.5 memcpy  内存字节对应比较
isupper大写字母A~Z
isalpha字母a~z或A~Z
isalnum字母或者数字,a~z,A~Z,0~9
ispunct标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph任何图形字符
sprint任何可打印字符,包括图形字符和空白字符

 

 举例:isdigit接收字符的ASCII码值,返回int类型(如果是数字字符返回非0,不是数组字符返回0)

#include 

int main()
{
int ret = isdigit('5');//5
int ret = isdigit('Q');//0
printf("%d\n", ret);

return 0;
}
char ch = 'A';

	if (ch >= 'a' && ch <= 'z')
	{

	}

这样写很麻烦,我们一个函数搞定

int ret = islower(ch);//判断是否小写,是小写字母返回非0,否则返回0,快速判断

字符转换:

int memcmp ( const void * ptr1, 
 const void * ptr2, 
 size_t num );//比较的字节个数
 int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3,4,0x11223305 };
	int ret = memcmp(arr1, arr2, 18);
	printf("%d\n", ret);

	return 0;
}


3.6  memset  以字节为单位进行内存设置
void *memset( void *dest, int c, size_t count );//目的空间,设置的字符,字符个数
 int main()
{
	int arr[] = { 0x11111111,0x22222222,3,4,5 };
	memset(arr, 6, 20);//memset是以字节为单位来初始化内存单元的
	return 0;
}

注意事项:

1.函数memcpy从source的位置开始向后复制count个字节的数据到destination的内存位置。


2.这个函数在遇到 '\0' 的时候并不会停下来。


3.如果source和destination有任何的重叠,复制的结果都是未定义的。


4.返回目的地的起始地址

 

[+++]

思路:  

1. 作者实现memcpy函数的时候,作者不知道你要拷贝什么数据,类似qsort 

2.拷贝时应该根据数据类型强制类型转换,一个字节一个字节拷贝

#include 
#include 
#include 

//void* my_memcpy(void* dest, const void* src, size_t count)
//{
//	assert(src && dest);
//	while (count--)
//	{
//		*(char*)dest = *(char*)src;
//		dest = (char*)dest + 1;
//		src = (char*)src + 1;
//	}
// }

void* my_memcpy(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	while (count--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}

	return ret;
}


int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[5] = { 0 };
	my_memcpy(arr2, arr1, 20);



    int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };   
    // 1 2 1 2 3 4 5 8 9 10 期望的结果
	my_memcpy(arr1+2, arr1, 20);

	return 0;
}

但是当我们想拷贝同一块空间的数据时,数据却发生错误

 原因在于数据覆盖了我们想要拷贝的空间

 在同一块内存拷贝,目标和源头数据空间有交叉,我们应该用memmove


[+++]
[+++]

#include 

int main()
{
		int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
		memmove(arr1 + 2, arr1, 20);
	return 0;
}

[+++]

[+++]

思路:

把34567拷贝放到12345,正着放不会覆盖数据(dest

 而想把34567拷贝放到45678,正着放会覆盖数据,我们可以先把7放到8上,6放到7,5放到6...从后往前拷贝数据,这样数据就不会被覆盖

 总结:当需要拷贝的地址dest > src地址,从后向前拷贝 ;dest < src时,从前向后拷贝

当dest和src空间无交集,前后关系无所谓,我们这里默认从后向前拷贝(方便)

{
	if (dest > src)
	{
		;  //从后向前拷贝
	}
	else
	{
		;  //从前向后拷贝
	}
}

也可以换成另一种写法

if (dest > src && dest<((char*)src+count))
	{
		;//从后向前拷贝
	}
	else
	{
		;//从前向后拷贝
	}

代码思路:从前向后的代码就是memcpy模拟,从后向前,我们需要+20个字节来到dest和src末尾

void* my_memmove(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	if (dest > src)
	{
		while (count--)
		{
	*((char*)dest + count) =  *((char*)src + count);  //count=19,正好指向最后一个字节
		}
		//从后向前拷贝
	}
	else
	{
			void* ret = dest;
			while (count--)
			{
				*(char*)dest = *(char*)src;
				dest = (char*)dest + 1;
				src = (char*)src + 1;
			}
		 //从前向后拷贝
	}
	return ret;
}


	int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr1 + 2, arr1, 20);
	//my_memmove(arr1 , arr1+2, 20);
	return 0;
}

[+++]
[+++]

 

[+++]


[+++]
[+++]

[+++]

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 82, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

目录

C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。


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

1.1 strlen

1.2 三种方式模拟实现strlen库函数

1.3 strcpy 字符串拷贝

1.4 模拟实现strcpy

strcpy函数返回的是目标空间的起始地址

strcpy函数的返回类型的设置是为了实现链式访问

1.5 strcat 字符串追加

1.6 模拟实现strcat

1.7 strcmp 比较字符串

1.8 模拟实现strcmp

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

2.1 strncpy

2.2 strncat

2.3 strncmp

2.4 strstr  在一个字符串中找另一个字符串

2.5 模拟实现strstr

2.6 strtok

2.7 strerror  perror  返回错误码,所对应的错误信息

2.8 字符分类函数

3. 内存 *** 作函数

3.1 memcpy 内存空间数据拷贝

3.2 模拟实现memcpy

 在同一块内存拷贝,目标和源头数据空间有交叉,我们应该用memmove

3.3 memmove  可以实现重叠内存拷贝

3.4  模拟实现memmove

3.5 memcpy  内存字节对应比较

3.6  memset  以字节为单位进行内存设置


C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。


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

1.1 strlen

size_t strlen ( const char * str );//函数原型,注意函数的返回值为size_t

字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' )。


char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };//无法用strlen求字符串长度

char arr[10] = { 'a', 'b', 'c', 'd', 'e', 'f' };//限定长度,可以求

 下面结果是什么?

 if ((int)strlen("abc") - (int)strlen("qwerty") > 0)
	{
		printf(">\n");
	}
	else
	{
		printf("<=\n");
	}

答案:>,两个无符号数相减结果是正数


1.2 三种方式模拟实现strlen库函数

通常写法

 #include 
 #include 

size_t my_strlen(const char* str)
{
	int count = 0;//统计字符的个数
    assert(str);
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}
 
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

递归

 #include 
 #include 

//my_strlen("abcdef")
//1+my_strlen("bcdef")
//1+1+my_strlen("cdef")
//1+1+1+ my_strlen("def")
//1+1+1+1+ my_strlen("ef")
//1 + 1 + 1 + 1 +1+my_strlen("f")
//1 + 1 + 1 + 1 + 1 + 1+ my_strlen("")
//1 + 1 + 1 + 1 + 1 + 1 + 0 = 6
 
size_t my_strlen(const char* str)
{
    assert(str);
	if (*str != '\0')
		return 1 + my_strlen(str+1);
	else
		return 0;
}
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

指针-指针方法

 #include 
 #include 


size_t my_strlen(const char* arr,int sz)//指针-指针
{
    assert(arr);
    char* right = arr + sz - 1;
    return right - arr;
}
 
int main()
{
    char arr[] = "hello";
    int sz = sizeof(arr) / sizeof(arr[0]);
    int len=my_strlen(arr,sz);
    printf("%d", len);
    return 0;
}

1.3 strcpy 字符串拷贝

字符串拷贝,把源字符串拷贝放到目标空间字符串中,其中需要注意的事项

1.源字符串必须以 '\0' 结束。


char arr1[20] = {0};
char arr2[] = {'a','b','c'};//程序崩溃,没有char arr1[4] = "x";
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

2.会将源字符串中的 '\0' 拷贝到目标空间。


#include 
#include 
#include 

int main()
{
	
	char arr1[] = "XXXXXXXXXXXX";//arr1指向的是常量字符串,常量是不可修改的
	char arr2[] = "abcdef";
	strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

3.目标空间必须足够大,以确保能存放源字符串。


strcpy不管空间够不够,只要你敢写它就敢放,哪怕程序崩溃

char* arr1 = "qwertyuiop";//arr1指向的是常量字符串,常量是不可修改的
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

4.目标空间必须可变。


1.4 模拟实现strcpy

char* my_strcpy(char*dest, const char* src) { assert(src && dest); char* ret = dest; while(*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = { 0 }; char* arr2 = "hello bit"; printf("%s\n", my_strcpy(arr1, arr2)); return 0; } strcpy函数返回的是目标空间的起始地址 strcpy函数的返回类型的设置是为了实现链式访问
1.5 strcat 字符串追加

int main() { char arr1[10] = "hello " ; char* arr2 = "bit"; printf("%s\n", strcat(arr1, arr2)); return 0; }

int main()
{
	char arr1[20] =  "hello
1.6 模拟实现strcat
XXXXX" ; char arr2[] = "bit"; printf("%s\n", strcat(arr1, arr2)); return 0; }

注意事项:

1.源字符串必须以 '\0' 结束。


从\0位置开始追加

char* my_strcat(char* dest, char* src)
{
	assert(dest && src);
	char* ret = dest;
	//找目标空间中的
1.7 strcmp 比较字符串
while (*dest) { dest++; } //拷贝 while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = "hello"; char arr2[] = " bit"; printf("%s\n", my_strcat(arr1, arr2)); return 0; }

2.目标空间必须有足够的大,能容纳下源字符串的内容。


3.目标空间必须可修改。


strcmp函数比较的不是字符串的长度

而是比较字符串中对应位置上的字符的大小(ASCII码值),如果相同,就比较下一对儿,直到不同或者都遇到
1.8 模拟实现strcmp
2  长度受限制的字符串函数
的ASCII码值是0
2.1 strncpy

字符串自己给自己追加,如何?

我们所写的模拟函数无法完成自己追加自己,也不建议这么使用


char *strncpy( char *strDest, const char *strSource, size_t count ); int main() { char arr1[] = "abcdef"; char arr2[] = "qwewwwwww"; strncpy(arr1, arr2, 5); printf("%s\n", arr1); return 0; }

 标准规定:

第一个字符串大于第二个字符串,则返回大于0的数字

第一个字符串等于第二个字符串,则返回0

第一个字符串小于第二个字符串,则返回小于0的数字


int main() { char arr1[] = "abcdef"; char arr2[] = "qwe"; strncpy(arr1, arr2, 5);//不够默认补
2.2 strncat
printf("%s\n", arr1); return 0; }
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 arr1[] = "abcd";
	char arr2[] = "abdc";
	int ret = my_strcmp(arr1, arr2);
	if (ret >0)
	{
		printf(">\n");
	}
	else if (ret == 0)
	{
		printf("== \n");
	}
	else
	{
		printf("<\n");
	}
	printf("%d\n", ret);
	return 0;
}

int main() { char arr1[20] = "abcdef
2.3 strncmp
XXXXX"; char arr2[] = "qwe"; strncat(arr1, arr2, 3);//追加三个,还会再把int main() { char arr1[] = "abcdef"; char arr2[] = "abcdq"; int ret = strncmp(arr1, arr2, 4);//相等 printf("%d\n", ret); return 0; } 放进去 printf("%s\n", arr1); return 0; }
2.4 strstr  在一个字符串中找另一个字符串

 拷贝num个字符从源字符串到目标空间。


如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。


char * strstr ( const char *str1, const char * str2);
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "de";
	char * p=strstr(arr1, arr2);
	if (p == NULL)//strstr找不到返回NULL指针,我们需要判断
	{
		printf("找不到");
	}
	else
	{
		printf("%s ", p);
	}
	return 0;
}

2.5 模拟实现strstr


char* my_strstr(const char* str1, const char* str2) { assert(str1 && str2); const char* s1 = str1; const char* s2 = str2; const char* cur = str1; while (*cur)//cur不等于
2.6 strtok
进来 { s1 = cur;//判断失败返回cur指向的位置 s2 = str2;//判断失败回到起始位置 while (*s1 && *s2 && (*s1 == *s2))//两个字符串都被查找完,没有数据了 { s1++; s2++; } if (*s2 == 'char * strtok ( char * str, const char * sep );')//字串找到,返回记录地址 { return (char*)cur; } cur++;//匹配不成功,指向下一步 } return NULL;//找不到 } int main() { char arr1[] = "abbbcdef"; char arr2[] = "bbc"; char* ret = my_strstr(arr1, arr2); if (NULL == ret) { printf("找不到子串\n"); } else { printf("%s\n", ret); } return 0; }

 追加num个字符从源字符串到目标空间。


int main()
{
	char arr[] = "lanyangyang@landawang.cunba";
  //char arr[] = "lanyangyang
2.7 strerror  perror  返回错误码,所对应的错误信息
landawangchar * strerror ( int errnum );cunba"; strtok函数会把数组变成这样 char buf[50] = { 0 }; const char* sep = "@. "; strcpy(buf, arr); //printf("%s\n", strtok(buf, sep));//只找第一个标记 //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找 //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找 优化 char* str = NULL; for (str=strtok(buf, sep); str!=NULL; str=strtok(NULL, sep)) { printf("%s\n", str); } return 0; }


2.8 字符分类函数

函数


如果他的参数符合下列条件就返回真
iscntrl

 判断str2是不是str1的子串,如果str2在str1中出现,返回在str1中第一次出现的地址

如果没有出现,返回空指针

任何控制字符

sspace

 思路:只需要找的子串如果复杂,我们需要三个指针协助

s1指针指向str1,s2指针指向str2。


cur指针指向str1,作用是记录开始匹配的地址

如果两个字符串对应位置不相等,str1往后走

如果相等,开始匹配,我们应该记住开始匹配的str1位置,因为有可能相等,有可能不相等

如果一直到\0结束,str2就是str1子串

如果不相等,重新找到记录位置的地址,往后+1.往后走重新开始匹配,其中str2指针重新指向数组起始位置地址

空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t'或者垂直制表符'\v'

isdigit

作用:指定分隔符,让数组分段

十进制数字 0~9

 sep参数是个字符串,定义了用作分隔符的字符集合

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


strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。


(注: strtok函数会改变被 *** 作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。


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


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


如果字符串中不存在更多的标记,则返回 NULL 指针。


isxdigit


十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F
islower

 库函数在使用失败的时候,会留下错误码errno(全局变量),类似网站404错误码

strerror就是翻译错误信息

#include 
#include 
#include 
#include 

int main()
{
	printf("%s\n", strerror(0));
	printf("%s\n", strerror(1));
	printf("%s\n", strerror(2));
	printf("%s\n", strerror(3));
	int* p = (int*)malloc(INT_MAX);//想堆区申请内存的
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		perror("Malloc");//perror是打印错误信息,strerror是把错误码转换成错误信息
 //只需要写字符串,然后输出错误码对应得错误信息,更加方便
		return 1;
	}
	return 0;
}

返回错误码所对应错误信息字符串首字符地址

 


小写字母a~z int tolower ( int c ); int toupper ( int c ); int main() { char ch = 'A'; putchar(toupper(ch)); putchar(tolower(ch)); return 0; }
3. 内存 *** 作函数
3.1 memcpy 内存空间数据拷贝
void * memcpy ( void * destination, const void * source, size_t count );int main() { int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 }; int arr2[5] = { 0 }; return 0; memcpy(arr2,arr1,20);//拷贝20个字节 }
3.2 模拟实现memcpy
3.3 memmove  可以实现重叠内存拷贝
void *memmove( void *dest, const void *src, size_t count );1
3.4  模拟实现memmove
3.5 memcpy  内存字节对应比较
isupper大写字母A~Z
isalpha字母a~z或A~Z
isalnum字母或者数字,a~z,A~Z,0~9
ispunct标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph任何图形字符
sprint任何可打印字符,包括图形字符和空白字符

 

 举例:isdigit接收字符的ASCII码值,返回int类型(如果是数字字符返回非0,不是数组字符返回0)

#include 

int main()
{
int ret = isdigit('5');//5
int ret = isdigit('Q');//0
printf("%d\n", ret);

return 0;
}
char ch = 'A';

	if (ch >= 'a' && ch <= 'z')
	{

	}

这样写很麻烦,我们一个函数搞定

int ret = islower(ch);//判断是否小写,是小写字母返回非0,否则返回0,快速判断

字符转换:

int memcmp ( const void * ptr1, 
 const void * ptr2, 
 size_t num );//比较的字节个数
 int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3,4,0x11223305 };
	int ret = memcmp(arr1, arr2, 18);
	printf("%d\n", ret);

	return 0;
}


3.6  memset  以字节为单位进行内存设置
void *memset( void *dest, int c, size_t count );//目的空间,设置的字符,字符个数
 int main()
{
	int arr[] = { 0x11111111,0x22222222,3,4,5 };
	memset(arr, 6, 20);//memset是以字节为单位来初始化内存单元的
	return 0;
}

注意事项:

1.函数memcpy从source的位置开始向后复制count个字节的数据到destination的内存位置。


2.这个函数在遇到 '\0' 的时候并不会停下来。


3.如果source和destination有任何的重叠,复制的结果都是未定义的。


4.返回目的地的起始地址

 

思路:  

1. 作者实现memcpy函数的时候,作者不知道你要拷贝什么数据,类似qsort 

2.拷贝时应该根据数据类型强制类型转换,一个字节一个字节拷贝

#include 
#include 
#include 

//void* my_memcpy(void* dest, const void* src, size_t count)
//{
//	assert(src && dest);
//	while (count--)
//	{
//		*(char*)dest = *(char*)src;
//		dest = (char*)dest + 1;
//		src = (char*)src + 1;
//	}
// }

void* my_memcpy(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	while (count--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}

	return ret;
}


int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[5] = { 0 };
	my_memcpy(arr2, arr1, 20);



    int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };   
    // 1 2 1 2 3 4 5 8 9 10 期望的结果
	my_memcpy(arr1+2, arr1, 20);

	return 0;
}

但是当我们想拷贝同一块空间的数据时,数据却发生错误

 原因在于数据覆盖了我们想要拷贝的空间

 在同一块内存拷贝,目标和源头数据空间有交叉,我们应该用memmove


[+++]
[+++]

#include 

int main()
{
		int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
		memmove(arr1 + 2, arr1, 20);
	return 0;
}

[+++]

[+++]

思路:

把34567拷贝放到12345,正着放不会覆盖数据(dest

 而想把34567拷贝放到45678,正着放会覆盖数据,我们可以先把7放到8上,6放到7,5放到6...从后往前拷贝数据,这样数据就不会被覆盖

 总结:当需要拷贝的地址dest > src地址,从后向前拷贝 ;dest < src时,从前向后拷贝

当dest和src空间无交集,前后关系无所谓,我们这里默认从后向前拷贝(方便)

{
	if (dest > src)
	{
		;  //从后向前拷贝
	}
	else
	{
		;  //从前向后拷贝
	}
}

也可以换成另一种写法

if (dest > src && dest<((char*)src+count))
	{
		;//从后向前拷贝
	}
	else
	{
		;//从前向后拷贝
	}

代码思路:从前向后的代码就是memcpy模拟,从后向前,我们需要+20个字节来到dest和src末尾

void* my_memmove(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	if (dest > src)
	{
		while (count--)
		{
	*((char*)dest + count) =  *((char*)src + count);  //count=19,正好指向最后一个字节
		}
		//从后向前拷贝
	}
	else
	{
			void* ret = dest;
			while (count--)
			{
				*(char*)dest = *(char*)src;
				dest = (char*)dest + 1;
				src = (char*)src + 1;
			}
		 //从前向后拷贝
	}
	return ret;
}


	int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr1 + 2, arr1, 20);
	//my_memmove(arr1 , arr1+2, 20);
	return 0;
}

[+++]
[+++]

 

[+++]


[+++]
[+++]

[+++]

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 83, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

目录

C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。


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

1.1 strlen

1.2 三种方式模拟实现strlen库函数

1.3 strcpy 字符串拷贝

1.4 模拟实现strcpy

strcpy函数返回的是目标空间的起始地址

strcpy函数的返回类型的设置是为了实现链式访问

1.5 strcat 字符串追加

1.6 模拟实现strcat

1.7 strcmp 比较字符串

1.8 模拟实现strcmp

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

2.1 strncpy

2.2 strncat

2.3 strncmp

2.4 strstr  在一个字符串中找另一个字符串

2.5 模拟实现strstr

2.6 strtok

2.7 strerror  perror  返回错误码,所对应的错误信息

2.8 字符分类函数

3. 内存 *** 作函数

3.1 memcpy 内存空间数据拷贝

3.2 模拟实现memcpy

 在同一块内存拷贝,目标和源头数据空间有交叉,我们应该用memmove

3.3 memmove  可以实现重叠内存拷贝

3.4  模拟实现memmove

3.5 memcpy  内存字节对应比较

3.6  memset  以字节为单位进行内存设置


C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。


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

1.1 strlen

size_t strlen ( const char * str );//函数原型,注意函数的返回值为size_t

字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' )。


char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };//无法用strlen求字符串长度

char arr[10] = { 'a', 'b', 'c', 'd', 'e', 'f' };//限定长度,可以求

 下面结果是什么?

 if ((int)strlen("abc") - (int)strlen("qwerty") > 0)
	{
		printf(">\n");
	}
	else
	{
		printf("<=\n");
	}

答案:>,两个无符号数相减结果是正数


1.2 三种方式模拟实现strlen库函数

通常写法

 #include 
 #include 

size_t my_strlen(const char* str)
{
	int count = 0;//统计字符的个数
    assert(str);
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}
 
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

递归

 #include 
 #include 

//my_strlen("abcdef")
//1+my_strlen("bcdef")
//1+1+my_strlen("cdef")
//1+1+1+ my_strlen("def")
//1+1+1+1+ my_strlen("ef")
//1 + 1 + 1 + 1 +1+my_strlen("f")
//1 + 1 + 1 + 1 + 1 + 1+ my_strlen("")
//1 + 1 + 1 + 1 + 1 + 1 + 0 = 6
 
size_t my_strlen(const char* str)
{
    assert(str);
	if (*str != '\0')
		return 1 + my_strlen(str+1);
	else
		return 0;
}
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

指针-指针方法

 #include 
 #include 


size_t my_strlen(const char* arr,int sz)//指针-指针
{
    assert(arr);
    char* right = arr + sz - 1;
    return right - arr;
}
 
int main()
{
    char arr[] = "hello";
    int sz = sizeof(arr) / sizeof(arr[0]);
    int len=my_strlen(arr,sz);
    printf("%d", len);
    return 0;
}

1.3 strcpy 字符串拷贝

字符串拷贝,把源字符串拷贝放到目标空间字符串中,其中需要注意的事项

1.源字符串必须以 '\0' 结束。


char arr1[20] = {0};
char arr2[] = {'a','b','c'};//程序崩溃,没有char arr1[4] = "x";
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

2.会将源字符串中的 '\0' 拷贝到目标空间。


#include 
#include 
#include 

int main()
{
	
	char arr1[] = "XXXXXXXXXXXX";//arr1指向的是常量字符串,常量是不可修改的
	char arr2[] = "abcdef";
	strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

3.目标空间必须足够大,以确保能存放源字符串。


strcpy不管空间够不够,只要你敢写它就敢放,哪怕程序崩溃

char* arr1 = "qwertyuiop";//arr1指向的是常量字符串,常量是不可修改的
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

4.目标空间必须可变。


1.4 模拟实现strcpy

char* my_strcpy(char*dest, const char* src) { assert(src && dest); char* ret = dest; while(*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = { 0 }; char* arr2 = "hello bit"; printf("%s\n", my_strcpy(arr1, arr2)); return 0; } strcpy函数返回的是目标空间的起始地址 strcpy函数的返回类型的设置是为了实现链式访问
1.5 strcat 字符串追加

int main() { char arr1[10] = "hello " ; char* arr2 = "bit"; printf("%s\n", strcat(arr1, arr2)); return 0; }

int main()
{
	char arr1[20] =  "hello
1.6 模拟实现strcat
XXXXX" ; char arr2[] = "bit"; printf("%s\n", strcat(arr1, arr2)); return 0; }

注意事项:

1.源字符串必须以 '\0' 结束。


从\0位置开始追加

char* my_strcat(char* dest, char* src)
{
	assert(dest && src);
	char* ret = dest;
	//找目标空间中的
1.7 strcmp 比较字符串
while (*dest) { dest++; } //拷贝 while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = "hello"; char arr2[] = " bit"; printf("%s\n", my_strcat(arr1, arr2)); return 0; }

2.目标空间必须有足够的大,能容纳下源字符串的内容。


3.目标空间必须可修改。


strcmp函数比较的不是字符串的长度

而是比较字符串中对应位置上的字符的大小(ASCII码值),如果相同,就比较下一对儿,直到不同或者都遇到
1.8 模拟实现strcmp
2  长度受限制的字符串函数
的ASCII码值是0
2.1 strncpy

字符串自己给自己追加,如何?

我们所写的模拟函数无法完成自己追加自己,也不建议这么使用


char *strncpy( char *strDest, const char *strSource, size_t count ); int main() { char arr1[] = "abcdef"; char arr2[] = "qwewwwwww"; strncpy(arr1, arr2, 5); printf("%s\n", arr1); return 0; }

 标准规定:

第一个字符串大于第二个字符串,则返回大于0的数字

第一个字符串等于第二个字符串,则返回0

第一个字符串小于第二个字符串,则返回小于0的数字


int main() { char arr1[] = "abcdef"; char arr2[] = "qwe"; strncpy(arr1, arr2, 5);//不够默认补
2.2 strncat
printf("%s\n", arr1); return 0; }
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 arr1[] = "abcd";
	char arr2[] = "abdc";
	int ret = my_strcmp(arr1, arr2);
	if (ret >0)
	{
		printf(">\n");
	}
	else if (ret == 0)
	{
		printf("== \n");
	}
	else
	{
		printf("<\n");
	}
	printf("%d\n", ret);
	return 0;
}

int main() { char arr1[20] = "abcdef
2.3 strncmp
XXXXX"; char arr2[] = "qwe"; strncat(arr1, arr2, 3);//追加三个,还会再把int main() { char arr1[] = "abcdef"; char arr2[] = "abcdq"; int ret = strncmp(arr1, arr2, 4);//相等 printf("%d\n", ret); return 0; } 放进去 printf("%s\n", arr1); return 0; }
2.4 strstr  在一个字符串中找另一个字符串

 拷贝num个字符从源字符串到目标空间。


如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。


char * strstr ( const char *str1, const char * str2);
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "de";
	char * p=strstr(arr1, arr2);
	if (p == NULL)//strstr找不到返回NULL指针,我们需要判断
	{
		printf("找不到");
	}
	else
	{
		printf("%s ", p);
	}
	return 0;
}

2.5 模拟实现strstr


char* my_strstr(const char* str1, const char* str2) { assert(str1 && str2); const char* s1 = str1; const char* s2 = str2; const char* cur = str1; while (*cur)//cur不等于
2.6 strtok
进来 { s1 = cur;//判断失败返回cur指向的位置 s2 = str2;//判断失败回到起始位置 while (*s1 && *s2 && (*s1 == *s2))//两个字符串都被查找完,没有数据了 { s1++; s2++; } if (*s2 == 'char * strtok ( char * str, const char * sep );')//字串找到,返回记录地址 { return (char*)cur; } cur++;//匹配不成功,指向下一步 } return NULL;//找不到 } int main() { char arr1[] = "abbbcdef"; char arr2[] = "bbc"; char* ret = my_strstr(arr1, arr2); if (NULL == ret) { printf("找不到子串\n"); } else { printf("%s\n", ret); } return 0; }

 追加num个字符从源字符串到目标空间。


int main()
{
	char arr[] = "lanyangyang@landawang.cunba";
  //char arr[] = "lanyangyang
2.7 strerror  perror  返回错误码,所对应的错误信息
landawangchar * strerror ( int errnum );cunba"; strtok函数会把数组变成这样 char buf[50] = { 0 }; const char* sep = "@. "; strcpy(buf, arr); //printf("%s\n", strtok(buf, sep));//只找第一个标记 //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找 //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找 优化 char* str = NULL; for (str=strtok(buf, sep); str!=NULL; str=strtok(NULL, sep)) { printf("%s\n", str); } return 0; }


2.8 字符分类函数

函数


如果他的参数符合下列条件就返回真
iscntrl

 判断str2是不是str1的子串,如果str2在str1中出现,返回在str1中第一次出现的地址

如果没有出现,返回空指针

任何控制字符

sspace

 思路:只需要找的子串如果复杂,我们需要三个指针协助

s1指针指向str1,s2指针指向str2。


cur指针指向str1,作用是记录开始匹配的地址

如果两个字符串对应位置不相等,str1往后走

如果相等,开始匹配,我们应该记住开始匹配的str1位置,因为有可能相等,有可能不相等

如果一直到\0结束,str2就是str1子串

如果不相等,重新找到记录位置的地址,往后+1.往后走重新开始匹配,其中str2指针重新指向数组起始位置地址

空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t'或者垂直制表符'\v'

isdigit

作用:指定分隔符,让数组分段

十进制数字 0~9

 sep参数是个字符串,定义了用作分隔符的字符集合

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


strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。


(注: strtok函数会改变被 *** 作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。


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


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


如果字符串中不存在更多的标记,则返回 NULL 指针。


isxdigit


十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F
islower

 库函数在使用失败的时候,会留下错误码errno(全局变量),类似网站404错误码

strerror就是翻译错误信息

#include 
#include 
#include 
#include 

int main()
{
	printf("%s\n", strerror(0));
	printf("%s\n", strerror(1));
	printf("%s\n", strerror(2));
	printf("%s\n", strerror(3));
	int* p = (int*)malloc(INT_MAX);//想堆区申请内存的
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		perror("Malloc");//perror是打印错误信息,strerror是把错误码转换成错误信息
 //只需要写字符串,然后输出错误码对应得错误信息,更加方便
		return 1;
	}
	return 0;
}

返回错误码所对应错误信息字符串首字符地址

 


小写字母a~z int tolower ( int c ); int toupper ( int c ); int main() { char ch = 'A'; putchar(toupper(ch)); putchar(tolower(ch)); return 0; }
3. 内存 *** 作函数
3.1 memcpy 内存空间数据拷贝
void * memcpy ( void * destination, const void * source, size_t count );int main() { int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 }; int arr2[5] = { 0 }; return 0; memcpy(arr2,arr1,20);//拷贝20个字节 }
3.2 模拟实现memcpy
3.3 memmove  可以实现重叠内存拷贝
void *memmove( void *dest, const void *src, size_t count );1
3.4  模拟实现memmove
3.5 memcpy  内存字节对应比较
isupper大写字母A~Z
isalpha字母a~z或A~Z
isalnum字母或者数字,a~z,A~Z,0~9
ispunct标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph任何图形字符
sprint任何可打印字符,包括图形字符和空白字符

 

 举例:isdigit接收字符的ASCII码值,返回int类型(如果是数字字符返回非0,不是数组字符返回0)

#include 

int main()
{
int ret = isdigit('5');//5
int ret = isdigit('Q');//0
printf("%d\n", ret);

return 0;
}
char ch = 'A';

	if (ch >= 'a' && ch <= 'z')
	{

	}

这样写很麻烦,我们一个函数搞定

int ret = islower(ch);//判断是否小写,是小写字母返回非0,否则返回0,快速判断

字符转换:

int memcmp ( const void * ptr1, 
 const void * ptr2, 
 size_t num );//比较的字节个数
 int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3,4,0x11223305 };
	int ret = memcmp(arr1, arr2, 18);
	printf("%d\n", ret);

	return 0;
}


3.6  memset  以字节为单位进行内存设置
void *memset( void *dest, int c, size_t count );//目的空间,设置的字符,字符个数
 int main()
{
	int arr[] = { 0x11111111,0x22222222,3,4,5 };
	memset(arr, 6, 20);//memset是以字节为单位来初始化内存单元的
	return 0;
}

注意事项:

1.函数memcpy从source的位置开始向后复制count个字节的数据到destination的内存位置。


2.这个函数在遇到 '\0' 的时候并不会停下来。


3.如果source和destination有任何的重叠,复制的结果都是未定义的。


4.返回目的地的起始地址

 

思路:  

1. 作者实现memcpy函数的时候,作者不知道你要拷贝什么数据,类似qsort 

2.拷贝时应该根据数据类型强制类型转换,一个字节一个字节拷贝

#include 
#include 
#include 

//void* my_memcpy(void* dest, const void* src, size_t count)
//{
//	assert(src && dest);
//	while (count--)
//	{
//		*(char*)dest = *(char*)src;
//		dest = (char*)dest + 1;
//		src = (char*)src + 1;
//	}
// }

void* my_memcpy(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	while (count--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}

	return ret;
}


int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[5] = { 0 };
	my_memcpy(arr2, arr1, 20);



    int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };   
    // 1 2 1 2 3 4 5 8 9 10 期望的结果
	my_memcpy(arr1+2, arr1, 20);

	return 0;
}

但是当我们想拷贝同一块空间的数据时,数据却发生错误

 原因在于数据覆盖了我们想要拷贝的空间

 在同一块内存拷贝,目标和源头数据空间有交叉,我们应该用memmove


[+++]

#include 

int main()
{
		int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
		memmove(arr1 + 2, arr1, 20);
	return 0;
}

[+++]

[+++]

思路:

把34567拷贝放到12345,正着放不会覆盖数据(dest

 而想把34567拷贝放到45678,正着放会覆盖数据,我们可以先把7放到8上,6放到7,5放到6...从后往前拷贝数据,这样数据就不会被覆盖

 总结:当需要拷贝的地址dest > src地址,从后向前拷贝 ;dest < src时,从前向后拷贝

当dest和src空间无交集,前后关系无所谓,我们这里默认从后向前拷贝(方便)

{
	if (dest > src)
	{
		;  //从后向前拷贝
	}
	else
	{
		;  //从前向后拷贝
	}
}

也可以换成另一种写法

if (dest > src && dest<((char*)src+count))
	{
		;//从后向前拷贝
	}
	else
	{
		;//从前向后拷贝
	}

代码思路:从前向后的代码就是memcpy模拟,从后向前,我们需要+20个字节来到dest和src末尾

void* my_memmove(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	if (dest > src)
	{
		while (count--)
		{
	*((char*)dest + count) =  *((char*)src + count);  //count=19,正好指向最后一个字节
		}
		//从后向前拷贝
	}
	else
	{
			void* ret = dest;
			while (count--)
			{
				*(char*)dest = *(char*)src;
				dest = (char*)dest + 1;
				src = (char*)src + 1;
			}
		 //从前向后拷贝
	}
	return ret;
}


	int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr1 + 2, arr1, 20);
	//my_memmove(arr1 , arr1+2, 20);
	return 0;
}

[+++]
[+++]

 

[+++]


[+++]
[+++]

[+++]

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 84, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

目录

C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。


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

1.1 strlen

1.2 三种方式模拟实现strlen库函数

1.3 strcpy 字符串拷贝

1.4 模拟实现strcpy

strcpy函数返回的是目标空间的起始地址

strcpy函数的返回类型的设置是为了实现链式访问

1.5 strcat 字符串追加

1.6 模拟实现strcat

1.7 strcmp 比较字符串

1.8 模拟实现strcmp

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

2.1 strncpy

2.2 strncat

2.3 strncmp

2.4 strstr  在一个字符串中找另一个字符串

2.5 模拟实现strstr

2.6 strtok

2.7 strerror  perror  返回错误码,所对应的错误信息

2.8 字符分类函数

3. 内存 *** 作函数

3.1 memcpy 内存空间数据拷贝

3.2 模拟实现memcpy

 在同一块内存拷贝,目标和源头数据空间有交叉,我们应该用memmove

3.3 memmove  可以实现重叠内存拷贝

3.4  模拟实现memmove

3.5 memcpy  内存字节对应比较

3.6  memset  以字节为单位进行内存设置


C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。


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

1.1 strlen

size_t strlen ( const char * str );//函数原型,注意函数的返回值为size_t

字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' )。


char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };//无法用strlen求字符串长度

char arr[10] = { 'a', 'b', 'c', 'd', 'e', 'f' };//限定长度,可以求

 下面结果是什么?

 if ((int)strlen("abc") - (int)strlen("qwerty") > 0)
	{
		printf(">\n");
	}
	else
	{
		printf("<=\n");
	}

答案:>,两个无符号数相减结果是正数


1.2 三种方式模拟实现strlen库函数

通常写法

 #include 
 #include 

size_t my_strlen(const char* str)
{
	int count = 0;//统计字符的个数
    assert(str);
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}
 
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

递归

 #include 
 #include 

//my_strlen("abcdef")
//1+my_strlen("bcdef")
//1+1+my_strlen("cdef")
//1+1+1+ my_strlen("def")
//1+1+1+1+ my_strlen("ef")
//1 + 1 + 1 + 1 +1+my_strlen("f")
//1 + 1 + 1 + 1 + 1 + 1+ my_strlen("")
//1 + 1 + 1 + 1 + 1 + 1 + 0 = 6
 
size_t my_strlen(const char* str)
{
    assert(str);
	if (*str != '\0')
		return 1 + my_strlen(str+1);
	else
		return 0;
}
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

指针-指针方法

 #include 
 #include 


size_t my_strlen(const char* arr,int sz)//指针-指针
{
    assert(arr);
    char* right = arr + sz - 1;
    return right - arr;
}
 
int main()
{
    char arr[] = "hello";
    int sz = sizeof(arr) / sizeof(arr[0]);
    int len=my_strlen(arr,sz);
    printf("%d", len);
    return 0;
}

1.3 strcpy 字符串拷贝

字符串拷贝,把源字符串拷贝放到目标空间字符串中,其中需要注意的事项

1.源字符串必须以 '\0' 结束。


char arr1[20] = {0};
char arr2[] = {'a','b','c'};//程序崩溃,没有char arr1[4] = "x";
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

2.会将源字符串中的 '\0' 拷贝到目标空间。


#include 
#include 
#include 

int main()
{
	
	char arr1[] = "XXXXXXXXXXXX";//arr1指向的是常量字符串,常量是不可修改的
	char arr2[] = "abcdef";
	strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

3.目标空间必须足够大,以确保能存放源字符串。


strcpy不管空间够不够,只要你敢写它就敢放,哪怕程序崩溃

char* arr1 = "qwertyuiop";//arr1指向的是常量字符串,常量是不可修改的
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

4.目标空间必须可变。


1.4 模拟实现strcpy

char* my_strcpy(char*dest, const char* src) { assert(src && dest); char* ret = dest; while(*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = { 0 }; char* arr2 = "hello bit"; printf("%s\n", my_strcpy(arr1, arr2)); return 0; } strcpy函数返回的是目标空间的起始地址 strcpy函数的返回类型的设置是为了实现链式访问
1.5 strcat 字符串追加

int main() { char arr1[10] = "hello " ; char* arr2 = "bit"; printf("%s\n", strcat(arr1, arr2)); return 0; }

int main()
{
	char arr1[20] =  "hello
1.6 模拟实现strcat
XXXXX" ; char arr2[] = "bit"; printf("%s\n", strcat(arr1, arr2)); return 0; }

注意事项:

1.源字符串必须以 '\0' 结束。


从\0位置开始追加

char* my_strcat(char* dest, char* src)
{
	assert(dest && src);
	char* ret = dest;
	//找目标空间中的
1.7 strcmp 比较字符串
while (*dest) { dest++; } //拷贝 while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = "hello"; char arr2[] = " bit"; printf("%s\n", my_strcat(arr1, arr2)); return 0; }

2.目标空间必须有足够的大,能容纳下源字符串的内容。


3.目标空间必须可修改。


strcmp函数比较的不是字符串的长度

而是比较字符串中对应位置上的字符的大小(ASCII码值),如果相同,就比较下一对儿,直到不同或者都遇到
1.8 模拟实现strcmp
2  长度受限制的字符串函数
的ASCII码值是0
2.1 strncpy

字符串自己给自己追加,如何?

我们所写的模拟函数无法完成自己追加自己,也不建议这么使用


char *strncpy( char *strDest, const char *strSource, size_t count ); int main() { char arr1[] = "abcdef"; char arr2[] = "qwewwwwww"; strncpy(arr1, arr2, 5); printf("%s\n", arr1); return 0; }

 标准规定:

第一个字符串大于第二个字符串,则返回大于0的数字

第一个字符串等于第二个字符串,则返回0

第一个字符串小于第二个字符串,则返回小于0的数字


int main() { char arr1[] = "abcdef"; char arr2[] = "qwe"; strncpy(arr1, arr2, 5);//不够默认补
2.2 strncat
printf("%s\n", arr1); return 0; }
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 arr1[] = "abcd";
	char arr2[] = "abdc";
	int ret = my_strcmp(arr1, arr2);
	if (ret >0)
	{
		printf(">\n");
	}
	else if (ret == 0)
	{
		printf("== \n");
	}
	else
	{
		printf("<\n");
	}
	printf("%d\n", ret);
	return 0;
}

int main() { char arr1[20] = "abcdef
2.3 strncmp
XXXXX"; char arr2[] = "qwe"; strncat(arr1, arr2, 3);//追加三个,还会再把int main() { char arr1[] = "abcdef"; char arr2[] = "abcdq"; int ret = strncmp(arr1, arr2, 4);//相等 printf("%d\n", ret); return 0; } 放进去 printf("%s\n", arr1); return 0; }
2.4 strstr  在一个字符串中找另一个字符串

 拷贝num个字符从源字符串到目标空间。


如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。


char * strstr ( const char *str1, const char * str2);
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "de";
	char * p=strstr(arr1, arr2);
	if (p == NULL)//strstr找不到返回NULL指针,我们需要判断
	{
		printf("找不到");
	}
	else
	{
		printf("%s ", p);
	}
	return 0;
}

2.5 模拟实现strstr


char* my_strstr(const char* str1, const char* str2) { assert(str1 && str2); const char* s1 = str1; const char* s2 = str2; const char* cur = str1; while (*cur)//cur不等于
2.6 strtok
进来 { s1 = cur;//判断失败返回cur指向的位置 s2 = str2;//判断失败回到起始位置 while (*s1 && *s2 && (*s1 == *s2))//两个字符串都被查找完,没有数据了 { s1++; s2++; } if (*s2 == 'char * strtok ( char * str, const char * sep );')//字串找到,返回记录地址 { return (char*)cur; } cur++;//匹配不成功,指向下一步 } return NULL;//找不到 } int main() { char arr1[] = "abbbcdef"; char arr2[] = "bbc"; char* ret = my_strstr(arr1, arr2); if (NULL == ret) { printf("找不到子串\n"); } else { printf("%s\n", ret); } return 0; }

 追加num个字符从源字符串到目标空间。


int main()
{
	char arr[] = "lanyangyang@landawang.cunba";
  //char arr[] = "lanyangyang
2.7 strerror  perror  返回错误码,所对应的错误信息
landawangchar * strerror ( int errnum );cunba"; strtok函数会把数组变成这样 char buf[50] = { 0 }; const char* sep = "@. "; strcpy(buf, arr); //printf("%s\n", strtok(buf, sep));//只找第一个标记 //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找 //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找 优化 char* str = NULL; for (str=strtok(buf, sep); str!=NULL; str=strtok(NULL, sep)) { printf("%s\n", str); } return 0; }


2.8 字符分类函数

函数


如果他的参数符合下列条件就返回真
iscntrl

 判断str2是不是str1的子串,如果str2在str1中出现,返回在str1中第一次出现的地址

如果没有出现,返回空指针

任何控制字符

sspace

 思路:只需要找的子串如果复杂,我们需要三个指针协助

s1指针指向str1,s2指针指向str2。


cur指针指向str1,作用是记录开始匹配的地址

如果两个字符串对应位置不相等,str1往后走

如果相等,开始匹配,我们应该记住开始匹配的str1位置,因为有可能相等,有可能不相等

如果一直到\0结束,str2就是str1子串

如果不相等,重新找到记录位置的地址,往后+1.往后走重新开始匹配,其中str2指针重新指向数组起始位置地址

空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t'或者垂直制表符'\v'

isdigit

作用:指定分隔符,让数组分段

十进制数字 0~9

 sep参数是个字符串,定义了用作分隔符的字符集合

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


strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。


(注: strtok函数会改变被 *** 作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。


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


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


如果字符串中不存在更多的标记,则返回 NULL 指针。


isxdigit


十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F
islower

 库函数在使用失败的时候,会留下错误码errno(全局变量),类似网站404错误码

strerror就是翻译错误信息

#include 
#include 
#include 
#include 

int main()
{
	printf("%s\n", strerror(0));
	printf("%s\n", strerror(1));
	printf("%s\n", strerror(2));
	printf("%s\n", strerror(3));
	int* p = (int*)malloc(INT_MAX);//想堆区申请内存的
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		perror("Malloc");//perror是打印错误信息,strerror是把错误码转换成错误信息
 //只需要写字符串,然后输出错误码对应得错误信息,更加方便
		return 1;
	}
	return 0;
}

返回错误码所对应错误信息字符串首字符地址

 


小写字母a~z int tolower ( int c ); int toupper ( int c ); int main() { char ch = 'A'; putchar(toupper(ch)); putchar(tolower(ch)); return 0; }
3. 内存 *** 作函数
3.1 memcpy 内存空间数据拷贝
void * memcpy ( void * destination, const void * source, size_t count );int main() { int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 }; int arr2[5] = { 0 }; return 0; memcpy(arr2,arr1,20);//拷贝20个字节 }
3.2 模拟实现memcpy
3.3 memmove  可以实现重叠内存拷贝
void *memmove( void *dest, const void *src, size_t count );1
3.4  模拟实现memmove
3.5 memcpy  内存字节对应比较
isupper大写字母A~Z
isalpha字母a~z或A~Z
isalnum字母或者数字,a~z,A~Z,0~9
ispunct标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph任何图形字符
sprint任何可打印字符,包括图形字符和空白字符

 

 举例:isdigit接收字符的ASCII码值,返回int类型(如果是数字字符返回非0,不是数组字符返回0)

#include 

int main()
{
int ret = isdigit('5');//5
int ret = isdigit('Q');//0
printf("%d\n", ret);

return 0;
}
char ch = 'A';

	if (ch >= 'a' && ch <= 'z')
	{

	}

这样写很麻烦,我们一个函数搞定

int ret = islower(ch);//判断是否小写,是小写字母返回非0,否则返回0,快速判断

字符转换:

int memcmp ( const void * ptr1, 
 const void * ptr2, 
 size_t num );//比较的字节个数
 int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3,4,0x11223305 };
	int ret = memcmp(arr1, arr2, 18);
	printf("%d\n", ret);

	return 0;
}


3.6  memset  以字节为单位进行内存设置
void *memset( void *dest, int c, size_t count );//目的空间,设置的字符,字符个数
 int main()
{
	int arr[] = { 0x11111111,0x22222222,3,4,5 };
	memset(arr, 6, 20);//memset是以字节为单位来初始化内存单元的
	return 0;
}

注意事项:

1.函数memcpy从source的位置开始向后复制count个字节的数据到destination的内存位置。


2.这个函数在遇到 '\0' 的时候并不会停下来。


3.如果source和destination有任何的重叠,复制的结果都是未定义的。


4.返回目的地的起始地址

 

思路:  

1. 作者实现memcpy函数的时候,作者不知道你要拷贝什么数据,类似qsort 

2.拷贝时应该根据数据类型强制类型转换,一个字节一个字节拷贝

#include 
#include 
#include 

//void* my_memcpy(void* dest, const void* src, size_t count)
//{
//	assert(src && dest);
//	while (count--)
//	{
//		*(char*)dest = *(char*)src;
//		dest = (char*)dest + 1;
//		src = (char*)src + 1;
//	}
// }

void* my_memcpy(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	while (count--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}

	return ret;
}


int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[5] = { 0 };
	my_memcpy(arr2, arr1, 20);



    int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };   
    // 1 2 1 2 3 4 5 8 9 10 期望的结果
	my_memcpy(arr1+2, arr1, 20);

	return 0;
}

但是当我们想拷贝同一块空间的数据时,数据却发生错误

 原因在于数据覆盖了我们想要拷贝的空间

 在同一块内存拷贝,目标和源头数据空间有交叉,我们应该用memmove


 

#include 

int main()
{
		int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
		memmove(arr1 + 2, arr1, 20);
	return 0;
}

[+++]

[+++]

思路:

把34567拷贝放到12345,正着放不会覆盖数据(dest

 而想把34567拷贝放到45678,正着放会覆盖数据,我们可以先把7放到8上,6放到7,5放到6...从后往前拷贝数据,这样数据就不会被覆盖

 总结:当需要拷贝的地址dest > src地址,从后向前拷贝 ;dest < src时,从前向后拷贝

当dest和src空间无交集,前后关系无所谓,我们这里默认从后向前拷贝(方便)

{
	if (dest > src)
	{
		;  //从后向前拷贝
	}
	else
	{
		;  //从前向后拷贝
	}
}

也可以换成另一种写法

if (dest > src && dest<((char*)src+count))
	{
		;//从后向前拷贝
	}
	else
	{
		;//从前向后拷贝
	}

代码思路:从前向后的代码就是memcpy模拟,从后向前,我们需要+20个字节来到dest和src末尾

void* my_memmove(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	if (dest > src)
	{
		while (count--)
		{
	*((char*)dest + count) =  *((char*)src + count);  //count=19,正好指向最后一个字节
		}
		//从后向前拷贝
	}
	else
	{
			void* ret = dest;
			while (count--)
			{
				*(char*)dest = *(char*)src;
				dest = (char*)dest + 1;
				src = (char*)src + 1;
			}
		 //从前向后拷贝
	}
	return ret;
}


	int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr1 + 2, arr1, 20);
	//my_memmove(arr1 , arr1+2, 20);
	return 0;
}

[+++]
[+++]

 

[+++]


[+++]
[+++]

[+++]

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 85, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

目录

C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。


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

1.1 strlen

1.2 三种方式模拟实现strlen库函数

1.3 strcpy 字符串拷贝

1.4 模拟实现strcpy

strcpy函数返回的是目标空间的起始地址

strcpy函数的返回类型的设置是为了实现链式访问

1.5 strcat 字符串追加

1.6 模拟实现strcat

1.7 strcmp 比较字符串

1.8 模拟实现strcmp

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

2.1 strncpy

2.2 strncat

2.3 strncmp

2.4 strstr  在一个字符串中找另一个字符串

2.5 模拟实现strstr

2.6 strtok

2.7 strerror  perror  返回错误码,所对应的错误信息

2.8 字符分类函数

3. 内存 *** 作函数

3.1 memcpy 内存空间数据拷贝

3.2 模拟实现memcpy

 在同一块内存拷贝,目标和源头数据空间有交叉,我们应该用memmove

3.3 memmove  可以实现重叠内存拷贝

3.4  模拟实现memmove

3.5 memcpy  内存字节对应比较

3.6  memset  以字节为单位进行内存设置


C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。


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

1.1 strlen

size_t strlen ( const char * str );//函数原型,注意函数的返回值为size_t

字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' )。


char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };//无法用strlen求字符串长度

char arr[10] = { 'a', 'b', 'c', 'd', 'e', 'f' };//限定长度,可以求

 下面结果是什么?

 if ((int)strlen("abc") - (int)strlen("qwerty") > 0)
	{
		printf(">\n");
	}
	else
	{
		printf("<=\n");
	}

答案:>,两个无符号数相减结果是正数


1.2 三种方式模拟实现strlen库函数

通常写法

 #include 
 #include 

size_t my_strlen(const char* str)
{
	int count = 0;//统计字符的个数
    assert(str);
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}
 
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

递归

 #include 
 #include 

//my_strlen("abcdef")
//1+my_strlen("bcdef")
//1+1+my_strlen("cdef")
//1+1+1+ my_strlen("def")
//1+1+1+1+ my_strlen("ef")
//1 + 1 + 1 + 1 +1+my_strlen("f")
//1 + 1 + 1 + 1 + 1 + 1+ my_strlen("")
//1 + 1 + 1 + 1 + 1 + 1 + 0 = 6
 
size_t my_strlen(const char* str)
{
    assert(str);
	if (*str != '\0')
		return 1 + my_strlen(str+1);
	else
		return 0;
}
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

指针-指针方法

 #include 
 #include 


size_t my_strlen(const char* arr,int sz)//指针-指针
{
    assert(arr);
    char* right = arr + sz - 1;
    return right - arr;
}
 
int main()
{
    char arr[] = "hello";
    int sz = sizeof(arr) / sizeof(arr[0]);
    int len=my_strlen(arr,sz);
    printf("%d", len);
    return 0;
}

1.3 strcpy 字符串拷贝

字符串拷贝,把源字符串拷贝放到目标空间字符串中,其中需要注意的事项

1.源字符串必须以 '\0' 结束。


char arr1[20] = {0};
char arr2[] = {'a','b','c'};//程序崩溃,没有char arr1[4] = "x";
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

2.会将源字符串中的 '\0' 拷贝到目标空间。


#include 
#include 
#include 

int main()
{
	
	char arr1[] = "XXXXXXXXXXXX";//arr1指向的是常量字符串,常量是不可修改的
	char arr2[] = "abcdef";
	strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

3.目标空间必须足够大,以确保能存放源字符串。


strcpy不管空间够不够,只要你敢写它就敢放,哪怕程序崩溃

char* arr1 = "qwertyuiop";//arr1指向的是常量字符串,常量是不可修改的
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

4.目标空间必须可变。


1.4 模拟实现strcpy

char* my_strcpy(char*dest, const char* src) { assert(src && dest); char* ret = dest; while(*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = { 0 }; char* arr2 = "hello bit"; printf("%s\n", my_strcpy(arr1, arr2)); return 0; } strcpy函数返回的是目标空间的起始地址 strcpy函数的返回类型的设置是为了实现链式访问
1.5 strcat 字符串追加

int main() { char arr1[10] = "hello " ; char* arr2 = "bit"; printf("%s\n", strcat(arr1, arr2)); return 0; }

int main()
{
	char arr1[20] =  "hello
1.6 模拟实现strcat
XXXXX" ; char arr2[] = "bit"; printf("%s\n", strcat(arr1, arr2)); return 0; }

注意事项:

1.源字符串必须以 '\0' 结束。


从\0位置开始追加

char* my_strcat(char* dest, char* src)
{
	assert(dest && src);
	char* ret = dest;
	//找目标空间中的
1.7 strcmp 比较字符串
while (*dest) { dest++; } //拷贝 while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = "hello"; char arr2[] = " bit"; printf("%s\n", my_strcat(arr1, arr2)); return 0; }

2.目标空间必须有足够的大,能容纳下源字符串的内容。


3.目标空间必须可修改。


strcmp函数比较的不是字符串的长度

而是比较字符串中对应位置上的字符的大小(ASCII码值),如果相同,就比较下一对儿,直到不同或者都遇到
1.8 模拟实现strcmp
2  长度受限制的字符串函数
的ASCII码值是0
2.1 strncpy

字符串自己给自己追加,如何?

我们所写的模拟函数无法完成自己追加自己,也不建议这么使用


char *strncpy( char *strDest, const char *strSource, size_t count ); int main() { char arr1[] = "abcdef"; char arr2[] = "qwewwwwww"; strncpy(arr1, arr2, 5); printf("%s\n", arr1); return 0; }

 标准规定:

第一个字符串大于第二个字符串,则返回大于0的数字

第一个字符串等于第二个字符串,则返回0

第一个字符串小于第二个字符串,则返回小于0的数字


int main() { char arr1[] = "abcdef"; char arr2[] = "qwe"; strncpy(arr1, arr2, 5);//不够默认补
2.2 strncat
printf("%s\n", arr1); return 0; }
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 arr1[] = "abcd";
	char arr2[] = "abdc";
	int ret = my_strcmp(arr1, arr2);
	if (ret >0)
	{
		printf(">\n");
	}
	else if (ret == 0)
	{
		printf("== \n");
	}
	else
	{
		printf("<\n");
	}
	printf("%d\n", ret);
	return 0;
}

int main() { char arr1[20] = "abcdef
2.3 strncmp
XXXXX"; char arr2[] = "qwe"; strncat(arr1, arr2, 3);//追加三个,还会再把int main() { char arr1[] = "abcdef"; char arr2[] = "abcdq"; int ret = strncmp(arr1, arr2, 4);//相等 printf("%d\n", ret); return 0; } 放进去 printf("%s\n", arr1); return 0; }
2.4 strstr  在一个字符串中找另一个字符串

 拷贝num个字符从源字符串到目标空间。


如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。


char * strstr ( const char *str1, const char * str2);
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "de";
	char * p=strstr(arr1, arr2);
	if (p == NULL)//strstr找不到返回NULL指针,我们需要判断
	{
		printf("找不到");
	}
	else
	{
		printf("%s ", p);
	}
	return 0;
}

2.5 模拟实现strstr


char* my_strstr(const char* str1, const char* str2) { assert(str1 && str2); const char* s1 = str1; const char* s2 = str2; const char* cur = str1; while (*cur)//cur不等于
2.6 strtok
进来 { s1 = cur;//判断失败返回cur指向的位置 s2 = str2;//判断失败回到起始位置 while (*s1 && *s2 && (*s1 == *s2))//两个字符串都被查找完,没有数据了 { s1++; s2++; } if (*s2 == 'char * strtok ( char * str, const char * sep );')//字串找到,返回记录地址 { return (char*)cur; } cur++;//匹配不成功,指向下一步 } return NULL;//找不到 } int main() { char arr1[] = "abbbcdef"; char arr2[] = "bbc"; char* ret = my_strstr(arr1, arr2); if (NULL == ret) { printf("找不到子串\n"); } else { printf("%s\n", ret); } return 0; }

 追加num个字符从源字符串到目标空间。


int main()
{
	char arr[] = "lanyangyang@landawang.cunba";
  //char arr[] = "lanyangyang
2.7 strerror  perror  返回错误码,所对应的错误信息
landawangchar * strerror ( int errnum );cunba"; strtok函数会把数组变成这样 char buf[50] = { 0 }; const char* sep = "@. "; strcpy(buf, arr); //printf("%s\n", strtok(buf, sep));//只找第一个标记 //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找 //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找 优化 char* str = NULL; for (str=strtok(buf, sep); str!=NULL; str=strtok(NULL, sep)) { printf("%s\n", str); } return 0; }


2.8 字符分类函数

函数


如果他的参数符合下列条件就返回真
iscntrl

 判断str2是不是str1的子串,如果str2在str1中出现,返回在str1中第一次出现的地址

如果没有出现,返回空指针

任何控制字符

sspace

 思路:只需要找的子串如果复杂,我们需要三个指针协助

s1指针指向str1,s2指针指向str2。


cur指针指向str1,作用是记录开始匹配的地址

如果两个字符串对应位置不相等,str1往后走

如果相等,开始匹配,我们应该记住开始匹配的str1位置,因为有可能相等,有可能不相等

如果一直到\0结束,str2就是str1子串

如果不相等,重新找到记录位置的地址,往后+1.往后走重新开始匹配,其中str2指针重新指向数组起始位置地址

空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t'或者垂直制表符'\v'

isdigit

作用:指定分隔符,让数组分段

十进制数字 0~9

 sep参数是个字符串,定义了用作分隔符的字符集合

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


strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。


(注: strtok函数会改变被 *** 作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。


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


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


如果字符串中不存在更多的标记,则返回 NULL 指针。


isxdigit


十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F
islower

 库函数在使用失败的时候,会留下错误码errno(全局变量),类似网站404错误码

strerror就是翻译错误信息

#include 
#include 
#include 
#include 

int main()
{
	printf("%s\n", strerror(0));
	printf("%s\n", strerror(1));
	printf("%s\n", strerror(2));
	printf("%s\n", strerror(3));
	int* p = (int*)malloc(INT_MAX);//想堆区申请内存的
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		perror("Malloc");//perror是打印错误信息,strerror是把错误码转换成错误信息
 //只需要写字符串,然后输出错误码对应得错误信息,更加方便
		return 1;
	}
	return 0;
}

返回错误码所对应错误信息字符串首字符地址

 


小写字母a~z int tolower ( int c ); int toupper ( int c ); int main() { char ch = 'A'; putchar(toupper(ch)); putchar(tolower(ch)); return 0; }
3. 内存 *** 作函数
3.1 memcpy 内存空间数据拷贝
void * memcpy ( void * destination, const void * source, size_t count );int main() { int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 }; int arr2[5] = { 0 }; return 0; memcpy(arr2,arr1,20);//拷贝20个字节 }
3.2 模拟实现memcpy
3.3 memmove  可以实现重叠内存拷贝
void *memmove( void *dest, const void *src, size_t count );1
3.4  模拟实现memmove
3.5 memcpy  内存字节对应比较
isupper大写字母A~Z
isalpha字母a~z或A~Z
isalnum字母或者数字,a~z,A~Z,0~9
ispunct标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph任何图形字符
sprint任何可打印字符,包括图形字符和空白字符

 

 举例:isdigit接收字符的ASCII码值,返回int类型(如果是数字字符返回非0,不是数组字符返回0)

#include 

int main()
{
int ret = isdigit('5');//5
int ret = isdigit('Q');//0
printf("%d\n", ret);

return 0;
}
char ch = 'A';

	if (ch >= 'a' && ch <= 'z')
	{

	}

这样写很麻烦,我们一个函数搞定

int ret = islower(ch);//判断是否小写,是小写字母返回非0,否则返回0,快速判断

字符转换:

int memcmp ( const void * ptr1, 
 const void * ptr2, 
 size_t num );//比较的字节个数
 int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3,4,0x11223305 };
	int ret = memcmp(arr1, arr2, 18);
	printf("%d\n", ret);

	return 0;
}


3.6  memset  以字节为单位进行内存设置
void *memset( void *dest, int c, size_t count );//目的空间,设置的字符,字符个数
 int main()
{
	int arr[] = { 0x11111111,0x22222222,3,4,5 };
	memset(arr, 6, 20);//memset是以字节为单位来初始化内存单元的
	return 0;
}

注意事项:

1.函数memcpy从source的位置开始向后复制count个字节的数据到destination的内存位置。


2.这个函数在遇到 '\0' 的时候并不会停下来。


3.如果source和destination有任何的重叠,复制的结果都是未定义的。


4.返回目的地的起始地址

 

思路:  

1. 作者实现memcpy函数的时候,作者不知道你要拷贝什么数据,类似qsort 

2.拷贝时应该根据数据类型强制类型转换,一个字节一个字节拷贝

#include 
#include 
#include 

//void* my_memcpy(void* dest, const void* src, size_t count)
//{
//	assert(src && dest);
//	while (count--)
//	{
//		*(char*)dest = *(char*)src;
//		dest = (char*)dest + 1;
//		src = (char*)src + 1;
//	}
// }

void* my_memcpy(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	while (count--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}

	return ret;
}


int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[5] = { 0 };
	my_memcpy(arr2, arr1, 20);



    int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };   
    // 1 2 1 2 3 4 5 8 9 10 期望的结果
	my_memcpy(arr1+2, arr1, 20);

	return 0;
}

但是当我们想拷贝同一块空间的数据时,数据却发生错误

 原因在于数据覆盖了我们想要拷贝的空间

 在同一块内存拷贝,目标和源头数据空间有交叉,我们应该用memmove


 

#include 

int main()
{
		int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
		memmove(arr1 + 2, arr1, 20);
	return 0;
}

 

[+++]

思路:

把34567拷贝放到12345,正着放不会覆盖数据(dest

 而想把34567拷贝放到45678,正着放会覆盖数据,我们可以先把7放到8上,6放到7,5放到6...从后往前拷贝数据,这样数据就不会被覆盖

 总结:当需要拷贝的地址dest > src地址,从后向前拷贝 ;dest < src时,从前向后拷贝

当dest和src空间无交集,前后关系无所谓,我们这里默认从后向前拷贝(方便)

{
	if (dest > src)
	{
		;  //从后向前拷贝
	}
	else
	{
		;  //从前向后拷贝
	}
}

也可以换成另一种写法

if (dest > src && dest<((char*)src+count))
	{
		;//从后向前拷贝
	}
	else
	{
		;//从前向后拷贝
	}

代码思路:从前向后的代码就是memcpy模拟,从后向前,我们需要+20个字节来到dest和src末尾

void* my_memmove(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	if (dest > src)
	{
		while (count--)
		{
	*((char*)dest + count) =  *((char*)src + count);  //count=19,正好指向最后一个字节
		}
		//从后向前拷贝
	}
	else
	{
			void* ret = dest;
			while (count--)
			{
				*(char*)dest = *(char*)src;
				dest = (char*)dest + 1;
				src = (char*)src + 1;
			}
		 //从前向后拷贝
	}
	return ret;
}


	int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr1 + 2, arr1, 20);
	//my_memmove(arr1 , arr1+2, 20);
	return 0;
}

[+++]
[+++]

 

[+++]


[+++]
[+++]

[+++]

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 86, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

目录

C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。


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

1.1 strlen

1.2 三种方式模拟实现strlen库函数

1.3 strcpy 字符串拷贝

1.4 模拟实现strcpy

strcpy函数返回的是目标空间的起始地址

strcpy函数的返回类型的设置是为了实现链式访问

1.5 strcat 字符串追加

1.6 模拟实现strcat

1.7 strcmp 比较字符串

1.8 模拟实现strcmp

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

2.1 strncpy

2.2 strncat

2.3 strncmp

2.4 strstr  在一个字符串中找另一个字符串

2.5 模拟实现strstr

2.6 strtok

2.7 strerror  perror  返回错误码,所对应的错误信息

2.8 字符分类函数

3. 内存 *** 作函数

3.1 memcpy 内存空间数据拷贝

3.2 模拟实现memcpy

 在同一块内存拷贝,目标和源头数据空间有交叉,我们应该用memmove

3.3 memmove  可以实现重叠内存拷贝

3.4  模拟实现memmove

3.5 memcpy  内存字节对应比较

3.6  memset  以字节为单位进行内存设置


C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。


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

1.1 strlen

size_t strlen ( const char * str );//函数原型,注意函数的返回值为size_t

字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' )。


char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };//无法用strlen求字符串长度

char arr[10] = { 'a', 'b', 'c', 'd', 'e', 'f' };//限定长度,可以求

 下面结果是什么?

 if ((int)strlen("abc") - (int)strlen("qwerty") > 0)
	{
		printf(">\n");
	}
	else
	{
		printf("<=\n");
	}

答案:>,两个无符号数相减结果是正数


1.2 三种方式模拟实现strlen库函数

通常写法

 #include 
 #include 

size_t my_strlen(const char* str)
{
	int count = 0;//统计字符的个数
    assert(str);
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}
 
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

递归

 #include 
 #include 

//my_strlen("abcdef")
//1+my_strlen("bcdef")
//1+1+my_strlen("cdef")
//1+1+1+ my_strlen("def")
//1+1+1+1+ my_strlen("ef")
//1 + 1 + 1 + 1 +1+my_strlen("f")
//1 + 1 + 1 + 1 + 1 + 1+ my_strlen("")
//1 + 1 + 1 + 1 + 1 + 1 + 0 = 6
 
size_t my_strlen(const char* str)
{
    assert(str);
	if (*str != '\0')
		return 1 + my_strlen(str+1);
	else
		return 0;
}
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

指针-指针方法

 #include 
 #include 


size_t my_strlen(const char* arr,int sz)//指针-指针
{
    assert(arr);
    char* right = arr + sz - 1;
    return right - arr;
}
 
int main()
{
    char arr[] = "hello";
    int sz = sizeof(arr) / sizeof(arr[0]);
    int len=my_strlen(arr,sz);
    printf("%d", len);
    return 0;
}

1.3 strcpy 字符串拷贝

字符串拷贝,把源字符串拷贝放到目标空间字符串中,其中需要注意的事项

1.源字符串必须以 '\0' 结束。


char arr1[20] = {0};
char arr2[] = {'a','b','c'};//程序崩溃,没有char arr1[4] = "x";
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

2.会将源字符串中的 '\0' 拷贝到目标空间。


#include 
#include 
#include 

int main()
{
	
	char arr1[] = "XXXXXXXXXXXX";//arr1指向的是常量字符串,常量是不可修改的
	char arr2[] = "abcdef";
	strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

3.目标空间必须足够大,以确保能存放源字符串。


strcpy不管空间够不够,只要你敢写它就敢放,哪怕程序崩溃

char* arr1 = "qwertyuiop";//arr1指向的是常量字符串,常量是不可修改的
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

4.目标空间必须可变。


1.4 模拟实现strcpy

char* my_strcpy(char*dest, const char* src) { assert(src && dest); char* ret = dest; while(*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = { 0 }; char* arr2 = "hello bit"; printf("%s\n", my_strcpy(arr1, arr2)); return 0; } strcpy函数返回的是目标空间的起始地址 strcpy函数的返回类型的设置是为了实现链式访问
1.5 strcat 字符串追加

int main() { char arr1[10] = "hello " ; char* arr2 = "bit"; printf("%s\n", strcat(arr1, arr2)); return 0; }

int main()
{
	char arr1[20] =  "hello
1.6 模拟实现strcat
XXXXX" ; char arr2[] = "bit"; printf("%s\n", strcat(arr1, arr2)); return 0; }

注意事项:

1.源字符串必须以 '\0' 结束。


从\0位置开始追加

char* my_strcat(char* dest, char* src)
{
	assert(dest && src);
	char* ret = dest;
	//找目标空间中的
1.7 strcmp 比较字符串
while (*dest) { dest++; } //拷贝 while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = "hello"; char arr2[] = " bit"; printf("%s\n", my_strcat(arr1, arr2)); return 0; }

2.目标空间必须有足够的大,能容纳下源字符串的内容。


3.目标空间必须可修改。


strcmp函数比较的不是字符串的长度

而是比较字符串中对应位置上的字符的大小(ASCII码值),如果相同,就比较下一对儿,直到不同或者都遇到
1.8 模拟实现strcmp
2  长度受限制的字符串函数
的ASCII码值是0
2.1 strncpy

字符串自己给自己追加,如何?

我们所写的模拟函数无法完成自己追加自己,也不建议这么使用


char *strncpy( char *strDest, const char *strSource, size_t count ); int main() { char arr1[] = "abcdef"; char arr2[] = "qwewwwwww"; strncpy(arr1, arr2, 5); printf("%s\n", arr1); return 0; }

 标准规定:

第一个字符串大于第二个字符串,则返回大于0的数字

第一个字符串等于第二个字符串,则返回0

第一个字符串小于第二个字符串,则返回小于0的数字


int main() { char arr1[] = "abcdef"; char arr2[] = "qwe"; strncpy(arr1, arr2, 5);//不够默认补
2.2 strncat
printf("%s\n", arr1); return 0; }
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 arr1[] = "abcd";
	char arr2[] = "abdc";
	int ret = my_strcmp(arr1, arr2);
	if (ret >0)
	{
		printf(">\n");
	}
	else if (ret == 0)
	{
		printf("== \n");
	}
	else
	{
		printf("<\n");
	}
	printf("%d\n", ret);
	return 0;
}

int main() { char arr1[20] = "abcdef
2.3 strncmp
XXXXX"; char arr2[] = "qwe"; strncat(arr1, arr2, 3);//追加三个,还会再把int main() { char arr1[] = "abcdef"; char arr2[] = "abcdq"; int ret = strncmp(arr1, arr2, 4);//相等 printf("%d\n", ret); return 0; } 放进去 printf("%s\n", arr1); return 0; }
2.4 strstr  在一个字符串中找另一个字符串

 拷贝num个字符从源字符串到目标空间。


如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。


char * strstr ( const char *str1, const char * str2);
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "de";
	char * p=strstr(arr1, arr2);
	if (p == NULL)//strstr找不到返回NULL指针,我们需要判断
	{
		printf("找不到");
	}
	else
	{
		printf("%s ", p);
	}
	return 0;
}

2.5 模拟实现strstr


char* my_strstr(const char* str1, const char* str2) { assert(str1 && str2); const char* s1 = str1; const char* s2 = str2; const char* cur = str1; while (*cur)//cur不等于
2.6 strtok
进来 { s1 = cur;//判断失败返回cur指向的位置 s2 = str2;//判断失败回到起始位置 while (*s1 && *s2 && (*s1 == *s2))//两个字符串都被查找完,没有数据了 { s1++; s2++; } if (*s2 == 'char * strtok ( char * str, const char * sep );')//字串找到,返回记录地址 { return (char*)cur; } cur++;//匹配不成功,指向下一步 } return NULL;//找不到 } int main() { char arr1[] = "abbbcdef"; char arr2[] = "bbc"; char* ret = my_strstr(arr1, arr2); if (NULL == ret) { printf("找不到子串\n"); } else { printf("%s\n", ret); } return 0; }

 追加num个字符从源字符串到目标空间。


int main()
{
	char arr[] = "lanyangyang@landawang.cunba";
  //char arr[] = "lanyangyang
2.7 strerror  perror  返回错误码,所对应的错误信息
landawangchar * strerror ( int errnum );cunba"; strtok函数会把数组变成这样 char buf[50] = { 0 }; const char* sep = "@. "; strcpy(buf, arr); //printf("%s\n", strtok(buf, sep));//只找第一个标记 //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找 //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找 优化 char* str = NULL; for (str=strtok(buf, sep); str!=NULL; str=strtok(NULL, sep)) { printf("%s\n", str); } return 0; }


2.8 字符分类函数

函数


如果他的参数符合下列条件就返回真
iscntrl

 判断str2是不是str1的子串,如果str2在str1中出现,返回在str1中第一次出现的地址

如果没有出现,返回空指针

任何控制字符

sspace

 思路:只需要找的子串如果复杂,我们需要三个指针协助

s1指针指向str1,s2指针指向str2。


cur指针指向str1,作用是记录开始匹配的地址

如果两个字符串对应位置不相等,str1往后走

如果相等,开始匹配,我们应该记住开始匹配的str1位置,因为有可能相等,有可能不相等

如果一直到\0结束,str2就是str1子串

如果不相等,重新找到记录位置的地址,往后+1.往后走重新开始匹配,其中str2指针重新指向数组起始位置地址

空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t'或者垂直制表符'\v'

isdigit

作用:指定分隔符,让数组分段

十进制数字 0~9

 sep参数是个字符串,定义了用作分隔符的字符集合

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


strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。


(注: strtok函数会改变被 *** 作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。


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


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


如果字符串中不存在更多的标记,则返回 NULL 指针。


isxdigit


十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F
islower

 库函数在使用失败的时候,会留下错误码errno(全局变量),类似网站404错误码

strerror就是翻译错误信息

#include 
#include 
#include 
#include 

int main()
{
	printf("%s\n", strerror(0));
	printf("%s\n", strerror(1));
	printf("%s\n", strerror(2));
	printf("%s\n", strerror(3));
	int* p = (int*)malloc(INT_MAX);//想堆区申请内存的
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		perror("Malloc");//perror是打印错误信息,strerror是把错误码转换成错误信息
 //只需要写字符串,然后输出错误码对应得错误信息,更加方便
		return 1;
	}
	return 0;
}

返回错误码所对应错误信息字符串首字符地址

 


小写字母a~z int tolower ( int c ); int toupper ( int c ); int main() { char ch = 'A'; putchar(toupper(ch)); putchar(tolower(ch)); return 0; }
3. 内存 *** 作函数
3.1 memcpy 内存空间数据拷贝
void * memcpy ( void * destination, const void * source, size_t count );int main() { int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 }; int arr2[5] = { 0 }; return 0; memcpy(arr2,arr1,20);//拷贝20个字节 }
3.2 模拟实现memcpy
3.3 memmove  可以实现重叠内存拷贝
void *memmove( void *dest, const void *src, size_t count );1
3.4  模拟实现memmove
3.5 memcpy  内存字节对应比较
isupper大写字母A~Z
isalpha字母a~z或A~Z
isalnum字母或者数字,a~z,A~Z,0~9
ispunct标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph任何图形字符
sprint任何可打印字符,包括图形字符和空白字符

 

 举例:isdigit接收字符的ASCII码值,返回int类型(如果是数字字符返回非0,不是数组字符返回0)

#include 

int main()
{
int ret = isdigit('5');//5
int ret = isdigit('Q');//0
printf("%d\n", ret);

return 0;
}
char ch = 'A';

	if (ch >= 'a' && ch <= 'z')
	{

	}

这样写很麻烦,我们一个函数搞定

int ret = islower(ch);//判断是否小写,是小写字母返回非0,否则返回0,快速判断

字符转换:

int memcmp ( const void * ptr1, 
 const void * ptr2, 
 size_t num );//比较的字节个数
 int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3,4,0x11223305 };
	int ret = memcmp(arr1, arr2, 18);
	printf("%d\n", ret);

	return 0;
}


3.6  memset  以字节为单位进行内存设置
void *memset( void *dest, int c, size_t count );//目的空间,设置的字符,字符个数
 int main()
{
	int arr[] = { 0x11111111,0x22222222,3,4,5 };
	memset(arr, 6, 20);//memset是以字节为单位来初始化内存单元的
	return 0;
}

注意事项:

1.函数memcpy从source的位置开始向后复制count个字节的数据到destination的内存位置。


2.这个函数在遇到 '\0' 的时候并不会停下来。


3.如果source和destination有任何的重叠,复制的结果都是未定义的。


4.返回目的地的起始地址

 

思路:  

1. 作者实现memcpy函数的时候,作者不知道你要拷贝什么数据,类似qsort 

2.拷贝时应该根据数据类型强制类型转换,一个字节一个字节拷贝

#include 
#include 
#include 

//void* my_memcpy(void* dest, const void* src, size_t count)
//{
//	assert(src && dest);
//	while (count--)
//	{
//		*(char*)dest = *(char*)src;
//		dest = (char*)dest + 1;
//		src = (char*)src + 1;
//	}
// }

void* my_memcpy(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	while (count--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}

	return ret;
}


int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[5] = { 0 };
	my_memcpy(arr2, arr1, 20);



    int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };   
    // 1 2 1 2 3 4 5 8 9 10 期望的结果
	my_memcpy(arr1+2, arr1, 20);

	return 0;
}

但是当我们想拷贝同一块空间的数据时,数据却发生错误

 原因在于数据覆盖了我们想要拷贝的空间

 在同一块内存拷贝,目标和源头数据空间有交叉,我们应该用memmove


 

#include 

int main()
{
		int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
		memmove(arr1 + 2, arr1, 20);
	return 0;
}

 

思路:

把34567拷贝放到12345,正着放不会覆盖数据(dest

 而想把34567拷贝放到45678,正着放会覆盖数据,我们可以先把7放到8上,6放到7,5放到6...从后往前拷贝数据,这样数据就不会被覆盖

 总结:当需要拷贝的地址dest > src地址,从后向前拷贝 ;dest < src时,从前向后拷贝

当dest和src空间无交集,前后关系无所谓,我们这里默认从后向前拷贝(方便)

{
	if (dest > src)
	{
		;  //从后向前拷贝
	}
	else
	{
		;  //从前向后拷贝
	}
}

也可以换成另一种写法

if (dest > src && dest<((char*)src+count))
	{
		;//从后向前拷贝
	}
	else
	{
		;//从前向后拷贝
	}

代码思路:从前向后的代码就是memcpy模拟,从后向前,我们需要+20个字节来到dest和src末尾

void* my_memmove(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	if (dest > src)
	{
		while (count--)
		{
	*((char*)dest + count) =  *((char*)src + count);  //count=19,正好指向最后一个字节
		}
		//从后向前拷贝
	}
	else
	{
			void* ret = dest;
			while (count--)
			{
				*(char*)dest = *(char*)src;
				dest = (char*)dest + 1;
				src = (char*)src + 1;
			}
		 //从前向后拷贝
	}
	return ret;
}


	int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr1 + 2, arr1, 20);
	//my_memmove(arr1 , arr1+2, 20);
	return 0;
}

[+++]
[+++]

 

[+++]


[+++]
[+++]

[+++]

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 87, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

目录

C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。


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

1.1 strlen

1.2 三种方式模拟实现strlen库函数

1.3 strcpy 字符串拷贝

1.4 模拟实现strcpy

strcpy函数返回的是目标空间的起始地址

strcpy函数的返回类型的设置是为了实现链式访问

1.5 strcat 字符串追加

1.6 模拟实现strcat

1.7 strcmp 比较字符串

1.8 模拟实现strcmp

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

2.1 strncpy

2.2 strncat

2.3 strncmp

2.4 strstr  在一个字符串中找另一个字符串

2.5 模拟实现strstr

2.6 strtok

2.7 strerror  perror  返回错误码,所对应的错误信息

2.8 字符分类函数

3. 内存 *** 作函数

3.1 memcpy 内存空间数据拷贝

3.2 模拟实现memcpy

 在同一块内存拷贝,目标和源头数据空间有交叉,我们应该用memmove

3.3 memmove  可以实现重叠内存拷贝

3.4  模拟实现memmove

3.5 memcpy  内存字节对应比较

3.6  memset  以字节为单位进行内存设置


C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。


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

1.1 strlen

size_t strlen ( const char * str );//函数原型,注意函数的返回值为size_t

字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' )。


char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };//无法用strlen求字符串长度

char arr[10] = { 'a', 'b', 'c', 'd', 'e', 'f' };//限定长度,可以求

 下面结果是什么?

 if ((int)strlen("abc") - (int)strlen("qwerty") > 0)
	{
		printf(">\n");
	}
	else
	{
		printf("<=\n");
	}

答案:>,两个无符号数相减结果是正数


1.2 三种方式模拟实现strlen库函数

通常写法

 #include 
 #include 

size_t my_strlen(const char* str)
{
	int count = 0;//统计字符的个数
    assert(str);
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}
 
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

递归

 #include 
 #include 

//my_strlen("abcdef")
//1+my_strlen("bcdef")
//1+1+my_strlen("cdef")
//1+1+1+ my_strlen("def")
//1+1+1+1+ my_strlen("ef")
//1 + 1 + 1 + 1 +1+my_strlen("f")
//1 + 1 + 1 + 1 + 1 + 1+ my_strlen("")
//1 + 1 + 1 + 1 + 1 + 1 + 0 = 6
 
size_t my_strlen(const char* str)
{
    assert(str);
	if (*str != '\0')
		return 1 + my_strlen(str+1);
	else
		return 0;
}
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

指针-指针方法

 #include 
 #include 


size_t my_strlen(const char* arr,int sz)//指针-指针
{
    assert(arr);
    char* right = arr + sz - 1;
    return right - arr;
}
 
int main()
{
    char arr[] = "hello";
    int sz = sizeof(arr) / sizeof(arr[0]);
    int len=my_strlen(arr,sz);
    printf("%d", len);
    return 0;
}

1.3 strcpy 字符串拷贝

字符串拷贝,把源字符串拷贝放到目标空间字符串中,其中需要注意的事项

1.源字符串必须以 '\0' 结束。


char arr1[20] = {0};
char arr2[] = {'a','b','c'};//程序崩溃,没有char arr1[4] = "x";
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

2.会将源字符串中的 '\0' 拷贝到目标空间。


#include 
#include 
#include 

int main()
{
	
	char arr1[] = "XXXXXXXXXXXX";//arr1指向的是常量字符串,常量是不可修改的
	char arr2[] = "abcdef";
	strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

3.目标空间必须足够大,以确保能存放源字符串。


strcpy不管空间够不够,只要你敢写它就敢放,哪怕程序崩溃

char* arr1 = "qwertyuiop";//arr1指向的是常量字符串,常量是不可修改的
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

4.目标空间必须可变。


1.4 模拟实现strcpy

char* my_strcpy(char*dest, const char* src) { assert(src && dest); char* ret = dest; while(*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = { 0 }; char* arr2 = "hello bit"; printf("%s\n", my_strcpy(arr1, arr2)); return 0; } strcpy函数返回的是目标空间的起始地址 strcpy函数的返回类型的设置是为了实现链式访问
1.5 strcat 字符串追加

int main() { char arr1[10] = "hello " ; char* arr2 = "bit"; printf("%s\n", strcat(arr1, arr2)); return 0; }

int main()
{
	char arr1[20] =  "hello
1.6 模拟实现strcat
XXXXX" ; char arr2[] = "bit"; printf("%s\n", strcat(arr1, arr2)); return 0; }

注意事项:

1.源字符串必须以 '\0' 结束。


从\0位置开始追加

char* my_strcat(char* dest, char* src)
{
	assert(dest && src);
	char* ret = dest;
	//找目标空间中的
1.7 strcmp 比较字符串
while (*dest) { dest++; } //拷贝 while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = "hello"; char arr2[] = " bit"; printf("%s\n", my_strcat(arr1, arr2)); return 0; }

2.目标空间必须有足够的大,能容纳下源字符串的内容。


3.目标空间必须可修改。


strcmp函数比较的不是字符串的长度

而是比较字符串中对应位置上的字符的大小(ASCII码值),如果相同,就比较下一对儿,直到不同或者都遇到
1.8 模拟实现strcmp
2  长度受限制的字符串函数
的ASCII码值是0
2.1 strncpy

字符串自己给自己追加,如何?

我们所写的模拟函数无法完成自己追加自己,也不建议这么使用


char *strncpy( char *strDest, const char *strSource, size_t count ); int main() { char arr1[] = "abcdef"; char arr2[] = "qwewwwwww"; strncpy(arr1, arr2, 5); printf("%s\n", arr1); return 0; }

 标准规定:

第一个字符串大于第二个字符串,则返回大于0的数字

第一个字符串等于第二个字符串,则返回0

第一个字符串小于第二个字符串,则返回小于0的数字


int main() { char arr1[] = "abcdef"; char arr2[] = "qwe"; strncpy(arr1, arr2, 5);//不够默认补
2.2 strncat
printf("%s\n", arr1); return 0; }
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 arr1[] = "abcd";
	char arr2[] = "abdc";
	int ret = my_strcmp(arr1, arr2);
	if (ret >0)
	{
		printf(">\n");
	}
	else if (ret == 0)
	{
		printf("== \n");
	}
	else
	{
		printf("<\n");
	}
	printf("%d\n", ret);
	return 0;
}

int main() { char arr1[20] = "abcdef
2.3 strncmp
XXXXX"; char arr2[] = "qwe"; strncat(arr1, arr2, 3);//追加三个,还会再把int main() { char arr1[] = "abcdef"; char arr2[] = "abcdq"; int ret = strncmp(arr1, arr2, 4);//相等 printf("%d\n", ret); return 0; } 放进去 printf("%s\n", arr1); return 0; }
2.4 strstr  在一个字符串中找另一个字符串

 拷贝num个字符从源字符串到目标空间。


如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。


char * strstr ( const char *str1, const char * str2);
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "de";
	char * p=strstr(arr1, arr2);
	if (p == NULL)//strstr找不到返回NULL指针,我们需要判断
	{
		printf("找不到");
	}
	else
	{
		printf("%s ", p);
	}
	return 0;
}

2.5 模拟实现strstr


char* my_strstr(const char* str1, const char* str2) { assert(str1 && str2); const char* s1 = str1; const char* s2 = str2; const char* cur = str1; while (*cur)//cur不等于
2.6 strtok
进来 { s1 = cur;//判断失败返回cur指向的位置 s2 = str2;//判断失败回到起始位置 while (*s1 && *s2 && (*s1 == *s2))//两个字符串都被查找完,没有数据了 { s1++; s2++; } if (*s2 == 'char * strtok ( char * str, const char * sep );')//字串找到,返回记录地址 { return (char*)cur; } cur++;//匹配不成功,指向下一步 } return NULL;//找不到 } int main() { char arr1[] = "abbbcdef"; char arr2[] = "bbc"; char* ret = my_strstr(arr1, arr2); if (NULL == ret) { printf("找不到子串\n"); } else { printf("%s\n", ret); } return 0; }

 追加num个字符从源字符串到目标空间。


int main()
{
	char arr[] = "lanyangyang@landawang.cunba";
  //char arr[] = "lanyangyang
2.7 strerror  perror  返回错误码,所对应的错误信息
landawangchar * strerror ( int errnum );cunba"; strtok函数会把数组变成这样 char buf[50] = { 0 }; const char* sep = "@. "; strcpy(buf, arr); //printf("%s\n", strtok(buf, sep));//只找第一个标记 //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找 //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找 优化 char* str = NULL; for (str=strtok(buf, sep); str!=NULL; str=strtok(NULL, sep)) { printf("%s\n", str); } return 0; }


2.8 字符分类函数

函数


如果他的参数符合下列条件就返回真
iscntrl

 判断str2是不是str1的子串,如果str2在str1中出现,返回在str1中第一次出现的地址

如果没有出现,返回空指针

任何控制字符

sspace

 思路:只需要找的子串如果复杂,我们需要三个指针协助

s1指针指向str1,s2指针指向str2。


cur指针指向str1,作用是记录开始匹配的地址

如果两个字符串对应位置不相等,str1往后走

如果相等,开始匹配,我们应该记住开始匹配的str1位置,因为有可能相等,有可能不相等

如果一直到\0结束,str2就是str1子串

如果不相等,重新找到记录位置的地址,往后+1.往后走重新开始匹配,其中str2指针重新指向数组起始位置地址

空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t'或者垂直制表符'\v'

isdigit

作用:指定分隔符,让数组分段

十进制数字 0~9

 sep参数是个字符串,定义了用作分隔符的字符集合

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


strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。


(注: strtok函数会改变被 *** 作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。


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


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


如果字符串中不存在更多的标记,则返回 NULL 指针。


isxdigit


十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F
islower

 库函数在使用失败的时候,会留下错误码errno(全局变量),类似网站404错误码

strerror就是翻译错误信息

#include 
#include 
#include 
#include 

int main()
{
	printf("%s\n", strerror(0));
	printf("%s\n", strerror(1));
	printf("%s\n", strerror(2));
	printf("%s\n", strerror(3));
	int* p = (int*)malloc(INT_MAX);//想堆区申请内存的
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		perror("Malloc");//perror是打印错误信息,strerror是把错误码转换成错误信息
 //只需要写字符串,然后输出错误码对应得错误信息,更加方便
		return 1;
	}
	return 0;
}

返回错误码所对应错误信息字符串首字符地址

 


小写字母a~z int tolower ( int c ); int toupper ( int c ); int main() { char ch = 'A'; putchar(toupper(ch)); putchar(tolower(ch)); return 0; }
3. 内存 *** 作函数
3.1 memcpy 内存空间数据拷贝
void * memcpy ( void * destination, const void * source, size_t count );int main() { int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 }; int arr2[5] = { 0 }; return 0; memcpy(arr2,arr1,20);//拷贝20个字节 }
3.2 模拟实现memcpy
3.3 memmove  可以实现重叠内存拷贝
void *memmove( void *dest, const void *src, size_t count );1
3.4  模拟实现memmove
3.5 memcpy  内存字节对应比较
isupper大写字母A~Z
isalpha字母a~z或A~Z
isalnum字母或者数字,a~z,A~Z,0~9
ispunct标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph任何图形字符
sprint任何可打印字符,包括图形字符和空白字符

 

 举例:isdigit接收字符的ASCII码值,返回int类型(如果是数字字符返回非0,不是数组字符返回0)

#include 

int main()
{
int ret = isdigit('5');//5
int ret = isdigit('Q');//0
printf("%d\n", ret);

return 0;
}
char ch = 'A';

	if (ch >= 'a' && ch <= 'z')
	{

	}

这样写很麻烦,我们一个函数搞定

int ret = islower(ch);//判断是否小写,是小写字母返回非0,否则返回0,快速判断

字符转换:

int memcmp ( const void * ptr1, 
 const void * ptr2, 
 size_t num );//比较的字节个数
 int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3,4,0x11223305 };
	int ret = memcmp(arr1, arr2, 18);
	printf("%d\n", ret);

	return 0;
}


3.6  memset  以字节为单位进行内存设置
void *memset( void *dest, int c, size_t count );//目的空间,设置的字符,字符个数
 int main()
{
	int arr[] = { 0x11111111,0x22222222,3,4,5 };
	memset(arr, 6, 20);//memset是以字节为单位来初始化内存单元的
	return 0;
}

注意事项:

1.函数memcpy从source的位置开始向后复制count个字节的数据到destination的内存位置。


2.这个函数在遇到 '\0' 的时候并不会停下来。


3.如果source和destination有任何的重叠,复制的结果都是未定义的。


4.返回目的地的起始地址

 

思路:  

1. 作者实现memcpy函数的时候,作者不知道你要拷贝什么数据,类似qsort 

2.拷贝时应该根据数据类型强制类型转换,一个字节一个字节拷贝

#include 
#include 
#include 

//void* my_memcpy(void* dest, const void* src, size_t count)
//{
//	assert(src && dest);
//	while (count--)
//	{
//		*(char*)dest = *(char*)src;
//		dest = (char*)dest + 1;
//		src = (char*)src + 1;
//	}
// }

void* my_memcpy(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	while (count--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}

	return ret;
}


int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[5] = { 0 };
	my_memcpy(arr2, arr1, 20);



    int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };   
    // 1 2 1 2 3 4 5 8 9 10 期望的结果
	my_memcpy(arr1+2, arr1, 20);

	return 0;
}

但是当我们想拷贝同一块空间的数据时,数据却发生错误

 原因在于数据覆盖了我们想要拷贝的空间

 在同一块内存拷贝,目标和源头数据空间有交叉,我们应该用memmove


 

#include 

int main()
{
		int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
		memmove(arr1 + 2, arr1, 20);
	return 0;
}

 

思路:

把34567拷贝放到12345,正着放不会覆盖数据(dest

 而想把34567拷贝放到45678,正着放会覆盖数据,我们可以先把7放到8上,6放到7,5放到6...从后往前拷贝数据,这样数据就不会被覆盖

 总结:当需要拷贝的地址dest > src地址,从后向前拷贝 ;dest < src时,从前向后拷贝

当dest和src空间无交集,前后关系无所谓,我们这里默认从后向前拷贝(方便)

{
	if (dest > src)
	{
		;  //从后向前拷贝
	}
	else
	{
		;  //从前向后拷贝
	}
}

也可以换成另一种写法

if (dest > src && dest<((char*)src+count))
	{
		;//从后向前拷贝
	}
	else
	{
		;//从前向后拷贝
	}

代码思路:从前向后的代码就是memcpy模拟,从后向前,我们需要+20个字节来到dest和src末尾

void* my_memmove(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	if (dest > src)
	{
		while (count--)
		{
	*((char*)dest + count) =  *((char*)src + count);  //count=19,正好指向最后一个字节
		}
		//从后向前拷贝
	}
	else
	{
			void* ret = dest;
			while (count--)
			{
				*(char*)dest = *(char*)src;
				dest = (char*)dest + 1;
				src = (char*)src + 1;
			}
		 //从前向后拷贝
	}
	return ret;
}


	int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr1 + 2, arr1, 20);
	//my_memmove(arr1 , arr1+2, 20);
	return 0;
}

[+++]

 

[+++]


[+++]
[+++]

[+++]

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 88, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

目录

C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。


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

1.1 strlen

1.2 三种方式模拟实现strlen库函数

1.3 strcpy 字符串拷贝

1.4 模拟实现strcpy

strcpy函数返回的是目标空间的起始地址

strcpy函数的返回类型的设置是为了实现链式访问

1.5 strcat 字符串追加

1.6 模拟实现strcat

1.7 strcmp 比较字符串

1.8 模拟实现strcmp

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

2.1 strncpy

2.2 strncat

2.3 strncmp

2.4 strstr  在一个字符串中找另一个字符串

2.5 模拟实现strstr

2.6 strtok

2.7 strerror  perror  返回错误码,所对应的错误信息

2.8 字符分类函数

3. 内存 *** 作函数

3.1 memcpy 内存空间数据拷贝

3.2 模拟实现memcpy

 在同一块内存拷贝,目标和源头数据空间有交叉,我们应该用memmove

3.3 memmove  可以实现重叠内存拷贝

3.4  模拟实现memmove

3.5 memcpy  内存字节对应比较

3.6  memset  以字节为单位进行内存设置


C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。


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

1.1 strlen

size_t strlen ( const char * str );//函数原型,注意函数的返回值为size_t

字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' )。


char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };//无法用strlen求字符串长度

char arr[10] = { 'a', 'b', 'c', 'd', 'e', 'f' };//限定长度,可以求

 下面结果是什么?

 if ((int)strlen("abc") - (int)strlen("qwerty") > 0)
	{
		printf(">\n");
	}
	else
	{
		printf("<=\n");
	}

答案:>,两个无符号数相减结果是正数


1.2 三种方式模拟实现strlen库函数

通常写法

 #include 
 #include 

size_t my_strlen(const char* str)
{
	int count = 0;//统计字符的个数
    assert(str);
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}
 
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

递归

 #include 
 #include 

//my_strlen("abcdef")
//1+my_strlen("bcdef")
//1+1+my_strlen("cdef")
//1+1+1+ my_strlen("def")
//1+1+1+1+ my_strlen("ef")
//1 + 1 + 1 + 1 +1+my_strlen("f")
//1 + 1 + 1 + 1 + 1 + 1+ my_strlen("")
//1 + 1 + 1 + 1 + 1 + 1 + 0 = 6
 
size_t my_strlen(const char* str)
{
    assert(str);
	if (*str != '\0')
		return 1 + my_strlen(str+1);
	else
		return 0;
}
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

指针-指针方法

 #include 
 #include 


size_t my_strlen(const char* arr,int sz)//指针-指针
{
    assert(arr);
    char* right = arr + sz - 1;
    return right - arr;
}
 
int main()
{
    char arr[] = "hello";
    int sz = sizeof(arr) / sizeof(arr[0]);
    int len=my_strlen(arr,sz);
    printf("%d", len);
    return 0;
}

1.3 strcpy 字符串拷贝

字符串拷贝,把源字符串拷贝放到目标空间字符串中,其中需要注意的事项

1.源字符串必须以 '\0' 结束。


char arr1[20] = {0};
char arr2[] = {'a','b','c'};//程序崩溃,没有char arr1[4] = "x";
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

2.会将源字符串中的 '\0' 拷贝到目标空间。


#include 
#include 
#include 

int main()
{
	
	char arr1[] = "XXXXXXXXXXXX";//arr1指向的是常量字符串,常量是不可修改的
	char arr2[] = "abcdef";
	strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

3.目标空间必须足够大,以确保能存放源字符串。


strcpy不管空间够不够,只要你敢写它就敢放,哪怕程序崩溃

char* arr1 = "qwertyuiop";//arr1指向的是常量字符串,常量是不可修改的
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

4.目标空间必须可变。


1.4 模拟实现strcpy

char* my_strcpy(char*dest, const char* src) { assert(src && dest); char* ret = dest; while(*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = { 0 }; char* arr2 = "hello bit"; printf("%s\n", my_strcpy(arr1, arr2)); return 0; } strcpy函数返回的是目标空间的起始地址 strcpy函数的返回类型的设置是为了实现链式访问
1.5 strcat 字符串追加

int main() { char arr1[10] = "hello " ; char* arr2 = "bit"; printf("%s\n", strcat(arr1, arr2)); return 0; }

int main()
{
	char arr1[20] =  "hello
1.6 模拟实现strcat
XXXXX" ; char arr2[] = "bit"; printf("%s\n", strcat(arr1, arr2)); return 0; }

注意事项:

1.源字符串必须以 '\0' 结束。


从\0位置开始追加

char* my_strcat(char* dest, char* src)
{
	assert(dest && src);
	char* ret = dest;
	//找目标空间中的
1.7 strcmp 比较字符串
while (*dest) { dest++; } //拷贝 while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = "hello"; char arr2[] = " bit"; printf("%s\n", my_strcat(arr1, arr2)); return 0; }

2.目标空间必须有足够的大,能容纳下源字符串的内容。


3.目标空间必须可修改。


strcmp函数比较的不是字符串的长度

而是比较字符串中对应位置上的字符的大小(ASCII码值),如果相同,就比较下一对儿,直到不同或者都遇到
1.8 模拟实现strcmp
2  长度受限制的字符串函数
的ASCII码值是0
2.1 strncpy

字符串自己给自己追加,如何?

我们所写的模拟函数无法完成自己追加自己,也不建议这么使用


char *strncpy( char *strDest, const char *strSource, size_t count ); int main() { char arr1[] = "abcdef"; char arr2[] = "qwewwwwww"; strncpy(arr1, arr2, 5); printf("%s\n", arr1); return 0; }

 标准规定:

第一个字符串大于第二个字符串,则返回大于0的数字

第一个字符串等于第二个字符串,则返回0

第一个字符串小于第二个字符串,则返回小于0的数字


int main() { char arr1[] = "abcdef"; char arr2[] = "qwe"; strncpy(arr1, arr2, 5);//不够默认补
2.2 strncat
printf("%s\n", arr1); return 0; }
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 arr1[] = "abcd";
	char arr2[] = "abdc";
	int ret = my_strcmp(arr1, arr2);
	if (ret >0)
	{
		printf(">\n");
	}
	else if (ret == 0)
	{
		printf("== \n");
	}
	else
	{
		printf("<\n");
	}
	printf("%d\n", ret);
	return 0;
}

int main() { char arr1[20] = "abcdef
2.3 strncmp
XXXXX"; char arr2[] = "qwe"; strncat(arr1, arr2, 3);//追加三个,还会再把int main() { char arr1[] = "abcdef"; char arr2[] = "abcdq"; int ret = strncmp(arr1, arr2, 4);//相等 printf("%d\n", ret); return 0; } 放进去 printf("%s\n", arr1); return 0; }
2.4 strstr  在一个字符串中找另一个字符串

 拷贝num个字符从源字符串到目标空间。


如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。


char * strstr ( const char *str1, const char * str2);
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "de";
	char * p=strstr(arr1, arr2);
	if (p == NULL)//strstr找不到返回NULL指针,我们需要判断
	{
		printf("找不到");
	}
	else
	{
		printf("%s ", p);
	}
	return 0;
}

2.5 模拟实现strstr


char* my_strstr(const char* str1, const char* str2) { assert(str1 && str2); const char* s1 = str1; const char* s2 = str2; const char* cur = str1; while (*cur)//cur不等于
2.6 strtok
进来 { s1 = cur;//判断失败返回cur指向的位置 s2 = str2;//判断失败回到起始位置 while (*s1 && *s2 && (*s1 == *s2))//两个字符串都被查找完,没有数据了 { s1++; s2++; } if (*s2 == 'char * strtok ( char * str, const char * sep );')//字串找到,返回记录地址 { return (char*)cur; } cur++;//匹配不成功,指向下一步 } return NULL;//找不到 } int main() { char arr1[] = "abbbcdef"; char arr2[] = "bbc"; char* ret = my_strstr(arr1, arr2); if (NULL == ret) { printf("找不到子串\n"); } else { printf("%s\n", ret); } return 0; }

 追加num个字符从源字符串到目标空间。


int main()
{
	char arr[] = "lanyangyang@landawang.cunba";
  //char arr[] = "lanyangyang
2.7 strerror  perror  返回错误码,所对应的错误信息
landawangchar * strerror ( int errnum );cunba"; strtok函数会把数组变成这样 char buf[50] = { 0 }; const char* sep = "@. "; strcpy(buf, arr); //printf("%s\n", strtok(buf, sep));//只找第一个标记 //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找 //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找 优化 char* str = NULL; for (str=strtok(buf, sep); str!=NULL; str=strtok(NULL, sep)) { printf("%s\n", str); } return 0; }


2.8 字符分类函数

函数


如果他的参数符合下列条件就返回真
iscntrl

 判断str2是不是str1的子串,如果str2在str1中出现,返回在str1中第一次出现的地址

如果没有出现,返回空指针

任何控制字符

sspace

 思路:只需要找的子串如果复杂,我们需要三个指针协助

s1指针指向str1,s2指针指向str2。


cur指针指向str1,作用是记录开始匹配的地址

如果两个字符串对应位置不相等,str1往后走

如果相等,开始匹配,我们应该记住开始匹配的str1位置,因为有可能相等,有可能不相等

如果一直到\0结束,str2就是str1子串

如果不相等,重新找到记录位置的地址,往后+1.往后走重新开始匹配,其中str2指针重新指向数组起始位置地址

空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t'或者垂直制表符'\v'

isdigit

作用:指定分隔符,让数组分段

十进制数字 0~9

 sep参数是个字符串,定义了用作分隔符的字符集合

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


strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。


(注: strtok函数会改变被 *** 作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。


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


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


如果字符串中不存在更多的标记,则返回 NULL 指针。


isxdigit


十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F
islower

 库函数在使用失败的时候,会留下错误码errno(全局变量),类似网站404错误码

strerror就是翻译错误信息

#include 
#include 
#include 
#include 

int main()
{
	printf("%s\n", strerror(0));
	printf("%s\n", strerror(1));
	printf("%s\n", strerror(2));
	printf("%s\n", strerror(3));
	int* p = (int*)malloc(INT_MAX);//想堆区申请内存的
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		perror("Malloc");//perror是打印错误信息,strerror是把错误码转换成错误信息
 //只需要写字符串,然后输出错误码对应得错误信息,更加方便
		return 1;
	}
	return 0;
}

返回错误码所对应错误信息字符串首字符地址

 


小写字母a~z int tolower ( int c ); int toupper ( int c ); int main() { char ch = 'A'; putchar(toupper(ch)); putchar(tolower(ch)); return 0; }
3. 内存 *** 作函数
3.1 memcpy 内存空间数据拷贝
void * memcpy ( void * destination, const void * source, size_t count );int main() { int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 }; int arr2[5] = { 0 }; return 0; memcpy(arr2,arr1,20);//拷贝20个字节 }
3.2 模拟实现memcpy
3.3 memmove  可以实现重叠内存拷贝
void *memmove( void *dest, const void *src, size_t count );1
3.4  模拟实现memmove
3.5 memcpy  内存字节对应比较
isupper大写字母A~Z
isalpha字母a~z或A~Z
isalnum字母或者数字,a~z,A~Z,0~9
ispunct标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph任何图形字符
sprint任何可打印字符,包括图形字符和空白字符

 

 举例:isdigit接收字符的ASCII码值,返回int类型(如果是数字字符返回非0,不是数组字符返回0)

#include 

int main()
{
int ret = isdigit('5');//5
int ret = isdigit('Q');//0
printf("%d\n", ret);

return 0;
}
char ch = 'A';

	if (ch >= 'a' && ch <= 'z')
	{

	}

这样写很麻烦,我们一个函数搞定

int ret = islower(ch);//判断是否小写,是小写字母返回非0,否则返回0,快速判断

字符转换:

int memcmp ( const void * ptr1, 
 const void * ptr2, 
 size_t num );//比较的字节个数
 int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3,4,0x11223305 };
	int ret = memcmp(arr1, arr2, 18);
	printf("%d\n", ret);

	return 0;
}


3.6  memset  以字节为单位进行内存设置
void *memset( void *dest, int c, size_t count );//目的空间,设置的字符,字符个数
 int main()
{
	int arr[] = { 0x11111111,0x22222222,3,4,5 };
	memset(arr, 6, 20);//memset是以字节为单位来初始化内存单元的
	return 0;
}

注意事项:

1.函数memcpy从source的位置开始向后复制count个字节的数据到destination的内存位置。


2.这个函数在遇到 '\0' 的时候并不会停下来。


3.如果source和destination有任何的重叠,复制的结果都是未定义的。


4.返回目的地的起始地址

 

思路:  

1. 作者实现memcpy函数的时候,作者不知道你要拷贝什么数据,类似qsort 

2.拷贝时应该根据数据类型强制类型转换,一个字节一个字节拷贝

#include 
#include 
#include 

//void* my_memcpy(void* dest, const void* src, size_t count)
//{
//	assert(src && dest);
//	while (count--)
//	{
//		*(char*)dest = *(char*)src;
//		dest = (char*)dest + 1;
//		src = (char*)src + 1;
//	}
// }

void* my_memcpy(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	while (count--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}

	return ret;
}


int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[5] = { 0 };
	my_memcpy(arr2, arr1, 20);



    int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };   
    // 1 2 1 2 3 4 5 8 9 10 期望的结果
	my_memcpy(arr1+2, arr1, 20);

	return 0;
}

但是当我们想拷贝同一块空间的数据时,数据却发生错误

 原因在于数据覆盖了我们想要拷贝的空间

 在同一块内存拷贝,目标和源头数据空间有交叉,我们应该用memmove


 

#include 

int main()
{
		int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
		memmove(arr1 + 2, arr1, 20);
	return 0;
}

 

思路:

把34567拷贝放到12345,正着放不会覆盖数据(dest

 而想把34567拷贝放到45678,正着放会覆盖数据,我们可以先把7放到8上,6放到7,5放到6...从后往前拷贝数据,这样数据就不会被覆盖

 总结:当需要拷贝的地址dest > src地址,从后向前拷贝 ;dest < src时,从前向后拷贝

当dest和src空间无交集,前后关系无所谓,我们这里默认从后向前拷贝(方便)

{
	if (dest > src)
	{
		;  //从后向前拷贝
	}
	else
	{
		;  //从前向后拷贝
	}
}

也可以换成另一种写法

if (dest > src && dest<((char*)src+count))
	{
		;//从后向前拷贝
	}
	else
	{
		;//从前向后拷贝
	}

代码思路:从前向后的代码就是memcpy模拟,从后向前,我们需要+20个字节来到dest和src末尾

void* my_memmove(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	if (dest > src)
	{
		while (count--)
		{
	*((char*)dest + count) =  *((char*)src + count);  //count=19,正好指向最后一个字节
		}
		//从后向前拷贝
	}
	else
	{
			void* ret = dest;
			while (count--)
			{
				*(char*)dest = *(char*)src;
				dest = (char*)dest + 1;
				src = (char*)src + 1;
			}
		 //从前向后拷贝
	}
	return ret;
}


	int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr1 + 2, arr1, 20);
	//my_memmove(arr1 , arr1+2, 20);
	return 0;
}

 

 

[+++]


[+++]
[+++]

[+++]

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 89, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

目录

C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。


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

1.1 strlen

1.2 三种方式模拟实现strlen库函数

1.3 strcpy 字符串拷贝

1.4 模拟实现strcpy

strcpy函数返回的是目标空间的起始地址

strcpy函数的返回类型的设置是为了实现链式访问

1.5 strcat 字符串追加

1.6 模拟实现strcat

1.7 strcmp 比较字符串

1.8 模拟实现strcmp

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

2.1 strncpy

2.2 strncat

2.3 strncmp

2.4 strstr  在一个字符串中找另一个字符串

2.5 模拟实现strstr

2.6 strtok

2.7 strerror  perror  返回错误码,所对应的错误信息

2.8 字符分类函数

3. 内存 *** 作函数

3.1 memcpy 内存空间数据拷贝

3.2 模拟实现memcpy

 在同一块内存拷贝,目标和源头数据空间有交叉,我们应该用memmove

3.3 memmove  可以实现重叠内存拷贝

3.4  模拟实现memmove

3.5 memcpy  内存字节对应比较

3.6  memset  以字节为单位进行内存设置


C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。


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

1.1 strlen

size_t strlen ( const char * str );//函数原型,注意函数的返回值为size_t

字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' )。


char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };//无法用strlen求字符串长度

char arr[10] = { 'a', 'b', 'c', 'd', 'e', 'f' };//限定长度,可以求

 下面结果是什么?

 if ((int)strlen("abc") - (int)strlen("qwerty") > 0)
	{
		printf(">\n");
	}
	else
	{
		printf("<=\n");
	}

答案:>,两个无符号数相减结果是正数


1.2 三种方式模拟实现strlen库函数

通常写法

 #include 
 #include 

size_t my_strlen(const char* str)
{
	int count = 0;//统计字符的个数
    assert(str);
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}
 
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

递归

 #include 
 #include 

//my_strlen("abcdef")
//1+my_strlen("bcdef")
//1+1+my_strlen("cdef")
//1+1+1+ my_strlen("def")
//1+1+1+1+ my_strlen("ef")
//1 + 1 + 1 + 1 +1+my_strlen("f")
//1 + 1 + 1 + 1 + 1 + 1+ my_strlen("")
//1 + 1 + 1 + 1 + 1 + 1 + 0 = 6
 
size_t my_strlen(const char* str)
{
    assert(str);
	if (*str != '\0')
		return 1 + my_strlen(str+1);
	else
		return 0;
}
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

指针-指针方法

 #include 
 #include 


size_t my_strlen(const char* arr,int sz)//指针-指针
{
    assert(arr);
    char* right = arr + sz - 1;
    return right - arr;
}
 
int main()
{
    char arr[] = "hello";
    int sz = sizeof(arr) / sizeof(arr[0]);
    int len=my_strlen(arr,sz);
    printf("%d", len);
    return 0;
}

1.3 strcpy 字符串拷贝

字符串拷贝,把源字符串拷贝放到目标空间字符串中,其中需要注意的事项

1.源字符串必须以 '\0' 结束。


char arr1[20] = {0};
char arr2[] = {'a','b','c'};//程序崩溃,没有char arr1[4] = "x";
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

2.会将源字符串中的 '\0' 拷贝到目标空间。


#include 
#include 
#include 

int main()
{
	
	char arr1[] = "XXXXXXXXXXXX";//arr1指向的是常量字符串,常量是不可修改的
	char arr2[] = "abcdef";
	strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

3.目标空间必须足够大,以确保能存放源字符串。


strcpy不管空间够不够,只要你敢写它就敢放,哪怕程序崩溃

char* arr1 = "qwertyuiop";//arr1指向的是常量字符串,常量是不可修改的
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

4.目标空间必须可变。


1.4 模拟实现strcpy

char* my_strcpy(char*dest, const char* src) { assert(src && dest); char* ret = dest; while(*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = { 0 }; char* arr2 = "hello bit"; printf("%s\n", my_strcpy(arr1, arr2)); return 0; } strcpy函数返回的是目标空间的起始地址 strcpy函数的返回类型的设置是为了实现链式访问
1.5 strcat 字符串追加

int main() { char arr1[10] = "hello " ; char* arr2 = "bit"; printf("%s\n", strcat(arr1, arr2)); return 0; }

int main()
{
	char arr1[20] =  "hello
1.6 模拟实现strcat
XXXXX" ; char arr2[] = "bit"; printf("%s\n", strcat(arr1, arr2)); return 0; }

注意事项:

1.源字符串必须以 '\0' 结束。


从\0位置开始追加

char* my_strcat(char* dest, char* src)
{
	assert(dest && src);
	char* ret = dest;
	//找目标空间中的
1.7 strcmp 比较字符串
while (*dest) { dest++; } //拷贝 while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = "hello"; char arr2[] = " bit"; printf("%s\n", my_strcat(arr1, arr2)); return 0; }

2.目标空间必须有足够的大,能容纳下源字符串的内容。


3.目标空间必须可修改。


strcmp函数比较的不是字符串的长度

而是比较字符串中对应位置上的字符的大小(ASCII码值),如果相同,就比较下一对儿,直到不同或者都遇到
1.8 模拟实现strcmp
2  长度受限制的字符串函数
的ASCII码值是0
2.1 strncpy

字符串自己给自己追加,如何?

我们所写的模拟函数无法完成自己追加自己,也不建议这么使用


char *strncpy( char *strDest, const char *strSource, size_t count ); int main() { char arr1[] = "abcdef"; char arr2[] = "qwewwwwww"; strncpy(arr1, arr2, 5); printf("%s\n", arr1); return 0; }

 标准规定:

第一个字符串大于第二个字符串,则返回大于0的数字

第一个字符串等于第二个字符串,则返回0

第一个字符串小于第二个字符串,则返回小于0的数字


int main() { char arr1[] = "abcdef"; char arr2[] = "qwe"; strncpy(arr1, arr2, 5);//不够默认补
2.2 strncat
printf("%s\n", arr1); return 0; }
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 arr1[] = "abcd";
	char arr2[] = "abdc";
	int ret = my_strcmp(arr1, arr2);
	if (ret >0)
	{
		printf(">\n");
	}
	else if (ret == 0)
	{
		printf("== \n");
	}
	else
	{
		printf("<\n");
	}
	printf("%d\n", ret);
	return 0;
}

int main() { char arr1[20] = "abcdef
2.3 strncmp
XXXXX"; char arr2[] = "qwe"; strncat(arr1, arr2, 3);//追加三个,还会再把int main() { char arr1[] = "abcdef"; char arr2[] = "abcdq"; int ret = strncmp(arr1, arr2, 4);//相等 printf("%d\n", ret); return 0; } 放进去 printf("%s\n", arr1); return 0; }
2.4 strstr  在一个字符串中找另一个字符串

 拷贝num个字符从源字符串到目标空间。


如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。


char * strstr ( const char *str1, const char * str2);
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "de";
	char * p=strstr(arr1, arr2);
	if (p == NULL)//strstr找不到返回NULL指针,我们需要判断
	{
		printf("找不到");
	}
	else
	{
		printf("%s ", p);
	}
	return 0;
}

2.5 模拟实现strstr


char* my_strstr(const char* str1, const char* str2) { assert(str1 && str2); const char* s1 = str1; const char* s2 = str2; const char* cur = str1; while (*cur)//cur不等于
2.6 strtok
进来 { s1 = cur;//判断失败返回cur指向的位置 s2 = str2;//判断失败回到起始位置 while (*s1 && *s2 && (*s1 == *s2))//两个字符串都被查找完,没有数据了 { s1++; s2++; } if (*s2 == 'char * strtok ( char * str, const char * sep );')//字串找到,返回记录地址 { return (char*)cur; } cur++;//匹配不成功,指向下一步 } return NULL;//找不到 } int main() { char arr1[] = "abbbcdef"; char arr2[] = "bbc"; char* ret = my_strstr(arr1, arr2); if (NULL == ret) { printf("找不到子串\n"); } else { printf("%s\n", ret); } return 0; }

 追加num个字符从源字符串到目标空间。


int main()
{
	char arr[] = "lanyangyang@landawang.cunba";
  //char arr[] = "lanyangyang
2.7 strerror  perror  返回错误码,所对应的错误信息
landawangchar * strerror ( int errnum );cunba"; strtok函数会把数组变成这样 char buf[50] = { 0 }; const char* sep = "@. "; strcpy(buf, arr); //printf("%s\n", strtok(buf, sep));//只找第一个标记 //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找 //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找 优化 char* str = NULL; for (str=strtok(buf, sep); str!=NULL; str=strtok(NULL, sep)) { printf("%s\n", str); } return 0; }


2.8 字符分类函数

函数


如果他的参数符合下列条件就返回真
iscntrl

 判断str2是不是str1的子串,如果str2在str1中出现,返回在str1中第一次出现的地址

如果没有出现,返回空指针

任何控制字符

sspace

 思路:只需要找的子串如果复杂,我们需要三个指针协助

s1指针指向str1,s2指针指向str2。


cur指针指向str1,作用是记录开始匹配的地址

如果两个字符串对应位置不相等,str1往后走

如果相等,开始匹配,我们应该记住开始匹配的str1位置,因为有可能相等,有可能不相等

如果一直到\0结束,str2就是str1子串

如果不相等,重新找到记录位置的地址,往后+1.往后走重新开始匹配,其中str2指针重新指向数组起始位置地址

空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t'或者垂直制表符'\v'

isdigit

作用:指定分隔符,让数组分段

十进制数字 0~9

 sep参数是个字符串,定义了用作分隔符的字符集合

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


strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。


(注: strtok函数会改变被 *** 作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。


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


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


如果字符串中不存在更多的标记,则返回 NULL 指针。


isxdigit


十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F
islower

 库函数在使用失败的时候,会留下错误码errno(全局变量),类似网站404错误码

strerror就是翻译错误信息

#include 
#include 
#include 
#include 

int main()
{
	printf("%s\n", strerror(0));
	printf("%s\n", strerror(1));
	printf("%s\n", strerror(2));
	printf("%s\n", strerror(3));
	int* p = (int*)malloc(INT_MAX);//想堆区申请内存的
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		perror("Malloc");//perror是打印错误信息,strerror是把错误码转换成错误信息
 //只需要写字符串,然后输出错误码对应得错误信息,更加方便
		return 1;
	}
	return 0;
}

返回错误码所对应错误信息字符串首字符地址

 


小写字母a~z int tolower ( int c ); int toupper ( int c ); int main() { char ch = 'A'; putchar(toupper(ch)); putchar(tolower(ch)); return 0; }
3. 内存 *** 作函数
3.1 memcpy 内存空间数据拷贝
void * memcpy ( void * destination, const void * source, size_t count );int main() { int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 }; int arr2[5] = { 0 }; return 0; memcpy(arr2,arr1,20);//拷贝20个字节 }
3.2 模拟实现memcpy
3.3 memmove  可以实现重叠内存拷贝
void *memmove( void *dest, const void *src, size_t count );1
3.4  模拟实现memmove
3.5 memcpy  内存字节对应比较
isupper大写字母A~Z
isalpha字母a~z或A~Z
isalnum字母或者数字,a~z,A~Z,0~9
ispunct标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph任何图形字符
sprint任何可打印字符,包括图形字符和空白字符

 

 举例:isdigit接收字符的ASCII码值,返回int类型(如果是数字字符返回非0,不是数组字符返回0)

#include 

int main()
{
int ret = isdigit('5');//5
int ret = isdigit('Q');//0
printf("%d\n", ret);

return 0;
}
char ch = 'A';

	if (ch >= 'a' && ch <= 'z')
	{

	}

这样写很麻烦,我们一个函数搞定

int ret = islower(ch);//判断是否小写,是小写字母返回非0,否则返回0,快速判断

字符转换:

int memcmp ( const void * ptr1, 
 const void * ptr2, 
 size_t num );//比较的字节个数
 int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3,4,0x11223305 };
	int ret = memcmp(arr1, arr2, 18);
	printf("%d\n", ret);

	return 0;
}


3.6  memset  以字节为单位进行内存设置
void *memset( void *dest, int c, size_t count );//目的空间,设置的字符,字符个数
 int main()
{
	int arr[] = { 0x11111111,0x22222222,3,4,5 };
	memset(arr, 6, 20);//memset是以字节为单位来初始化内存单元的
	return 0;
}

注意事项:

1.函数memcpy从source的位置开始向后复制count个字节的数据到destination的内存位置。


2.这个函数在遇到 '\0' 的时候并不会停下来。


3.如果source和destination有任何的重叠,复制的结果都是未定义的。


4.返回目的地的起始地址

 

思路:  

1. 作者实现memcpy函数的时候,作者不知道你要拷贝什么数据,类似qsort 

2.拷贝时应该根据数据类型强制类型转换,一个字节一个字节拷贝

#include 
#include 
#include 

//void* my_memcpy(void* dest, const void* src, size_t count)
//{
//	assert(src && dest);
//	while (count--)
//	{
//		*(char*)dest = *(char*)src;
//		dest = (char*)dest + 1;
//		src = (char*)src + 1;
//	}
// }

void* my_memcpy(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	while (count--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}

	return ret;
}


int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[5] = { 0 };
	my_memcpy(arr2, arr1, 20);



    int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };   
    // 1 2 1 2 3 4 5 8 9 10 期望的结果
	my_memcpy(arr1+2, arr1, 20);

	return 0;
}

但是当我们想拷贝同一块空间的数据时,数据却发生错误

 原因在于数据覆盖了我们想要拷贝的空间

 在同一块内存拷贝,目标和源头数据空间有交叉,我们应该用memmove


 

#include 

int main()
{
		int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
		memmove(arr1 + 2, arr1, 20);
	return 0;
}

 

思路:

把34567拷贝放到12345,正着放不会覆盖数据(dest

 而想把34567拷贝放到45678,正着放会覆盖数据,我们可以先把7放到8上,6放到7,5放到6...从后往前拷贝数据,这样数据就不会被覆盖

 总结:当需要拷贝的地址dest > src地址,从后向前拷贝 ;dest < src时,从前向后拷贝

当dest和src空间无交集,前后关系无所谓,我们这里默认从后向前拷贝(方便)

{
	if (dest > src)
	{
		;  //从后向前拷贝
	}
	else
	{
		;  //从前向后拷贝
	}
}

也可以换成另一种写法

if (dest > src && dest<((char*)src+count))
	{
		;//从后向前拷贝
	}
	else
	{
		;//从前向后拷贝
	}

代码思路:从前向后的代码就是memcpy模拟,从后向前,我们需要+20个字节来到dest和src末尾

void* my_memmove(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	if (dest > src)
	{
		while (count--)
		{
	*((char*)dest + count) =  *((char*)src + count);  //count=19,正好指向最后一个字节
		}
		//从后向前拷贝
	}
	else
	{
			void* ret = dest;
			while (count--)
			{
				*(char*)dest = *(char*)src;
				dest = (char*)dest + 1;
				src = (char*)src + 1;
			}
		 //从前向后拷贝
	}
	return ret;
}


	int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr1 + 2, arr1, 20);
	//my_memmove(arr1 , arr1+2, 20);
	return 0;
}

 

 

 


[+++]
[+++]

[+++]

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 90, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

目录

C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。


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

1.1 strlen

1.2 三种方式模拟实现strlen库函数

1.3 strcpy 字符串拷贝

1.4 模拟实现strcpy

strcpy函数返回的是目标空间的起始地址

strcpy函数的返回类型的设置是为了实现链式访问

1.5 strcat 字符串追加

1.6 模拟实现strcat

1.7 strcmp 比较字符串

1.8 模拟实现strcmp

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

2.1 strncpy

2.2 strncat

2.3 strncmp

2.4 strstr  在一个字符串中找另一个字符串

2.5 模拟实现strstr

2.6 strtok

2.7 strerror  perror  返回错误码,所对应的错误信息

2.8 字符分类函数

3. 内存 *** 作函数

3.1 memcpy 内存空间数据拷贝

3.2 模拟实现memcpy

 在同一块内存拷贝,目标和源头数据空间有交叉,我们应该用memmove

3.3 memmove  可以实现重叠内存拷贝

3.4  模拟实现memmove

3.5 memcpy  内存字节对应比较

3.6  memset  以字节为单位进行内存设置


C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。


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

1.1 strlen

size_t strlen ( const char * str );//函数原型,注意函数的返回值为size_t

字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' )。


char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };//无法用strlen求字符串长度

char arr[10] = { 'a', 'b', 'c', 'd', 'e', 'f' };//限定长度,可以求

 下面结果是什么?

 if ((int)strlen("abc") - (int)strlen("qwerty") > 0)
	{
		printf(">\n");
	}
	else
	{
		printf("<=\n");
	}

答案:>,两个无符号数相减结果是正数


1.2 三种方式模拟实现strlen库函数

通常写法

 #include 
 #include 

size_t my_strlen(const char* str)
{
	int count = 0;//统计字符的个数
    assert(str);
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}
 
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

递归

 #include 
 #include 

//my_strlen("abcdef")
//1+my_strlen("bcdef")
//1+1+my_strlen("cdef")
//1+1+1+ my_strlen("def")
//1+1+1+1+ my_strlen("ef")
//1 + 1 + 1 + 1 +1+my_strlen("f")
//1 + 1 + 1 + 1 + 1 + 1+ my_strlen("")
//1 + 1 + 1 + 1 + 1 + 1 + 0 = 6
 
size_t my_strlen(const char* str)
{
    assert(str);
	if (*str != '\0')
		return 1 + my_strlen(str+1);
	else
		return 0;
}
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

指针-指针方法

 #include 
 #include 


size_t my_strlen(const char* arr,int sz)//指针-指针
{
    assert(arr);
    char* right = arr + sz - 1;
    return right - arr;
}
 
int main()
{
    char arr[] = "hello";
    int sz = sizeof(arr) / sizeof(arr[0]);
    int len=my_strlen(arr,sz);
    printf("%d", len);
    return 0;
}

1.3 strcpy 字符串拷贝

字符串拷贝,把源字符串拷贝放到目标空间字符串中,其中需要注意的事项

1.源字符串必须以 '\0' 结束。


char arr1[20] = {0};
char arr2[] = {'a','b','c'};//程序崩溃,没有char arr1[4] = "x";
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

2.会将源字符串中的 '\0' 拷贝到目标空间。


#include 
#include 
#include 

int main()
{
	
	char arr1[] = "XXXXXXXXXXXX";//arr1指向的是常量字符串,常量是不可修改的
	char arr2[] = "abcdef";
	strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

3.目标空间必须足够大,以确保能存放源字符串。


strcpy不管空间够不够,只要你敢写它就敢放,哪怕程序崩溃

char* arr1 = "qwertyuiop";//arr1指向的是常量字符串,常量是不可修改的
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

4.目标空间必须可变。


1.4 模拟实现strcpy

char* my_strcpy(char*dest, const char* src) { assert(src && dest); char* ret = dest; while(*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = { 0 }; char* arr2 = "hello bit"; printf("%s\n", my_strcpy(arr1, arr2)); return 0; } strcpy函数返回的是目标空间的起始地址 strcpy函数的返回类型的设置是为了实现链式访问
1.5 strcat 字符串追加

int main() { char arr1[10] = "hello " ; char* arr2 = "bit"; printf("%s\n", strcat(arr1, arr2)); return 0; }

int main()
{
	char arr1[20] =  "hello
1.6 模拟实现strcat
XXXXX" ; char arr2[] = "bit"; printf("%s\n", strcat(arr1, arr2)); return 0; }

注意事项:

1.源字符串必须以 '\0' 结束。


从\0位置开始追加

char* my_strcat(char* dest, char* src)
{
	assert(dest && src);
	char* ret = dest;
	//找目标空间中的
1.7 strcmp 比较字符串
while (*dest) { dest++; } //拷贝 while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = "hello"; char arr2[] = " bit"; printf("%s\n", my_strcat(arr1, arr2)); return 0; }

2.目标空间必须有足够的大,能容纳下源字符串的内容。


3.目标空间必须可修改。


strcmp函数比较的不是字符串的长度

而是比较字符串中对应位置上的字符的大小(ASCII码值),如果相同,就比较下一对儿,直到不同或者都遇到
1.8 模拟实现strcmp
2  长度受限制的字符串函数
的ASCII码值是0
2.1 strncpy

字符串自己给自己追加,如何?

我们所写的模拟函数无法完成自己追加自己,也不建议这么使用


char *strncpy( char *strDest, const char *strSource, size_t count ); int main() { char arr1[] = "abcdef"; char arr2[] = "qwewwwwww"; strncpy(arr1, arr2, 5); printf("%s\n", arr1); return 0; }

 标准规定:

第一个字符串大于第二个字符串,则返回大于0的数字

第一个字符串等于第二个字符串,则返回0

第一个字符串小于第二个字符串,则返回小于0的数字


int main() { char arr1[] = "abcdef"; char arr2[] = "qwe"; strncpy(arr1, arr2, 5);//不够默认补
2.2 strncat
printf("%s\n", arr1); return 0; }
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 arr1[] = "abcd";
	char arr2[] = "abdc";
	int ret = my_strcmp(arr1, arr2);
	if (ret >0)
	{
		printf(">\n");
	}
	else if (ret == 0)
	{
		printf("== \n");
	}
	else
	{
		printf("<\n");
	}
	printf("%d\n", ret);
	return 0;
}

int main() { char arr1[20] = "abcdef
2.3 strncmp
XXXXX"; char arr2[] = "qwe"; strncat(arr1, arr2, 3);//追加三个,还会再把int main() { char arr1[] = "abcdef"; char arr2[] = "abcdq"; int ret = strncmp(arr1, arr2, 4);//相等 printf("%d\n", ret); return 0; } 放进去 printf("%s\n", arr1); return 0; }
2.4 strstr  在一个字符串中找另一个字符串

 拷贝num个字符从源字符串到目标空间。


如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。


char * strstr ( const char *str1, const char * str2);
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "de";
	char * p=strstr(arr1, arr2);
	if (p == NULL)//strstr找不到返回NULL指针,我们需要判断
	{
		printf("找不到");
	}
	else
	{
		printf("%s ", p);
	}
	return 0;
}

2.5 模拟实现strstr


char* my_strstr(const char* str1, const char* str2) { assert(str1 && str2); const char* s1 = str1; const char* s2 = str2; const char* cur = str1; while (*cur)//cur不等于
2.6 strtok
进来 { s1 = cur;//判断失败返回cur指向的位置 s2 = str2;//判断失败回到起始位置 while (*s1 && *s2 && (*s1 == *s2))//两个字符串都被查找完,没有数据了 { s1++; s2++; } if (*s2 == 'char * strtok ( char * str, const char * sep );')//字串找到,返回记录地址 { return (char*)cur; } cur++;//匹配不成功,指向下一步 } return NULL;//找不到 } int main() { char arr1[] = "abbbcdef"; char arr2[] = "bbc"; char* ret = my_strstr(arr1, arr2); if (NULL == ret) { printf("找不到子串\n"); } else { printf("%s\n", ret); } return 0; }

 追加num个字符从源字符串到目标空间。


int main()
{
	char arr[] = "lanyangyang@landawang.cunba";
  //char arr[] = "lanyangyang
2.7 strerror  perror  返回错误码,所对应的错误信息
landawangchar * strerror ( int errnum );cunba"; strtok函数会把数组变成这样 char buf[50] = { 0 }; const char* sep = "@. "; strcpy(buf, arr); //printf("%s\n", strtok(buf, sep));//只找第一个标记 //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找 //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找 优化 char* str = NULL; for (str=strtok(buf, sep); str!=NULL; str=strtok(NULL, sep)) { printf("%s\n", str); } return 0; }


2.8 字符分类函数

函数


如果他的参数符合下列条件就返回真
iscntrl

 判断str2是不是str1的子串,如果str2在str1中出现,返回在str1中第一次出现的地址

如果没有出现,返回空指针

任何控制字符

sspace

 思路:只需要找的子串如果复杂,我们需要三个指针协助

s1指针指向str1,s2指针指向str2。


cur指针指向str1,作用是记录开始匹配的地址

如果两个字符串对应位置不相等,str1往后走

如果相等,开始匹配,我们应该记住开始匹配的str1位置,因为有可能相等,有可能不相等

如果一直到\0结束,str2就是str1子串

如果不相等,重新找到记录位置的地址,往后+1.往后走重新开始匹配,其中str2指针重新指向数组起始位置地址

空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t'或者垂直制表符'\v'

isdigit

作用:指定分隔符,让数组分段

十进制数字 0~9

 sep参数是个字符串,定义了用作分隔符的字符集合

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


strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。


(注: strtok函数会改变被 *** 作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。


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


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


如果字符串中不存在更多的标记,则返回 NULL 指针。


isxdigit


十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F
islower

 库函数在使用失败的时候,会留下错误码errno(全局变量),类似网站404错误码

strerror就是翻译错误信息

#include 
#include 
#include 
#include 

int main()
{
	printf("%s\n", strerror(0));
	printf("%s\n", strerror(1));
	printf("%s\n", strerror(2));
	printf("%s\n", strerror(3));
	int* p = (int*)malloc(INT_MAX);//想堆区申请内存的
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		perror("Malloc");//perror是打印错误信息,strerror是把错误码转换成错误信息
 //只需要写字符串,然后输出错误码对应得错误信息,更加方便
		return 1;
	}
	return 0;
}

返回错误码所对应错误信息字符串首字符地址

 


小写字母a~z int tolower ( int c ); int toupper ( int c ); int main() { char ch = 'A'; putchar(toupper(ch)); putchar(tolower(ch)); return 0; }
3. 内存 *** 作函数
3.1 memcpy 内存空间数据拷贝
void * memcpy ( void * destination, const void * source, size_t count );int main() { int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 }; int arr2[5] = { 0 }; return 0; memcpy(arr2,arr1,20);//拷贝20个字节 }
3.2 模拟实现memcpy
3.3 memmove  可以实现重叠内存拷贝
void *memmove( void *dest, const void *src, size_t count );1
3.4  模拟实现memmove
3.5 memcpy  内存字节对应比较
isupper大写字母A~Z
isalpha字母a~z或A~Z
isalnum字母或者数字,a~z,A~Z,0~9
ispunct标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph任何图形字符
sprint任何可打印字符,包括图形字符和空白字符

 

 举例:isdigit接收字符的ASCII码值,返回int类型(如果是数字字符返回非0,不是数组字符返回0)

#include 

int main()
{
int ret = isdigit('5');//5
int ret = isdigit('Q');//0
printf("%d\n", ret);

return 0;
}
char ch = 'A';

	if (ch >= 'a' && ch <= 'z')
	{

	}

这样写很麻烦,我们一个函数搞定

int ret = islower(ch);//判断是否小写,是小写字母返回非0,否则返回0,快速判断

字符转换:

int memcmp ( const void * ptr1, 
 const void * ptr2, 
 size_t num );//比较的字节个数
 int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3,4,0x11223305 };
	int ret = memcmp(arr1, arr2, 18);
	printf("%d\n", ret);

	return 0;
}


3.6  memset  以字节为单位进行内存设置
void *memset( void *dest, int c, size_t count );//目的空间,设置的字符,字符个数
 int main()
{
	int arr[] = { 0x11111111,0x22222222,3,4,5 };
	memset(arr, 6, 20);//memset是以字节为单位来初始化内存单元的
	return 0;
}

注意事项:

1.函数memcpy从source的位置开始向后复制count个字节的数据到destination的内存位置。


2.这个函数在遇到 '\0' 的时候并不会停下来。


3.如果source和destination有任何的重叠,复制的结果都是未定义的。


4.返回目的地的起始地址

 

思路:  

1. 作者实现memcpy函数的时候,作者不知道你要拷贝什么数据,类似qsort 

2.拷贝时应该根据数据类型强制类型转换,一个字节一个字节拷贝

#include 
#include 
#include 

//void* my_memcpy(void* dest, const void* src, size_t count)
//{
//	assert(src && dest);
//	while (count--)
//	{
//		*(char*)dest = *(char*)src;
//		dest = (char*)dest + 1;
//		src = (char*)src + 1;
//	}
// }

void* my_memcpy(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	while (count--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}

	return ret;
}


int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[5] = { 0 };
	my_memcpy(arr2, arr1, 20);



    int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };   
    // 1 2 1 2 3 4 5 8 9 10 期望的结果
	my_memcpy(arr1+2, arr1, 20);

	return 0;
}

但是当我们想拷贝同一块空间的数据时,数据却发生错误

 原因在于数据覆盖了我们想要拷贝的空间

 在同一块内存拷贝,目标和源头数据空间有交叉,我们应该用memmove


 

#include 

int main()
{
		int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
		memmove(arr1 + 2, arr1, 20);
	return 0;
}

 

思路:

把34567拷贝放到12345,正着放不会覆盖数据(dest

 而想把34567拷贝放到45678,正着放会覆盖数据,我们可以先把7放到8上,6放到7,5放到6...从后往前拷贝数据,这样数据就不会被覆盖

 总结:当需要拷贝的地址dest > src地址,从后向前拷贝 ;dest < src时,从前向后拷贝

当dest和src空间无交集,前后关系无所谓,我们这里默认从后向前拷贝(方便)

{
	if (dest > src)
	{
		;  //从后向前拷贝
	}
	else
	{
		;  //从前向后拷贝
	}
}

也可以换成另一种写法

if (dest > src && dest<((char*)src+count))
	{
		;//从后向前拷贝
	}
	else
	{
		;//从前向后拷贝
	}

代码思路:从前向后的代码就是memcpy模拟,从后向前,我们需要+20个字节来到dest和src末尾

void* my_memmove(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	if (dest > src)
	{
		while (count--)
		{
	*((char*)dest + count) =  *((char*)src + count);  //count=19,正好指向最后一个字节
		}
		//从后向前拷贝
	}
	else
	{
			void* ret = dest;
			while (count--)
			{
				*(char*)dest = *(char*)src;
				dest = (char*)dest + 1;
				src = (char*)src + 1;
			}
		 //从前向后拷贝
	}
	return ret;
}


	int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr1 + 2, arr1, 20);
	//my_memmove(arr1 , arr1+2, 20);
	return 0;
}

 

 

 


[+++]

[+++]

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 91, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

目录

C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。


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

1.1 strlen

1.2 三种方式模拟实现strlen库函数

1.3 strcpy 字符串拷贝

1.4 模拟实现strcpy

strcpy函数返回的是目标空间的起始地址

strcpy函数的返回类型的设置是为了实现链式访问

1.5 strcat 字符串追加

1.6 模拟实现strcat

1.7 strcmp 比较字符串

1.8 模拟实现strcmp

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

2.1 strncpy

2.2 strncat

2.3 strncmp

2.4 strstr  在一个字符串中找另一个字符串

2.5 模拟实现strstr

2.6 strtok

2.7 strerror  perror  返回错误码,所对应的错误信息

2.8 字符分类函数

3. 内存 *** 作函数

3.1 memcpy 内存空间数据拷贝

3.2 模拟实现memcpy

 在同一块内存拷贝,目标和源头数据空间有交叉,我们应该用memmove

3.3 memmove  可以实现重叠内存拷贝

3.4  模拟实现memmove

3.5 memcpy  内存字节对应比较

3.6  memset  以字节为单位进行内存设置


C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。


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

1.1 strlen

size_t strlen ( const char * str );//函数原型,注意函数的返回值为size_t

字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' )。


char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };//无法用strlen求字符串长度

char arr[10] = { 'a', 'b', 'c', 'd', 'e', 'f' };//限定长度,可以求

 下面结果是什么?

 if ((int)strlen("abc") - (int)strlen("qwerty") > 0)
	{
		printf(">\n");
	}
	else
	{
		printf("<=\n");
	}

答案:>,两个无符号数相减结果是正数


1.2 三种方式模拟实现strlen库函数

通常写法

 #include 
 #include 

size_t my_strlen(const char* str)
{
	int count = 0;//统计字符的个数
    assert(str);
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}
 
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

递归

 #include 
 #include 

//my_strlen("abcdef")
//1+my_strlen("bcdef")
//1+1+my_strlen("cdef")
//1+1+1+ my_strlen("def")
//1+1+1+1+ my_strlen("ef")
//1 + 1 + 1 + 1 +1+my_strlen("f")
//1 + 1 + 1 + 1 + 1 + 1+ my_strlen("")
//1 + 1 + 1 + 1 + 1 + 1 + 0 = 6
 
size_t my_strlen(const char* str)
{
    assert(str);
	if (*str != '\0')
		return 1 + my_strlen(str+1);
	else
		return 0;
}
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

指针-指针方法

 #include 
 #include 


size_t my_strlen(const char* arr,int sz)//指针-指针
{
    assert(arr);
    char* right = arr + sz - 1;
    return right - arr;
}
 
int main()
{
    char arr[] = "hello";
    int sz = sizeof(arr) / sizeof(arr[0]);
    int len=my_strlen(arr,sz);
    printf("%d", len);
    return 0;
}

1.3 strcpy 字符串拷贝

字符串拷贝,把源字符串拷贝放到目标空间字符串中,其中需要注意的事项

1.源字符串必须以 '\0' 结束。


char arr1[20] = {0};
char arr2[] = {'a','b','c'};//程序崩溃,没有char arr1[4] = "x";
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

2.会将源字符串中的 '\0' 拷贝到目标空间。


#include 
#include 
#include 

int main()
{
	
	char arr1[] = "XXXXXXXXXXXX";//arr1指向的是常量字符串,常量是不可修改的
	char arr2[] = "abcdef";
	strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

3.目标空间必须足够大,以确保能存放源字符串。


strcpy不管空间够不够,只要你敢写它就敢放,哪怕程序崩溃

char* arr1 = "qwertyuiop";//arr1指向的是常量字符串,常量是不可修改的
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

4.目标空间必须可变。


1.4 模拟实现strcpy

char* my_strcpy(char*dest, const char* src) { assert(src && dest); char* ret = dest; while(*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = { 0 }; char* arr2 = "hello bit"; printf("%s\n", my_strcpy(arr1, arr2)); return 0; } strcpy函数返回的是目标空间的起始地址 strcpy函数的返回类型的设置是为了实现链式访问
1.5 strcat 字符串追加

int main() { char arr1[10] = "hello " ; char* arr2 = "bit"; printf("%s\n", strcat(arr1, arr2)); return 0; }

int main()
{
	char arr1[20] =  "hello
1.6 模拟实现strcat
XXXXX" ; char arr2[] = "bit"; printf("%s\n", strcat(arr1, arr2)); return 0; }

注意事项:

1.源字符串必须以 '\0' 结束。


从\0位置开始追加

char* my_strcat(char* dest, char* src)
{
	assert(dest && src);
	char* ret = dest;
	//找目标空间中的
1.7 strcmp 比较字符串
while (*dest) { dest++; } //拷贝 while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = "hello"; char arr2[] = " bit"; printf("%s\n", my_strcat(arr1, arr2)); return 0; }

2.目标空间必须有足够的大,能容纳下源字符串的内容。


3.目标空间必须可修改。


strcmp函数比较的不是字符串的长度

而是比较字符串中对应位置上的字符的大小(ASCII码值),如果相同,就比较下一对儿,直到不同或者都遇到
1.8 模拟实现strcmp
2  长度受限制的字符串函数
的ASCII码值是0
2.1 strncpy

字符串自己给自己追加,如何?

我们所写的模拟函数无法完成自己追加自己,也不建议这么使用


char *strncpy( char *strDest, const char *strSource, size_t count ); int main() { char arr1[] = "abcdef"; char arr2[] = "qwewwwwww"; strncpy(arr1, arr2, 5); printf("%s\n", arr1); return 0; }

 标准规定:

第一个字符串大于第二个字符串,则返回大于0的数字

第一个字符串等于第二个字符串,则返回0

第一个字符串小于第二个字符串,则返回小于0的数字


int main() { char arr1[] = "abcdef"; char arr2[] = "qwe"; strncpy(arr1, arr2, 5);//不够默认补
2.2 strncat
printf("%s\n", arr1); return 0; }
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 arr1[] = "abcd";
	char arr2[] = "abdc";
	int ret = my_strcmp(arr1, arr2);
	if (ret >0)
	{
		printf(">\n");
	}
	else if (ret == 0)
	{
		printf("== \n");
	}
	else
	{
		printf("<\n");
	}
	printf("%d\n", ret);
	return 0;
}

int main() { char arr1[20] = "abcdef
2.3 strncmp
XXXXX"; char arr2[] = "qwe"; strncat(arr1, arr2, 3);//追加三个,还会再把int main() { char arr1[] = "abcdef"; char arr2[] = "abcdq"; int ret = strncmp(arr1, arr2, 4);//相等 printf("%d\n", ret); return 0; } 放进去 printf("%s\n", arr1); return 0; }
2.4 strstr  在一个字符串中找另一个字符串

 拷贝num个字符从源字符串到目标空间。


如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。


char * strstr ( const char *str1, const char * str2);
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "de";
	char * p=strstr(arr1, arr2);
	if (p == NULL)//strstr找不到返回NULL指针,我们需要判断
	{
		printf("找不到");
	}
	else
	{
		printf("%s ", p);
	}
	return 0;
}

2.5 模拟实现strstr


char* my_strstr(const char* str1, const char* str2) { assert(str1 && str2); const char* s1 = str1; const char* s2 = str2; const char* cur = str1; while (*cur)//cur不等于
2.6 strtok
进来 { s1 = cur;//判断失败返回cur指向的位置 s2 = str2;//判断失败回到起始位置 while (*s1 && *s2 && (*s1 == *s2))//两个字符串都被查找完,没有数据了 { s1++; s2++; } if (*s2 == 'char * strtok ( char * str, const char * sep );')//字串找到,返回记录地址 { return (char*)cur; } cur++;//匹配不成功,指向下一步 } return NULL;//找不到 } int main() { char arr1[] = "abbbcdef"; char arr2[] = "bbc"; char* ret = my_strstr(arr1, arr2); if (NULL == ret) { printf("找不到子串\n"); } else { printf("%s\n", ret); } return 0; }

 追加num个字符从源字符串到目标空间。


int main()
{
	char arr[] = "lanyangyang@landawang.cunba";
  //char arr[] = "lanyangyang
2.7 strerror  perror  返回错误码,所对应的错误信息
landawangchar * strerror ( int errnum );cunba"; strtok函数会把数组变成这样 char buf[50] = { 0 }; const char* sep = "@. "; strcpy(buf, arr); //printf("%s\n", strtok(buf, sep));//只找第一个标记 //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找 //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找 优化 char* str = NULL; for (str=strtok(buf, sep); str!=NULL; str=strtok(NULL, sep)) { printf("%s\n", str); } return 0; }


2.8 字符分类函数

函数


如果他的参数符合下列条件就返回真
iscntrl

 判断str2是不是str1的子串,如果str2在str1中出现,返回在str1中第一次出现的地址

如果没有出现,返回空指针

任何控制字符

sspace

 思路:只需要找的子串如果复杂,我们需要三个指针协助

s1指针指向str1,s2指针指向str2。


cur指针指向str1,作用是记录开始匹配的地址

如果两个字符串对应位置不相等,str1往后走

如果相等,开始匹配,我们应该记住开始匹配的str1位置,因为有可能相等,有可能不相等

如果一直到\0结束,str2就是str1子串

如果不相等,重新找到记录位置的地址,往后+1.往后走重新开始匹配,其中str2指针重新指向数组起始位置地址

空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t'或者垂直制表符'\v'

isdigit

作用:指定分隔符,让数组分段

十进制数字 0~9

 sep参数是个字符串,定义了用作分隔符的字符集合

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


strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。


(注: strtok函数会改变被 *** 作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。


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


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


如果字符串中不存在更多的标记,则返回 NULL 指针。


isxdigit


十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F
islower

 库函数在使用失败的时候,会留下错误码errno(全局变量),类似网站404错误码

strerror就是翻译错误信息

#include 
#include 
#include 
#include 

int main()
{
	printf("%s\n", strerror(0));
	printf("%s\n", strerror(1));
	printf("%s\n", strerror(2));
	printf("%s\n", strerror(3));
	int* p = (int*)malloc(INT_MAX);//想堆区申请内存的
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		perror("Malloc");//perror是打印错误信息,strerror是把错误码转换成错误信息
 //只需要写字符串,然后输出错误码对应得错误信息,更加方便
		return 1;
	}
	return 0;
}

返回错误码所对应错误信息字符串首字符地址

 


小写字母a~z int tolower ( int c ); int toupper ( int c ); int main() { char ch = 'A'; putchar(toupper(ch)); putchar(tolower(ch)); return 0; }
3. 内存 *** 作函数
3.1 memcpy 内存空间数据拷贝
void * memcpy ( void * destination, const void * source, size_t count );int main() { int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 }; int arr2[5] = { 0 }; return 0; memcpy(arr2,arr1,20);//拷贝20个字节 }
3.2 模拟实现memcpy
3.3 memmove  可以实现重叠内存拷贝
void *memmove( void *dest, const void *src, size_t count );1
3.4  模拟实现memmove
3.5 memcpy  内存字节对应比较
isupper大写字母A~Z
isalpha字母a~z或A~Z
isalnum字母或者数字,a~z,A~Z,0~9
ispunct标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph任何图形字符
sprint任何可打印字符,包括图形字符和空白字符

 

 举例:isdigit接收字符的ASCII码值,返回int类型(如果是数字字符返回非0,不是数组字符返回0)

#include 

int main()
{
int ret = isdigit('5');//5
int ret = isdigit('Q');//0
printf("%d\n", ret);

return 0;
}
char ch = 'A';

	if (ch >= 'a' && ch <= 'z')
	{

	}

这样写很麻烦,我们一个函数搞定

int ret = islower(ch);//判断是否小写,是小写字母返回非0,否则返回0,快速判断

字符转换:

int memcmp ( const void * ptr1, 
 const void * ptr2, 
 size_t num );//比较的字节个数
 int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3,4,0x11223305 };
	int ret = memcmp(arr1, arr2, 18);
	printf("%d\n", ret);

	return 0;
}


3.6  memset  以字节为单位进行内存设置
void *memset( void *dest, int c, size_t count );//目的空间,设置的字符,字符个数
 int main()
{
	int arr[] = { 0x11111111,0x22222222,3,4,5 };
	memset(arr, 6, 20);//memset是以字节为单位来初始化内存单元的
	return 0;
}

注意事项:

1.函数memcpy从source的位置开始向后复制count个字节的数据到destination的内存位置。


2.这个函数在遇到 '\0' 的时候并不会停下来。


3.如果source和destination有任何的重叠,复制的结果都是未定义的。


4.返回目的地的起始地址

 

思路:  

1. 作者实现memcpy函数的时候,作者不知道你要拷贝什么数据,类似qsort 

2.拷贝时应该根据数据类型强制类型转换,一个字节一个字节拷贝

#include 
#include 
#include 

//void* my_memcpy(void* dest, const void* src, size_t count)
//{
//	assert(src && dest);
//	while (count--)
//	{
//		*(char*)dest = *(char*)src;
//		dest = (char*)dest + 1;
//		src = (char*)src + 1;
//	}
// }

void* my_memcpy(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	while (count--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}

	return ret;
}


int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[5] = { 0 };
	my_memcpy(arr2, arr1, 20);



    int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };   
    // 1 2 1 2 3 4 5 8 9 10 期望的结果
	my_memcpy(arr1+2, arr1, 20);

	return 0;
}

但是当我们想拷贝同一块空间的数据时,数据却发生错误

 原因在于数据覆盖了我们想要拷贝的空间

 在同一块内存拷贝,目标和源头数据空间有交叉,我们应该用memmove


 

#include 

int main()
{
		int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
		memmove(arr1 + 2, arr1, 20);
	return 0;
}

 

思路:

把34567拷贝放到12345,正着放不会覆盖数据(dest

 而想把34567拷贝放到45678,正着放会覆盖数据,我们可以先把7放到8上,6放到7,5放到6...从后往前拷贝数据,这样数据就不会被覆盖

 总结:当需要拷贝的地址dest > src地址,从后向前拷贝 ;dest < src时,从前向后拷贝

当dest和src空间无交集,前后关系无所谓,我们这里默认从后向前拷贝(方便)

{
	if (dest > src)
	{
		;  //从后向前拷贝
	}
	else
	{
		;  //从前向后拷贝
	}
}

也可以换成另一种写法

if (dest > src && dest<((char*)src+count))
	{
		;//从后向前拷贝
	}
	else
	{
		;//从前向后拷贝
	}

代码思路:从前向后的代码就是memcpy模拟,从后向前,我们需要+20个字节来到dest和src末尾

void* my_memmove(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	if (dest > src)
	{
		while (count--)
		{
	*((char*)dest + count) =  *((char*)src + count);  //count=19,正好指向最后一个字节
		}
		//从后向前拷贝
	}
	else
	{
			void* ret = dest;
			while (count--)
			{
				*(char*)dest = *(char*)src;
				dest = (char*)dest + 1;
				src = (char*)src + 1;
			}
		 //从前向后拷贝
	}
	return ret;
}


	int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr1 + 2, arr1, 20);
	//my_memmove(arr1 , arr1+2, 20);
	return 0;
}

 

 

 


 

[+++]

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
C语言拯救者(模拟实现字符串函数和内存函数--11)_C_内存溢出

C语言拯救者(模拟实现字符串函数和内存函数--11)

C语言拯救者(模拟实现字符串函数和内存函数--11),第1张

目录

C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。


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

1.1 strlen

1.2 三种方式模拟实现strlen库函数

1.3 strcpy 字符串拷贝

1.4 模拟实现strcpy

strcpy函数返回的是目标空间的起始地址

strcpy函数的返回类型的设置是为了实现链式访问

1.5 strcat 字符串追加

1.6 模拟实现strcat

1.7 strcmp 比较字符串

1.8 模拟实现strcmp

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

2.1 strncpy

2.2 strncat

2.3 strncmp

2.4 strstr  在一个字符串中找另一个字符串

2.5 模拟实现strstr

2.6 strtok

2.7 strerror  perror  返回错误码,所对应的错误信息

2.8 字符分类函数

3. 内存 *** 作函数

3.1 memcpy 内存空间数据拷贝

3.2 模拟实现memcpy

 在同一块内存拷贝,目标和源头数据空间有交叉,我们应该用memmove

3.3 memmove  可以实现重叠内存拷贝

3.4  模拟实现memmove

3.5 memcpy  内存字节对应比较

3.6  memset  以字节为单位进行内存设置


C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。


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

1.1 strlen

size_t strlen ( const char * str );//函数原型,注意函数的返回值为size_t

字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' )。


char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };//无法用strlen求字符串长度

char arr[10] = { 'a', 'b', 'c', 'd', 'e', 'f' };//限定长度,可以求

 下面结果是什么?

 if ((int)strlen("abc") - (int)strlen("qwerty") > 0)
	{
		printf(">\n");
	}
	else
	{
		printf("<=\n");
	}

答案:>,两个无符号数相减结果是正数


1.2 三种方式模拟实现strlen库函数

通常写法

 #include 
 #include 

size_t my_strlen(const char* str)
{
	int count = 0;//统计字符的个数
    assert(str);
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}
 
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

递归

 #include 
 #include 

//my_strlen("abcdef")
//1+my_strlen("bcdef")
//1+1+my_strlen("cdef")
//1+1+1+ my_strlen("def")
//1+1+1+1+ my_strlen("ef")
//1 + 1 + 1 + 1 +1+my_strlen("f")
//1 + 1 + 1 + 1 + 1 + 1+ my_strlen("")
//1 + 1 + 1 + 1 + 1 + 1 + 0 = 6
 
size_t my_strlen(const char* str)
{
    assert(str);
	if (*str != '\0')
		return 1 + my_strlen(str+1);
	else
		return 0;
}
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

指针-指针方法

 #include 
 #include 


size_t my_strlen(const char* arr,int sz)//指针-指针
{
    assert(arr);
    char* right = arr + sz - 1;
    return right - arr;
}
 
int main()
{
    char arr[] = "hello";
    int sz = sizeof(arr) / sizeof(arr[0]);
    int len=my_strlen(arr,sz);
    printf("%d", len);
    return 0;
}

1.3 strcpy 字符串拷贝

字符串拷贝,把源字符串拷贝放到目标空间字符串中,其中需要注意的事项

1.源字符串必须以 '\0' 结束。


char arr1[20] = {0};
char arr2[] = {'a','b','c'};//程序崩溃,没有char arr1[4] = "x";
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

2.会将源字符串中的 '\0' 拷贝到目标空间。


#include 
#include 
#include 

int main()
{
	
	char arr1[] = "XXXXXXXXXXXX";//arr1指向的是常量字符串,常量是不可修改的
	char arr2[] = "abcdef";
	strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

3.目标空间必须足够大,以确保能存放源字符串。


strcpy不管空间够不够,只要你敢写它就敢放,哪怕程序崩溃

char* arr1 = "qwertyuiop";//arr1指向的是常量字符串,常量是不可修改的
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

4.目标空间必须可变。


1.4 模拟实现strcpy

char* my_strcpy(char*dest, const char* src) { assert(src && dest); char* ret = dest; while(*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = { 0 }; char* arr2 = "hello bit"; printf("%s\n", my_strcpy(arr1, arr2)); return 0; } strcpy函数返回的是目标空间的起始地址 strcpy函数的返回类型的设置是为了实现链式访问
1.5 strcat 字符串追加

int main() { char arr1[10] = "hello " ; char* arr2 = "bit"; printf("%s\n", strcat(arr1, arr2)); return 0; }

int main()
{
	char arr1[20] =  "hello
1.6 模拟实现strcat
XXXXX" ; char arr2[] = "bit"; printf("%s\n", strcat(arr1, arr2)); return 0; }

注意事项:

1.源字符串必须以 '\0' 结束。


从\0位置开始追加

char* my_strcat(char* dest, char* src)
{
	assert(dest && src);
	char* ret = dest;
	//找目标空间中的
1.7 strcmp 比较字符串
while (*dest) { dest++; } //拷贝 while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = "hello"; char arr2[] = " bit"; printf("%s\n", my_strcat(arr1, arr2)); return 0; }

2.目标空间必须有足够的大,能容纳下源字符串的内容。


3.目标空间必须可修改。


strcmp函数比较的不是字符串的长度

而是比较字符串中对应位置上的字符的大小(ASCII码值),如果相同,就比较下一对儿,直到不同或者都遇到
1.8 模拟实现strcmp
2  长度受限制的字符串函数
的ASCII码值是0
2.1 strncpy

字符串自己给自己追加,如何?

我们所写的模拟函数无法完成自己追加自己,也不建议这么使用


char *strncpy( char *strDest, const char *strSource, size_t count ); int main() { char arr1[] = "abcdef"; char arr2[] = "qwewwwwww"; strncpy(arr1, arr2, 5); printf("%s\n", arr1); return 0; }

 标准规定:

第一个字符串大于第二个字符串,则返回大于0的数字

第一个字符串等于第二个字符串,则返回0

第一个字符串小于第二个字符串,则返回小于0的数字


int main() { char arr1[] = "abcdef"; char arr2[] = "qwe"; strncpy(arr1, arr2, 5);//不够默认补
2.2 strncat
printf("%s\n", arr1); return 0; }
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 arr1[] = "abcd";
	char arr2[] = "abdc";
	int ret = my_strcmp(arr1, arr2);
	if (ret >0)
	{
		printf(">\n");
	}
	else if (ret == 0)
	{
		printf("== \n");
	}
	else
	{
		printf("<\n");
	}
	printf("%d\n", ret);
	return 0;
}

int main() { char arr1[20] = "abcdef
2.3 strncmp
XXXXX"; char arr2[] = "qwe"; strncat(arr1, arr2, 3);//追加三个,还会再把int main() { char arr1[] = "abcdef"; char arr2[] = "abcdq"; int ret = strncmp(arr1, arr2, 4);//相等 printf("%d\n", ret); return 0; } 放进去 printf("%s\n", arr1); return 0; }
2.4 strstr  在一个字符串中找另一个字符串

 拷贝num个字符从源字符串到目标空间。


如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。


char * strstr ( const char *str1, const char * str2);
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "de";
	char * p=strstr(arr1, arr2);
	if (p == NULL)//strstr找不到返回NULL指针,我们需要判断
	{
		printf("找不到");
	}
	else
	{
		printf("%s ", p);
	}
	return 0;
}

2.5 模拟实现strstr


char* my_strstr(const char* str1, const char* str2) { assert(str1 && str2); const char* s1 = str1; const char* s2 = str2; const char* cur = str1; while (*cur)//cur不等于
2.6 strtok
进来 { s1 = cur;//判断失败返回cur指向的位置 s2 = str2;//判断失败回到起始位置 while (*s1 && *s2 && (*s1 == *s2))//两个字符串都被查找完,没有数据了 { s1++; s2++; } if (*s2 == 'char * strtok ( char * str, const char * sep );')//字串找到,返回记录地址 { return (char*)cur; } cur++;//匹配不成功,指向下一步 } return NULL;//找不到 } int main() { char arr1[] = "abbbcdef"; char arr2[] = "bbc"; char* ret = my_strstr(arr1, arr2); if (NULL == ret) { printf("找不到子串\n"); } else { printf("%s\n", ret); } return 0; }

 追加num个字符从源字符串到目标空间。


int main()
{
	char arr[] = "lanyangyang@landawang.cunba";
  //char arr[] = "lanyangyang
2.7 strerror  perror  返回错误码,所对应的错误信息
landawangchar * strerror ( int errnum );cunba"; strtok函数会把数组变成这样 char buf[50] = { 0 }; const char* sep = "@. "; strcpy(buf, arr); //printf("%s\n", strtok(buf, sep));//只找第一个标记 //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找 //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找 优化 char* str = NULL; for (str=strtok(buf, sep); str!=NULL; str=strtok(NULL, sep)) { printf("%s\n", str); } return 0; }


2.8 字符分类函数

函数


如果他的参数符合下列条件就返回真
iscntrl

 判断str2是不是str1的子串,如果str2在str1中出现,返回在str1中第一次出现的地址

如果没有出现,返回空指针

任何控制字符

sspace

 思路:只需要找的子串如果复杂,我们需要三个指针协助

s1指针指向str1,s2指针指向str2。


cur指针指向str1,作用是记录开始匹配的地址

如果两个字符串对应位置不相等,str1往后走

如果相等,开始匹配,我们应该记住开始匹配的str1位置,因为有可能相等,有可能不相等

如果一直到\0结束,str2就是str1子串

如果不相等,重新找到记录位置的地址,往后+1.往后走重新开始匹配,其中str2指针重新指向数组起始位置地址

空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t'或者垂直制表符'\v'

isdigit

作用:指定分隔符,让数组分段

十进制数字 0~9

 sep参数是个字符串,定义了用作分隔符的字符集合

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


strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。


(注: strtok函数会改变被 *** 作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。


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


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


如果字符串中不存在更多的标记,则返回 NULL 指针。


isxdigit


十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F
islower

 库函数在使用失败的时候,会留下错误码errno(全局变量),类似网站404错误码

strerror就是翻译错误信息

#include 
#include 
#include 
#include 

int main()
{
	printf("%s\n", strerror(0));
	printf("%s\n", strerror(1));
	printf("%s\n", strerror(2));
	printf("%s\n", strerror(3));
	int* p = (int*)malloc(INT_MAX);//想堆区申请内存的
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		perror("Malloc");//perror是打印错误信息,strerror是把错误码转换成错误信息
 //只需要写字符串,然后输出错误码对应得错误信息,更加方便
		return 1;
	}
	return 0;
}

返回错误码所对应错误信息字符串首字符地址

 


小写字母a~z int tolower ( int c ); int toupper ( int c ); int main() { char ch = 'A'; putchar(toupper(ch)); putchar(tolower(ch)); return 0; }
3. 内存 *** 作函数
3.1 memcpy 内存空间数据拷贝
void * memcpy ( void * destination, const void * source, size_t count );int main() { int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 }; int arr2[5] = { 0 }; return 0; memcpy(arr2,arr1,20);//拷贝20个字节 }
3.2 模拟实现memcpy
3.3 memmove  可以实现重叠内存拷贝
void *memmove( void *dest, const void *src, size_t count );1
3.4  模拟实现memmove
3.5 memcpy  内存字节对应比较
isupper大写字母A~Z
isalpha字母a~z或A~Z
isalnum字母或者数字,a~z,A~Z,0~9
ispunct标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph任何图形字符
sprint任何可打印字符,包括图形字符和空白字符

 

 举例:isdigit接收字符的ASCII码值,返回int类型(如果是数字字符返回非0,不是数组字符返回0)

#include 

int main()
{
int ret = isdigit('5');//5
int ret = isdigit('Q');//0
printf("%d\n", ret);

return 0;
}
char ch = 'A';

	if (ch >= 'a' && ch <= 'z')
	{

	}

这样写很麻烦,我们一个函数搞定

int ret = islower(ch);//判断是否小写,是小写字母返回非0,否则返回0,快速判断

字符转换:

int memcmp ( const void * ptr1, 
 const void * ptr2, 
 size_t num );//比较的字节个数
 int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3,4,0x11223305 };
	int ret = memcmp(arr1, arr2, 18);
	printf("%d\n", ret);

	return 0;
}


3.6  memset  以字节为单位进行内存设置
void *memset( void *dest, int c, size_t count );//目的空间,设置的字符,字符个数
 int main()
{
	int arr[] = { 0x11111111,0x22222222,3,4,5 };
	memset(arr, 6, 20);//memset是以字节为单位来初始化内存单元的
	return 0;
}

注意事项:

1.函数memcpy从source的位置开始向后复制count个字节的数据到destination的内存位置。


2.这个函数在遇到 '\0' 的时候并不会停下来。


3.如果source和destination有任何的重叠,复制的结果都是未定义的。


4.返回目的地的起始地址

 

思路:  

1. 作者实现memcpy函数的时候,作者不知道你要拷贝什么数据,类似qsort 

2.拷贝时应该根据数据类型强制类型转换,一个字节一个字节拷贝

#include 
#include 
#include 

//void* my_memcpy(void* dest, const void* src, size_t count)
//{
//	assert(src && dest);
//	while (count--)
//	{
//		*(char*)dest = *(char*)src;
//		dest = (char*)dest + 1;
//		src = (char*)src + 1;
//	}
// }

void* my_memcpy(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	while (count--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}

	return ret;
}


int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[5] = { 0 };
	my_memcpy(arr2, arr1, 20);



    int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };   
    // 1 2 1 2 3 4 5 8 9 10 期望的结果
	my_memcpy(arr1+2, arr1, 20);

	return 0;
}

但是当我们想拷贝同一块空间的数据时,数据却发生错误

 原因在于数据覆盖了我们想要拷贝的空间

 在同一块内存拷贝,目标和源头数据空间有交叉,我们应该用memmove


 

#include 

int main()
{
		int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
		memmove(arr1 + 2, arr1, 20);
	return 0;
}

 

思路:

把34567拷贝放到12345,正着放不会覆盖数据(dest

 而想把34567拷贝放到45678,正着放会覆盖数据,我们可以先把7放到8上,6放到7,5放到6...从后往前拷贝数据,这样数据就不会被覆盖

 总结:当需要拷贝的地址dest > src地址,从后向前拷贝 ;dest < src时,从前向后拷贝

当dest和src空间无交集,前后关系无所谓,我们这里默认从后向前拷贝(方便)

{
	if (dest > src)
	{
		;  //从后向前拷贝
	}
	else
	{
		;  //从前向后拷贝
	}
}

也可以换成另一种写法

if (dest > src && dest<((char*)src+count))
	{
		;//从后向前拷贝
	}
	else
	{
		;//从前向后拷贝
	}

代码思路:从前向后的代码就是memcpy模拟,从后向前,我们需要+20个字节来到dest和src末尾

void* my_memmove(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	if (dest > src)
	{
		while (count--)
		{
	*((char*)dest + count) =  *((char*)src + count);  //count=19,正好指向最后一个字节
		}
		//从后向前拷贝
	}
	else
	{
			void* ret = dest;
			while (count--)
			{
				*(char*)dest = *(char*)src;
				dest = (char*)dest + 1;
				src = (char*)src + 1;
			}
		 //从前向后拷贝
	}
	return ret;
}


	int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr1 + 2, arr1, 20);
	//my_memmove(arr1 , arr1+2, 20);
	return 0;
}

 

 

 


 

 

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/568205.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-09
下一篇 2022-04-09

发表评论

登录后才能评论

评论列表(0条)

保存