第一:strlen函数

	功能:是求字符串的长度。
	注意: 1. 返回的是字符串中第一个出现'\0'前面的字符的长度
				2. 参考1,字符串中必须有'\0', 否则会继续往后统计,直到遇见'\0',则结果会是一个随机值。
				3. 该函数的返回类型为size_t(typedef unsigned int size_t)
	函数原型:size_t strlen( const char *string );
	参       数:string Null-terminated string
	返  回  值:Each of these functions returns the number of characters in string, excluding the terminal NULL. No return value is reserved to indicate an error.

使用和模拟实现功能代码如下:

// 计数器方法
int self_strlen_count(const char* p)
{
	assert(p != NULL);
	int cnt = 0;
	while (*p++) // whle(*p++  != '\0') same
	{
		cnt++;
	}
	return cnt;
}

//recursion
int self_strlen_recursion(const char* p)
{
	char* p1 = p;
	if (*p1 == '\0')
		return 0;
	else
		return 1 + self_strlen_recursion(++p1);
}

// point
int self_strlen_point(const char* p)
{
	char* p1 = p;
	while (*p1++ != '\0')
	{
		;
	}
	
	return --p1 - p;
}
int main()
{
	char* str1 = "abcdefg"; 
	char* str2[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g' }; // strlen的结果会是 > 7 的随机值
	printf("%d\n", strlen(str1));
	printf("%d\n", self_strlen_count(str1));
	printf("%d\n", self_strlen_recursion(str1));
	printf("%d\n", self_strlen_point(str1));

	// 因为strlen返回的是unsigned int
	// 00000000 00000000 00000000 00000011 strlen("abc")    原码=反码=补码
	// 00000000 00000000 00000000 00000111 strlen("abcdefg")原码=反码=补码
	// 10000000 00000000 00000000 00000011 相减得到-3,由于两端都是无符号数,则结果被当成无符号数处理,结果为大于0的值
	if (strlen("abc") - strlen("abcdefg"))
		printf(" ok \n"); // 执行
	else
		printf(" not ok \n");
	return 0;
}

第二:strcpy

	功能:是将一个字符串,拷贝到另外一个字符串中。Copy a string.
	注意:1. 源字符串必须包含'\0'
		   	2. 目标字符串的空间要足够大,为了能够存放下源字符串的内容
		   	3. 拷贝时,会将源字符串中的'\0'同时也拷贝过去。
	函数原型:char *strcpy( char *strDestination, const char *strSource );
	参       数:strDestination: Destination string
			          strSource: Null-terminated source string
    返  回  值:Each of these functions returns the destination string. No return value is reserved to indicate an error.

2.1 strcpy库函数的使用

int main()
{
	char str1[100] = "abcdefghijklmnopqrstuwvxyz";
	char* str2 = "1234567";
	printf("%s", strcpy(str1, str2));

	return 0;
}

2.2 模拟实现strcpy库函数

#include <string.h>
#include <stdio.h>
#include <assert.h>
char* self_strcpy(char* dest, const char* src)
{
	assert(NULL != dest);
	assert(NULL != src);
	char* start = dest;
	// 进行数据的拷贝-包括'\0'
	while (*dest++ = *src++) { ; }

	// 返回目标空间的地址
	return start;
}
int main()
{
	char str1[100] = "abcdefghijklmnopqrstuwvxyz";
	char* str2 = "1234567";
	//printf("%s", strcpy(str1, str2));
	printf("%s", self_strcpy(str1, str2));
	return 0;
}

第三:strcat

	功能:字符串的追加Append a string.
	注意:1. 源字符串必须以'\0'结束。
			   2. 目标字符串的空间必须足够大,以能够在继续存放源字符串的内容
			   3. 同样也会将源字符串中的'\0'追加过去。
	函数原型:char *strcat( char *strDestination, const char *strSource );
	参       数:同strcpy
	返  回  值:Each of these functions returns the destination string (strDestination). No return value is reserved to indicate an error.

3.1 函数的使用

#include <string.h>
#include <stdio.h>
#include <assert.h>
int main()
{
	char str1[100] = "abcdefg\0ijklmnopqrstuwvxyz";
	char* str2 = "1234567";
	printf("%s", strcat(str1, str2));
	//printf("%s", self_strcat(str1, str2));
	return 0;
}

3.2 strcat的模拟实现

char* self_strcat(char* dest, const char* src)
{
	assert(NULL != dest);
	assert(NULL != src);
	char* start = dest;
	if (*src == '\0')
	{
		return start;
	}
	else
	{
		while (*dest++) { ; }
		dest--;
		while (*dest++ = *src++) { ; }
		return start;
	}
}
int main()
{
	char str1[100] = "abcdefg\0ijklmnopqrstuwvxyz";
	char* str2 = "1234567";
	//printf("%s", strcat(str1, str2));
	printf("%s", self_strcat(str1, str2));
	return 0;
}

第四:strcmp

功能:比较两个字符串的大小Compare strings.
注意:1. 两个字符串都必须含有'\0'
		   2. return >0 : str1 > str2
			   return =0 : str1 == str2
			   return <0  : str1 < str2
函数原型:int strcmp( const char *string1, const char *string2 );
参       数:string1, string2: Null-terminated strings to compare, 同strcat
返 回  值:见注意2

4.1 strcmp函数的使用和模拟

#include <string.h>
#include <stdio.h>
#include <assert.h>

int self_strcmp(const char* str1, const char* str2)
{
	assert(NULL != str1);
	assert(NULL != str2);
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
		{
			return 0;
		}
		str1++;
		str2++;
	}
	return *str1 - *str2;
}
int main()
{
	printf("%d\t", strcmp("abc", "abc"));
	printf("%d\n", self_strcmp("abc", "abc"));
	printf("%d\t", strcmp("abcd", "abc"));
	printf("%d\n", self_strcmp("abcd", "abc"));
	printf("%d\t", strcmp("abc", "abf"));
	printf("%d\n", self_strcmp("abc", "abf"));
	return 0;
}

第五:strncpy

	功能:从源字符串中拷贝指定数量的字符到目标字符串中。
	注意:1. 源字符串中可以没有'\0'.
		       2. 拷贝的个数(n,即第三个参数的值)中不包含'\0',即拷贝n个字符,就是拷贝n个字符到目标字符。
			   3. 如果目标字符串中很长,且都有数据,且拷贝字符的个数 < 源字符串的长度,则只覆盖目标字符前面n个字符,后面字符保留。
			   4. 如果拷贝字符个数 > 源字符串的长度,则会拷贝多个'\0',知道拷贝个数等于要拷贝字符的个数。
	函数原型:char *strncpy( char *strDest, const char *strSource, size_t count );
	参数:strDest: Destination string
		   	strSource: Source string
			   count: Number of characters to be copied
	返回值:Each of these functions returns strDest. No return value is reserved to indicate an error.

5.1 strncpy函数的使用

#include <string.h>
#include <stdio.h>
#include <assert.h>
int main()
{
	char str1[] = "abcdefghijklmn";
	char str2[] = "01234";
	strncpy(str1, str2, 4);
	strncpy(str1, str2, 10);
	return 0;
}

5.2 strncpy的模拟实现

#include <string.h>
#include <stdio.h>
#include <assert.h>

char* self_strncpy(char* dest, const char* src, size_t count)
{
	assert(NULL != dest);
	assert(NULL != src);
	char* start = dest;
	//while (count--)
	//{
	//	if (*src)
	//	{
	//		*dest = *src;
	//		src++;
	//	}
	//	else
	//	{
	//		*dest = '\0';
	//	}
	//	dest++;
	//}
	while (count && (*dest++ = *src++)) { count--; }
	if (count)
	{
		while (--count) { *dest++ = '\0'; }
	}
	return start;
}
int main()
{
	char str1[] = "abcdefghijklmn";
	char str2[] = "01234";
	self_strncpy(str1, str2, 4);
	self_strncpy(str1, str2, 10);
	return 0;
}

第六:strncat函数

	功能:从源字符串中追加n个字符到目标字符串中
	注意:1. 追加n个字符后,会自动再追加一个'\0',这样才是一个完整的字符串
			   2. 如果追加的数量大于源字符串的长度,则追加完源字符串后和'\0'之后,不再追加多余的'\0',这一点与strncpy有点区别。
	函数原型:char *strncat( char *strDest, const char *strSource, size_t count );
	参       数: 同 strncpy
	返  回 值:同strncpy

6.1 strncat函数的使用

int main()
{
	char str1[100] = "abcdefg\0ijklmnopqrstuwvxyz";
	char* str2 = "1234567";
	printf("%s", strncat(str1, str2, 4));
	printf("%s", strncat(str1, str2, 10));
}

6.2 strncat函数的模拟实现

#include <string.h>
#include <stdio.h>
#include <assert.h>
char* self_strncat(char* dest, const char* src, int count)
{
	assert(NULL != dest);
	assert(NULL != src);
	char* start = dest;
	if (*src == '\0')
	{
		return start;
	}
	else
	{
		while (*dest++) { ; }
		dest--;
		while (count-- && (*dest++ = *src++)) { ; }
		if (++count == 0)
		{
			*dest = '\0';
		}
		return start;
	}
}
int main()
{
	char str1[100] = "abcdefg\0ijklmnopqrstuwvxyz";
	char* str2 = "1234567";
	printf("%s", self_strncat(str1, str2, 4));
	printf("%s", self_strncat(str1, str2, 10));
}

第七:strncmp函数

	功能:对两个字符串的前n个字符进行比较
	函数原型:int strncmp( const char *string1, const char *string2, size_t count );
	参数:同strncat和strncpy
	返回类型:同strcmp

7.1 函数的使用

#include <string.h>
#include <stdio.h>
int main()
{
	printf("%d\t", strncmp("abc", "abd", 2));
	printf("%d\t", strncmp("abc", "abd", 3));
	printf("%d\t", strncmp("abe", "abd", 3));
	return 0;
}

第八:strstr函数

	功能:查找一个字符串是否在另外一个字符串中
	函数原型:char *strstr( const char *string, const char *strCharSet );
	参       数:string: Null-terminated string to search
					  strCharSet: Null-terminated string to search for
	返 回  值:如果找到,返回子串所在字符串中第一份的地址,否则返回NULL空指针
	返 回  值:Each of these functions returns a pointer to the first occurrence of strCharSet in string, or NULL if strCharSet does not appear in string. If strCharSet points to a string of zero length, the function returns string.

NULL:空指针 NUll/NUL: '\0'

8.1 strstr函数的使用

int main()
{
	printf("%s\n", strstr("abcbcdef", "bcd")); // 找到返回子字符串在字符串中的地址
	printf("%s\n", strstr("abcbcdef", "bcdd")); // 找不到返回NULL指针
	return 0;
}

8.2 strstr函数的模拟实现

另外一种实现方法是基于KMP算法,这里不做演示。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
char* self_strstr(const char* p1, const char* p2)
{
	assert(p1 != NULL);   // 判断是否为空指针
	assert(p2 != NULL);
	char* s1 = NULL;        // 保持传进来的指针不发生变量
	char* s2 = NULL;
	char* cur = (char*)p1;       // 记录p1字符串的比对起始位置
	if (*p2 == '\0')      // 如果子串为空,则不需要查找,直接返回p1的地址
	{
		return (char*)p1;
	}
	while (*cur)          // 如果查找的当前位置不为空,则进行查找
	{
		s1 = cur;         // 将当前位置设置为查找的开始
		s2 = (char*)p2;          // 将子串的首地址作为匹配的开始
		while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)  // 如果都没有结束且相等,则均向后移动一个字符,继续判断
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')   // 如果是子串查找到了结尾,则找到了该子串,则返回原串的当前位置
		{
			return cur;
		}
		if (*s1 == '\0')   // 如果原串到了结尾,则不再更新当前位置,而是直接返回:空指针
		{
			return NULL;
		}
		cur++;
	}
	return NULL;
}
int main()
{
	printf("%s\n", strstr("abcbcdef", "bcd")); // 找到返回子字符串在字符串中的地址
	printf("%s\n", strstr("abcbcdef", "bcdd")); // 找不到返回NULL指针
	printf("%s\n", self_strstr("abcbcdef", "bcd")); // 找到返回子字符串在字符串中的地址
	printf("%s\n", self_strstr("abcbcdef", "bcdd")); // 找不到返回NULL指针
	return 0;
}

第九:strtok

	功       能:Find the next token in a string
	函数原型:char *strtok( char *strToken, const char *strDelimit );
	参        数: strToken: String containing token(s)
	                    用于切割或分隔那个字符串的起始地址,其包含>=0个分隔符集合中字符
	                    strDelimit:Set of delimiter characters
	                    参数是个字符串格式,内容为:用作分隔符的字符集合,如".@_ "等
	返回值:strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。
	解        释:
		1. 向strToken中查找分隔符,如果找到,则把str中的找到的分隔符修改为'\0',然后再把该分隔符前面的内容的地址进行返回。
			当对其进行打印时,只会打印'\0'前面的内容,即分隔符前面的内容
			strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
			该函数会记住该分隔符的位置。
		2. 当再次调用该函数时,会从上次分隔符(被改为'\0')的位置向后查找下一个分隔符,便把这次的分隔符修改为'\0',并把这次开始查找或者分隔的地址(即上一个分隔符后面字符的地址)返回。
			strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
			此时,第一个参数就要设为NULL空指针,不要设为str
		3. 再次调用时,重复步骤2.
		4. 直到返回NULL为止。
	注       意:1. 该函数会改变字符串的内容,所以需要拷贝一份,且该字符串不能被const修饰或者常量字符串。

9.1 strtok的使用

#include <string.h>
#include <stdio.h>
int main()
{
	char str[] = "www...baidu.com?name=hehe";
	char sep[] = ".?=";
	char buf[1000] = { 0 };
	strcpy(buf, str);
	char* ret = NULL;
	for (ret = strtok(str, sep); ret != NULL; ret = strtok(NULL, sep))
	{
		printf("%s\n", ret);
	}
	return 0;
}

第十:strerror函数

	功能:返回错误类型(错误码,每种错误类型对应一个错误码)的错误信息
	           Get a system error message (strerror) or prints a user-supplied error message (_strerror).
	函数原型:char *strerror( int errnum );
	errnum:是一个全局的错误码变量
	            当C语言的库函数在执行过程中,发生了错误,就会把对应的错误码,errnum

10.1 strerror函数的使用

#include <string.h>
#include <stdio.h>
#include <errno.h>

int main()
{
	FILE* fp = fopen("not_exist.txt", "r");
	if (NULL == fp)
	{
		printf("%s", strerror(errno));
	}
	return 0;
}

错误码:

第十一:字符分类函数

函数 解释
iscntrl 任何控制字符
isspace 空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t'或者垂直制表符'\v'
isdigit 十进制数字 0~9
isxdigit 十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F
islower 小写字母a~z
isupper 大写字母A~Z
isalpha 字母a~z或A~Z
isalnum 字母或者数字,a~z,A~Z,0~9
ispunct 标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph 任何图形字符
tolower 转小写字母
toupper 转大写字母

11.1 上述函数的使用

#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <ctype.h>

int main()
{
	printf("%d\t%d\n", isspace('d'), isspace('\t'));
	printf("%d\t%d\n", isdigit('d'), isdigit('9'));
	printf("%d\t%d\n", isxdigit('g'), isxdigit('f'));
	printf("%d\t%d\n", islower('A'), islower('a'));
	printf("%d\t%d\n", isupper('d'), isupper('S'));
	printf("%d\t%d\n", isalpha('1'), isalpha('a'));
	printf("%d\t%d\n", isalnum('.'), isalnum('2'));
	printf("%d\t%d\n", ispunct('0'), ispunct('.'));
	printf("%d\t%d\n", isgraph('.'), isgraph('^'));
	printf("%c\t%c\n", 'A', tolower('A'));
	printf("%c\t%c\n", 'a', toupper('a'));
	return 0;
}

第十二:memcpy函数

	功能:从内存的一个内存地址拷贝n个字节到另外一个内存地址中。Copies characters between buffers.
	          可以拷贝任意类型的数据,比如整型数组、结构体等
	注意:1. 在遇到'\0'时不会停下来
	2. 如果源地址开始拷贝内容的内存区域与目的地的起始地址有重复,则不能使用该函数,应该使用memmove函数
	函数原型:void *memcpy( void *dest, const void *src, size_t count );
	参       数:dest: New buffer 拷贝到哪里去
                  src: Buffer to copy from 从哪里开始拷贝
                  count: Number of characters to copy 拷贝多少个字节
	返回值:memcpy returns the value of dest.返回目的地的地址

12.1 memcpy函数的使用

#include <stdio.h>
#include <string.h>
int main()
{
	int i;
	int arr1[] = { 1, 2, 3, 4, 5 };
	int arr2[] = { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 };     // len: 10
	int arr3[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; // len: 11
	memcpy(arr3, arr1, sizeof(arr1));
	memcpy(arr3, arr2, sizeof(arr2));
	memcpy(arr1, arr2, sizeof(arr2)); // 目标位置大小小于拷贝大小,拷贝正常,但是运行报错。见下图所示:
	int len2 = sizeof(arr2) / sizeof(arr2[0]);
	int len3 = sizeof(arr3) / sizeof(arr3[0]);
	return 0;
}

12.2 memcpy函数的模拟实现

#include <stdio.h>
#include <string.h>
#include <assert.h>
typedef struct STU
{
	char name[20];
	int age;
	double grade;
} STU;
void* my_memcpy(void* dest, const void* src, size_t count)
{
	
	assert(dest != NULL);
	assert(src != NULL);
	void* start = dest;
	while (count--)
	{
		*(char*)dest = *(char*)src;   // 拷贝数据,一次拷贝一个字节
		++(char*)dest; // 如果写成(char*)dest++是错误的,因为++的优先级高
		++(char*)src;  // 而void类型的指针是没法进行++运算的。
	}
	return (void*)start;

}
int main()
{
	STU stu1[2] = { {"hanmeimei", 19, 99.9}, {"lilei", 18, 100.0} };
	STU stu2[4] = {0};
	my_memcpy(stu2, stu1, sizeof(stu1));
	return 0;
}

第十三:memmove函数

	功能:和memcpy类似。
	注意:可以弥补memcpy的不足:如果目的地的起始地址位于拷贝内容所在内存区域内,会覆盖原来的内容的缺陷。

13.1 memmove函数的使用和模拟实现

#include <string.h>
#include <stdio.h>
void* my_memmove(void* dest, const void* src, size_t count)
{

	assert(dest != NULL);
	assert(src != NULL);
	void* start = dest;
	// void类型的指针可以比较大小是不报警告的,但是解引用和++操作时会报错,需要转换为指定类型的指针

	//if (src < dest && (char*)dest < (char*)src + count)
	//if ( (char*)src < (char*)dest && (char*)dest < (char*)src + count ) // 这是一种
	//{
	//	while (count--)
	//	{
	//		*((char*)dest + count) = *((char*)src + count);   // 拷贝数据,一次拷贝一个字节
	//		//++(char*)dest; // 如果写成(char*)dest++是错误的,因为++的优先级高
	//		//++(char*)src;  // 而void类型的指针是没法进行++运算的。
	//	}
	//}
	//else
	//{
	//	while (count--)
	//	{
	//		*(char*)dest = *(char*)src;   // 拷贝数据,一次拷贝一个字节
	//		++(char*)dest; // 如果写成(char*)dest++是错误的,因为++的优先级高
	//		++(char*)src;  // 而void类型的指针是没法进行++运算的。
	//	}
	//}


	if ((char*)dest < (char*)src) // 这是另外一种方法  从前向后拷贝数据
	{
		while (count--)
		{
			*(char*)dest = *(char*)src;   // 拷贝数据,一次拷贝一个字节
			++(char*)dest; // 如果写成(char*)dest++是错误的,因为++的优先级高
			++(char*)src;  // 而void类型的指针是没法进行++运算的。
		}
	}
	else  // 从后向前拷贝数据
	{
		while (count--)
		{
			*((char*)dest + count) = *((char*)src + count);   // 拷贝数据,一次拷贝一个字节
			//++(char*)dest; // 如果写成(char*)dest++是错误的,因为++的优先级高
			//++(char*)src;  // 而void类型的指针是没法进行++运算的。
		}
	}
	return (void*)start;

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

	printf("自己实现的memcpy: ");
	memcpy(arr2, arr1, sizeof(arr1));
	my_memcpy(arr2 + 3, arr2,5 * sizeof(arr2[0])); // dest 位于src开始的拷贝区间之内,拷贝时会发生覆盖
	for (i = 0; i < len1; i++) { printf("%d ", arr2[i]); }
	printf("\n");

	printf("自己实现的memmove:");
	memcpy(arr2, arr1, sizeof(arr1));
	my_memmove(arr2 + 3, arr2, 5 * sizeof(arr2[0]));// dest 位于src开始的拷贝区间之内,拷贝时不会发生覆盖
	for (i = 0; i < len1; i++) { printf("%d ", arr2[i]); }
	printf("\n");

	printf("string实现memmove:");
	memcpy(arr2, arr1, sizeof(arr1));
	memmove(arr2 + 3, arr2, 5 * sizeof(arr2[0]));// dest 位于src开始的拷贝区间之内,拷贝时不会发生覆盖
	for (i = 0; i < len1; i++) { printf("%d ", arr2[i]); }
	printf("\n");

	printf("自己实现的memmove:");
	memcpy(arr2, arr1, sizeof(arr1));
	my_memmove(arr2, arr2+3, 5 * sizeof(arr2[0]));  // dest 位于src开始的拷贝区间之前
	for (i = 0; i < len1; i++) { printf("%d ", arr2[i]); }
	printf("\n");
	return 0;
}

第十四:memcmp函数

	功能:比较内存中指向不同的空间的n个字节的大小:Compare characters in two buffers.
	函数原型:int memcmp( const void *buf1, const void *buf2, size_t count );
	参数:同strcmp类似
	返回值:同strcmp

14.1 strcmp函数的使用

#include <stdio.h>
#include <string.h>
int main()
{
	char str1[] = "hello python";
	char str2[] = "hello Python";
	printf("%d\n", memcmp(str1, str2, 6)); // 0
	printf("%d\n", memcmp(str1, str2, 7)); // > 0
	printf("%d\n", memcmp(str2, str1, 7)); // < 0
	return 0;
}

第十五:memset函数

	功能:设置一个缓冲区(参数一所指向的空间)为一个特定的字符(参数二),设置的个数为n(参数三)
	           Sets buffers to a specified character.
	函数原型:void *memset( void *dest, int c, size_t count );
	参数:dest  : Pointer to destination 缓冲区的地址
	           c       : Character to set 设置成的字符
	           count: Number of characters 设置的字节长度
	返回值:缓冲区的地址

15.1 memset函数的使用

#include <stdio.h>
#include <string.h>
void main(void)
{
	char buffer[] = "This is a test of the memset function";
	int i;
	printf("Before: %s\n", buffer);
	memset(buffer, '*', 14);
	printf("After:  %s\n", buffer);

	int arr[10] = { 0 };
	printf("Before: ");
	for (i = 0; i < 10; i++) { printf("%d ", arr[i]); }
	printf("\nAfter: ");
	memset(arr, 1, 10); // 是将部缓冲区的10个字节设置为1,而不是将arr的10个元素都设为1
	for (i = 0; i < 10; i++) { printf("%d ", arr[i]); }
	printf("\n");
	// 因为是小端存储,所以第9-12个字节为00000001 00000001 00000000 00000000
	// 补码为00000000 00000000 00000001 00000001 即257
}

持续更新中。。。 敬请期待!!! 如有问题或疑惑请留言,谢谢!