C语言进阶--处理字符和字符串的库函数的使用和注意事项

目录

前言

一、处理字符和字符串的库函数的使用和注意事项

1. 求字符串长度

strlen

2. 长度不受限制的字符串函数(一定要找到 '\0')

strcpy:字符串拷贝

strcat:字符串追加

strcmp:比较字符串

长度受限制的字符串函数介绍

strncat

strncmp

字符串查找

strstr

strtok

错误信息报告

strerror

字符操作

字符分类函数

字符转换

内存操作函数

memcpy

memmove

memset -- 内存设置

memcmp



前言

第一篇博客。


一、处理字符和字符串的库函数的使用和注意事项

1. 求字符串长度

strlen

  • 查找 \0 之前有多少个字符,返回类型为 size_t = unsigned int

2. 长度不受限制的字符串函数(一定要找到 '\0')

strcpy:字符串拷贝

  • 源字符串必须以 '\0' 结束

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

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

  • 目标空间必须可变

// char* strcpy(char* destination, const char* source);
// 自己模拟的库函数
char* my_strcpy(char* dest, const char* src)
{
	assert(dest != NULL);
	assert(src);// 需引入#include 
	char* ret = dest;// 将目的空间的地址起始存入 ret
	// 拷贝src指向的字符串到dest指向的空间,包含 '\0'
	while (*dest++ = *src++)// 当拷贝到src = '\0'时,表达式判断为 0	
	{
		;
	}
	// 返回目的空间的起始地址
	return ret;
}

strcat:字符串追加

  • 源字符串必须以 '\0' 结束

  • 目标空间需要足够大,能容纳下源字符串的内容

  • 目标空间必须可修改

// char* strcat(char* destination, const char* source);
char* my_strcat(char* dest, const char* src)
{
	char* ret = dest; // 将目的空间的地址起始存入 ret
	assert(dest != NULL);
	assert(src);
	// 1.找到目的字符串的 '\0'
	while (*dest != '\0')
	{
		dest++;
	}
	// 2.追加
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

strcmp:比较字符串

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

  • 第一个字符串等于第二个字符串,返回等于0的数字

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

// char* strcmp(const char* str1, const char* str2);
// 模拟实现库函数
int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1 == *str2)
	{
		if (*str1 == '\0') {// 相等的情况下
			return 0;
		}
        str1++;
        str2++;
	}
	return (*str1 - *str2);
}

长度受限制的字符串函数介绍

strncpy

代码如下(示例):

char* strncpy(char* destination, const char* source, size_t num);
  • 从源字符串拷贝num个字节替换到目标空间

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

strncat

代码如下(示例):

char* strncat(char* destination, const char* source, size_t num);
  • 从源字符串拷贝num个字节追加到目标空间
  • 如果源字符串长度小于 num,则在追加完之后,直接再追加一个 '\0',然后停止

strncmp

代码如下(示例):

char* strncat(const  char* str1, const char* str2, size_t num);
  • 第一个字符串大于第二个字符串,返回大于0的数字
  • 第一个字符串等于第二个字符串,返回等于0的数字
  • 第一个字符串小于第二个字符串,返回小于0的数字
  • num指定要比较的前多少位字节(一个汉字为两个字节)

拓展知识:

  1. NULL -- 空指针
  2. NUL / Null -- '\0'

字符串查找

strstr

代码如下(示例):

const char * strstr ( const char * str1, const char * str2 );
  • 若str2在str1中能被找到,则返回st1中的首个str2的指针
  • 若str2不能在str1中被找到,则返回一个空指针
// 模拟实现库函数
char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	char* p1 = NULL;// 如果不赋值空指针的话,p1就是一个野指针
	char* p2 = NULL;
	char* cur = str1;// 开始时的指针位置
	if (*str2 == '\0') {
		return (char*)str1;// 若str2是一个空字符,则直接返回str1
	}
	while (*cur)// 当我的str1指到 '\0'的时候停止
	{
		p1 = cur;// 我的p1每一次大的比较的时候都需要改变初始位置,找到str2的时候,
				// 将这个时候cur的指针位置返回出去 ------ str1(abbbc)	str2(abbc)
		p2 = str2;
		while ((*p1 != '\0') && (*p2 != '\0') && (*p1 == *p2))// 
		{
			p1++;
			p2++;
		}
		if (*p2 == '\0')
		{
			return cur;// 找到字串
		}
		if (*p1 == '\0')
		{
			return cur;// p2比p1要长
		}
		cur++;
	}
	return NULL;// 找不到字串
}

strtok

代码如下(示例):

char* strtok(char* str, const char* sep);
  • sep参数是个字符串,定义了用作分隔符的字符集合
  • 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或多个分隔符分割的标记
  • strtok函数找到str中的下一个标记,并将其用\0结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串时,一般都是临时拷贝的内容并且可修改)
  • strtok函数的第一个参数不为NULL,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置
  • strtok函数的第一个参数为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
  • 如果字符串不存在更多的标记,则返回NULL
// 函数的使用方法
int main()
{
	char arr[] = "[email protected]";
	char* p = "@.";
	char buff[1024] = { 0 };
	strcpy(buff, arr);// 拷贝一份可修改的数据
	// 切割buff中的字符串
	for (char* ret =  strtok(buff,p); ret != NULL; ret = strtok(NULL, p))
	{
		printf("%s\n", ret);
	}
	return 0;
}

错误信息报告

strerror

代码如下(示例):

char* strerror(int errnum);

返回错误码所对应的错误信息

#include
int main()
{
	// 错误码 -- 错误信息
	// 0	  -- No error
	// 1	  -- Operation not permitted
	// 2	  -- No such file or directory
	// 404/500 -- Unknown error
	// ....
	// errno是一个全局的错误码变量
	// 当C语言的库函数在执行过程中,发生了错误,就会把对应的错误码,赋值到errno中
	char* str = strerror(errno);
	printf("%s\n", str);

	// 模拟错误
	// 打开文件fopen
	FILE* pf = fopen("test.txt", "r"); // 打开并读取这个文件
	if (pf == NULL) {
		printf("%s\n", strerror(errno));// 使用errno需引入#include
	}
	else {
		printf("open file success\n");
	}
	return 0;
}

字符操作

字符分类函数

下列所有函数均有 ctype.h 提供,需引入#include ,返回值均为 int (非0为真,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 字母或者数字
ispunct 标点符号,任何不属于数字或者字母的图形符号(可打印)
isgraph 任何图形字符
isprint 任何可打印字符,包括图形字符和空白字符

代码如下(示例):

#include 
int main()
{
	char ch = 'Q';
	int ret = isupper(ch);// 判断ch是否为大写,非0为真
	printf("%d", ret);
	return 0;
}

字符转换

  1. tolower (int c);  -- 大写转小写
  2. toupper (int c);  -- 小写转大写

代码如下(示例):

#include 
#include 
int main()
{
	char ch = tolower('Q');// 大写转小写
	putchar(ch);// 输出字符
	char ch2 = tolower('t');// 小写转大写
	putchar(ch2);// 输出字符
	return 0;
    
    
    char arr[] = "I Am A Student";
	int i = 0;
	while (arr[i])
	{
		// 将所有大写字母转换为小写字母
		if (isupper) {
			arr[i] = tolower(arr[i]);
		}
		i++;
	}
	printf("%s\n",arr);
}

内存操作函数

memcpy

代码如下(示例):

void * memcpy ( void * destination, const void * source, size_t num );
  • 由于destination是一个无属性指针(不能进行解引用 和 加减法操作),所以我们可以拷贝任何类型的数据,包括数组,结构体等
  • 将source中的数据拷贝到 destination中
  • num的大小单位是字节

memcpy的使用

int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 0 };
	memcpy(arr2, arr1, sizeof(arr1));
	return 0;
}

模拟实现memcpy

// C语言标准规定: 
// memcpy 只要能处理不重叠的内存拷贝的情况就够了
// memmove 处理重叠内存的拷贝
void* my_memcpy(void* dest, void* src, size_t num)
{
    assert(dest);
    assert(src);
	void* ret = dest;// 为了防止dest中的数据指针位置变动
	while (num--)
	{
		*(char*)dest = *(char*)src;// 因为void*是无属性类型不能进行解引用和加减操作,
		                          // 所以分别将dest和src先强制转换为char*类型,再进行解引用操作
		++(char*)dest;// 先进行强制转换操作,再进行前置++
		++(char*)src;
	}
	return ret;
}

memmove

使用方法和参数与memcpy一样

代码如下(示例):

void * memmove ( void * destination, const void * source, size_t num );

模拟实现memmove

void* my_memmove(void* dest, void* src, size_t num)
{
	void* ret = dest;// 为了防止dest中的数据指针位置变动
	assert(dest && src);
    // 从前向后拷贝的判断有两种
    // 1.dest(char*)src + num,这种情况dest就越过了我们要拷贝的src的所有字节,也就不存在内存会不会重叠的问题了。
	if (dest < src) {
		while (num--)
		{
			*(char*)dest = *(char*)src;
			++(char*)dest;// 先进行强制转换操作,再进行前置++
			++(char*)src;
		}
	}
	else {// 从后向前拷贝
		while (num--)
		{
			// (若num=20则现在num=19,src+19正好找到第二十个字节)
			// 找到src中的最后一个字节赋值给dest的最后一个字节
			*((char*)dest + num) = *((char*)src + num);
		}
	}
	return ret;
}

memset -- 内存设置

  1. ptr是要设置内存地址

  2. value是你要设置的字符

  3. num的单位是字节

代码如下(示例):

void * memset ( void * ptr, int value, size_t num );

int main ()
{
  char str[] = "almost every programmer should know memset!";
  memset (str,'-',6);
  puts (str);// ------ every programmer should know memset!
  return 0;
}

memcmp

代码如下(示例):

int memcmp ( const void * ptr1, const void * ptr2, size_t num );
  1. 若ptr1的前num个字节小于ptr2的前num个字节,返回一个小于0的整数

  2. 若ptr1的前num个字节等于ptr2的前num个字节,返回一个等于0的整数

  3. 若ptr1的前num个字节大于ptr2的前num个字节,返回一个大于0的整数

int main()
{
	int arr1[] = { 1,3,3,4,5,6,7,8,9,10 };
	int arr2[] = { 1,2,3,5,7,6,8,2,5,6 };
	int ret = memcmp(arr1, arr2, 8);
	printf("%d", ret);
	return 0;
}

你可能感兴趣的:(C语言,c语言)