【C语言复习】C语言中的库函数使用及其实现(字符串及内存相关)

写在前面

库函数的实现在C语言的复习中占据着举足轻重的地位,因为本身实现就考验个人对C语言指针等知识点的理解与应用。当中,出现考察频率比较高的也是比较难的点的是:strcpy, strlen, strstr, memcpy, memove, strcat, atoi, itoa. 我们着手于这些比较常出现的库函数的实现。

同时,我将从函数介绍(如何使用及使用的注意事项),模拟实现两方面来展开。

库函数

字符串函数

strlen

size_t strlen ( const char * str );

注意:

  • 字符串以’\0’ 为结束标志,strlen函数返回的是在字符串中‘\0’ 之前的字符个数,不含’\0’。
  • 必须以’\0’ 为结束,否则是随机值。
  • 注意函数返回值,size_t (一种无符号整型)

模拟实现:

//计数器方式
int myStrlen(const char* str)
{
	int count = 0;
	while(*str)
	{
		count++;
		str++;
	}
	return count;
}
//不可以创建临时变量(不能用计数器),递归。
int myStrlen(const char* str)
{
	if(*str == '\0')
	{
		return 0;
	}
	return 1+myStrlen(str+1);
}
//指针-指针,也可以得到字符串长度
int myStrlen(const char* s)
{
  char* p = s;
  while(*p != '\0')
  {
    p++;
  }
  return p - s;
}

strcpy

char* strcpy(char * destination, const char * source );

字符串拷贝:

  • 源字符串必须以’\0’结尾。
  • 源字符串中的’\0’ 也会被拷贝至目标空间。
  • 目标空间确保要足够大。
  • 目标空间必须可变,不能不让操作。
  • 返回的是destination字符串
  • 要求destination可变,所以一般用数组。不可以用个const char*类型的字符串,因为其本身不可变。

模拟实现:

char* myStrcpy(char* dest, const char* source)
{
	char* ret = dest;
  assert(dest != NULL);
  assert(source != NULL);
  while(*s != '\0')
  {
		*dest = *source;
    dest++;
    source++;
  }
  dest = '\0';
	return ret;
}

strcat

char * strcat ( char * destination, const char * source );
  • Appends a copy of the source string to the destination string. The terminating null character in destination is overwritten by the first character of source, and a null-character is included at the end of the new string formed by the concatenation of both in destination.
  • 追加字符串。
  • 源字符串必须’\0’结尾。
  • 目标空间必须足够大。
  • 目标空间必须可变。
  • 返回destination

模拟实现:

char* myStrcat(char* dest, const char* source){
	char* ret = dest;
  assert(dest);
  assert(source);
  while(*dest != '\0')
  {
    dest++;
  }
  while((*dest++ = *source++ ));
	return ret;
}

strcmp

int strcmp ( const char * str1, const char * str2 );
  • This function starts comparing the first character of each string. If they are equal to each other, it continues with the following pairs until the characters differ or until a terminating null-character is reached.
  • 第一个字符串大于第二个字符串,返回大于0的数字。
  • 等于,返回0.
  • 小于,返回小于0的数字。

模拟实现:

int myStrcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	// while(*str1++ == *str2++ && *str1 != '\0');
	while(*str1 == *str2 && *str1 != '\0'){
		str1++;
		str2++;
	}
	if(*str1 == *str2 && *str1 == '\0'){
		return 0;
	}
	return (int)(*str1 - *str2);
}

strncpy

char * strncpy ( char * destination, const char * source, size_t num );
  • 与strcpy的区别在于,多了一个参数num。
  • 表示从源字符串拷贝num个字符到目标空间。
  • 如果源字符串的长度小于num,追加0,致总长度为num。

不需要掌握带n的字符串函数的模拟实现,只需要掌握原始的,并且清楚他们的区别。

strncat

char * strncat ( char * destination, const char * source, size_t num );

strncmp

int strncmp ( const char * str1, const char * str2, size_t num );
  • 比较到出现字符不相同,或者一个字符串结束,或者num个字符比较完成。其余与strcmp完全相同。

strstr

char* strstr(const char* str1, const char* str2);
  • 寻找str1中的子串str2,如果找到了返回str2开始的位置,如果没找到返回空指针。
  • 一般用来寻找子串开始的位置然后进行修改。

模拟实现:

char* myStrstr(const char* str1, const char* str2){
	char* cp = (char*)str1;
	char* s1, *s2;
	if(*str2 == NULL)
		return ((char*) str1);
	while(*cp)
	{
		s1 = cp;
		s2 = ((char*) str2);
		while(*s1 && *s2 && !(*s1 - *s2) )
		{
			s1++, s2++;
		}
		if(*s2 == '\0')
			return cp;
		cp++;
	}
	return NULL;
}

strtok

char * strtok ( char * str, const char * sep );
  • sep参数是字符串,定义分隔符的合集。
  • 第一个参数str制定一个字符串,包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
  • strtok函数会找到str中下个标记,并将其用’\0’结尾,返回一个指向这个标记的指针。
  • strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容而且可以修改。
  • strtok函数的第一个参数若为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
  • strtok函数的第一个参数若不为NULL,函数将找到str中的第一个标记,并保存它在字符串中的位置。(等待调用第一个参数为NULL时再使用它)。

使用:

int main()
{
	char str[] = "Please! Give age to civilization, not civilization to age."
	char* p;
	printf("%s\n", str);
	p = strtok(str, ",.!");
	while(p != NULL)
	{
		printf("%s\n",p);
		p = strtok(NULL,",.!");
	}
	return 0;
}

//输出:
//Please
// Give age to civilization
// not civilization to age
  • 不要求掌握模拟实现

strerror

char * strerror ( int errnum );
  • 作用是返回错误码所对应的错误信息。
  • 必须包含头文件 <errno.h>
  • 如果运行程序失败,返回了错误信息,会保存在errno中,使用打印strerror(errno) 的方法可以打印出错误信息。

字符分类函数

函数参数符合下列条件返回true
iscntrl控制字符
isspace空白字符, 包括space : ’ ‘, 换页’\f’, 换行’\n’, 回车’\r’, 制表符’\t’或者垂直制表符’\v’
isdigit十进制数组0-9
isxdigit十六进制数字,包括十进制数字和大小写字母a-f,A-F
islower / isupper大写小写字母
isgraph图形字符
isalpha大小写字母
isalnum字母或者数字
ispunct标点符号
isprint任何可打印字符,包括图形字符和空白字符

字符转换函数

int tolower(int c);
int toupper(int c);
  • 转换成大写或者小写字母。

memcpy

void* memcpy(void* destination, const void* source, size_t num);
  • 函数memcpy从source的位置开始向后复制num个字节的数据,到destination的内存位置。
  • 遇到’\0’不会停下来。
  • 如果source和destination有任何重叠,复制结果都是未定义的。不能用来处理任何内存重叠的空间!

模拟实现:

void* myMemcpy(void* destination, const void* source, size_t num)
{
	void* ret = dest;
  assert(destination && source);
  while(num --){
    *(char*) destination = *(char*) source;
		destination = (char*)destination + 1;
    source = (char*)source + 1;
  }
  return ret;
}
//一定要强转成char*类型再去赋值和++;

memmove

void * memmove ( void * destination, const void * source, size_t num );
  • 和memcpy相近,区别是,memmove函数处理的源内存块和目标内存块是可以重叠的。
  • 如果源空间和目标空间出现重叠,就得使用memmove函数处理。

模拟实现:

void* myMemmove( void* destination, const void* source, size_t num)
{
	void* ret = destination;
	if(destination <= source || (char*destination >= ((char*)source + num))){
		//Non-overlapping
		//just like the implement of memcpy
		while(num--){
			*(char*)destination = *(char*)source;
			destination = (char*)destination + 1;
			source = (char*)source + 1;
		}
		else{
			//overlapping
			//copy from higher add to lower add.
			// in case of wrong override.
			destination = (char*)destination + num - 1;
			source = (char*)source + num - 1;
			while(num --){
				*(char*)destination = *(char*)source;
				destination = (char*)destination - 1;
				source = (char*)source - 1;
			}
		}
	}
	return ret;
}

memcmp

int memcmp ( const void * ptr1, const void * ptr2, size_t num );
  • 比较从ptr1 和ptr2 指针开始的num 个字节
  • 返回值:如果内存块中内容相同返回0,总之,返回(*ptr1 - *ptr2)的int值。
  • 模拟实现与strcmp类似,不再重复实现。

小结

本小节的内容主要是库函数的使用及其实现。需要注意的是使用上述的函数,都需要包含头文件,如下所示。

#include<string.h>//上述所有的str和mem函数
#include<errno.h>//使用strerror(errno);

其中比较重要的模拟实现已经列在上面,多复习多总结,尤其是拷贝内存的memmove函数,比较关键。加油!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值