考研C语言复习进阶(2)

;一.字符函数和字符串函数

1.1strlen

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

参数指向的字符串必须要以 '\0' 结束。
注意函数的返回值为size_t,是无符号的( 易错 )
学会strlen函数的模拟实现

//递归
//int my_strlen1(const char* str)
//{
//	assert(str != NULL);
//	if (*str != '\0')
//		return 1 + my_strlen(str + 1);
//	else
//		return 0;
//}
//
指针-指针
//int my_strlen2(const char* str)
//{
//	const char* start = str;
//	assert(str != NULL);
//	while (*str)
//	{
//		str++;
//	}
//	return str - start;
//}
//循环
//int my_strlen(const char* str)
//{
//	assert(str != NULL);
//	int count = 0;
//	while (*str != '\0')
//	{
//		count++;
//		str++;
//	}
//	return count;
//}

库里面实现的是size_t的

注意: 

-3被当成无符号数也是大于0的,所以一直都输出大于号

 1.2strcpy

Copies the C string pointed by source into the array pointed by destination, including the
terminating null character (and stopping at that point).
源字符串必须以 '\0' 结束。
会将源字符串中的 '\0' 拷贝到目标空间。
目标空间必须足够大,以确保能存放源字符串。
目标空间必须可变。
学会模拟实现。 

//char* my_strcpy(char* dest, const char* src)
//{
//	char* ret = dest;
//	assert(dest && src);
//	while (*dest++ = *src++)
//	{
//		;
//	}
//	return ret;
//}

1.3strcat

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' 结束。
目标空间必须有足够的大,能容纳下源字符串的内容。
目标空间必须可修改。
字符串自己给自己追加,如何?

解决不了这种问题

//char* my_strcat(char* dest, const char* src)
//{
//	assert(dest && src);
//	char* ret = dest;
//
//	//1. 找目标空间的\0
//	while (*dest)
//	{
//		dest++;
//	}
//	//2. 追加
//	while (*dest++ = *src++)
//	{
//		;
//	}
//	return ret;
//}

1.4 strcmp

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的数字         

那么如何判断两个字符串 ?

1.5 strncpy

 如果n大于源字符串长度,则会补'\0'

//dest为目标数组,src为源数组,n为要复制的字符个数
char* My_strncpy(char* dest, const char* src, int n)
{
	assert(dest != NULL);//保证dest非空
	assert(src != NULL); //保证src非空
	char* ret = dest;    //将dest首地址储存在ret中,在之后dest++运算中,可以方便找到
	while (n)   //一次复制一个字符,要复制n次
	{
		*dest = *src;  //复制
		src++;    //源地址往后+1
		dest++;   //目标地址往后+1
		n--;      //跳出循环条件
	}
	return ret;  //返回目的数组的首地址
}
 

1.6 strncmp

 

//以null结尾的字符串 const修饰防止字符串被修改,进行保护
int My_strncmp(const char* str1, const char* str2, int n)
{
	if (!n)   //n=0时,无字符要比,直接return 0
		return 0;
	while (--n && *str1 && *str1 == *str2) //当字符相等且不为’\0‘时比较下个字符,知道n=0比完
	{
		str1++;
		str2++;
	}
	return *str1 - *str2;//字符不相等时,(*str1 - *str2)可以满足返回值正负的需求
}

1.7 strstr 

看我的另一篇博客,KMP算法

1.8strncat 

//dest为前面的字符串,src为后面要加的字符串,n为要拷贝的字符个数
char* My_strncat(char* dest, const char* src, int n)
{
	char* ret = dest;  //将dest首地址储存在ret中,在之后dest++运算中,可以方便找到
	assert(dest != NULL && src != NULL);  //保证dest、src非空
	while (*dest != '\0')//用指针往后一个个找,找到dest结尾的‘\0’
	    dest++;
	while (n && (*dest++ = *src++) != '\0')//把src里的字符一个个放入dest后
		//(*dest++ = *src++) 表示先把*src赋给*dest,再把两个指针同时往后移一位(方便下次循环)
	     n--;   //循环跳出条件
	*dest = '\0';      //字符追加完成后,再追加’\0’
	return ret; //返回dest字符串起始地址
}

 

1.9strtok

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

strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:
strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容
并且可修改。)
strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串
中的位置。
strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标
记。
如果字符串中不存在更多的标记,则返回 NULL 指针。

strtok 函数大概的运行实现

#include <string.h>
#include <stdio.h>
 
int main () {
   char str[80] = "aaa - bbb - ccc";
   const char s[2] = "-";   //字符串里可以是一个分隔符,也可以是分隔符的集合
   char *token;
   
   /* 获取第一个子字符串 */
   token = strtok(str, s);
   
   /* 继续获取其他的子字符串 */
   while( token != NULL ) {
      printf( "%s\n", token );
    
      token = strtok(NULL, s);
   }
   
   return(0);
}

实现代码:

char *strtok(char *str,const char *delim)
{
    static char *next_start = NULL;  //保存到静态存储区,函数返回后不会被销毁

    if(str == NULL && (str = next_start) == NULL)
    {
        return NULL;
    }

    char       *s = str;
    const char *t = NULL;

    while(*s)
    {
        t = delim;

        while(*t)
        {
            if(*t == *s)
            {
                next_start = s + 1;

                if(s == str)    //第一个字符就是分隔符
                {
                    str = next_start;
                    break;
                }
                else
                {
                    *s = '\0';
                    return str;
                }
            }
            else
            {
                t++;
            }
        }

        s++;
    }

    return NULL;
}

1.10 strerror

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

 错误码转换为字符串

/* strerror example : error list */
#include <stdio.h>
#include <string.h>
#include <errno.h>//必须包含的头文件
int main ()
{
    FILE * pFile;
 pFile = fopen ("unexist.ent","r");
 if (pFile == NULL)
  printf ("Error opening file unexist.ent: %s\n",strerror(errno));
  //errno: Last error number
 return 0;
}
Edit & Run

 perror=printf+strerror

1.***字符分类函数 

 

#include <stdio.h>
#include <ctype.h>
int main ()
{
 int i=0;
 char str[]="Test String.\n";
 char c;
 while (str[i])
{
  c=str[i];
  if (isupper(c))
    c=tolower(c);
  putchar (c);
  i++;
}
 return 0;
}

 1.11 memcpy

函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
这个函数在遇到 '\0' 的时候并不会停下来。
如果source和destination有任何的重叠,复制的结果都是未定义的。 

 实现:

void * memcpy ( void * dst, const void * src, size_t count)
{
    void * ret = dst;
assert(dst);
 assert(src);
    /*
    * copy from lower addresses to higher addresses
    */
    while (count--) {
        *(char *)dst = *(char *)src;
        dst = (char *)dst + 1;
        src = (char *)src + 1;
   }
    return(ret);
}

使用: 

#include <stdio.h>
#include <string.h>
struct {
 char name[40];
 int age;
} person, person_copy;
int main ()
{
 char myname[] = "Pierre de Fermat";
 /* using memcpy to copy string: */
 memcpy ( person.name, myname, strlen(myname)+1 );
 person.age = 46;
 /* using memcpy to copy structure: */
 memcpy ( &person_copy, &person, sizeof(person) );
 printf ("person_copy: %s, %d \n", person_copy.name, person_copy.age );
 return 0;
}

 

 1.12 memmove

和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
如果源空间和目标空间出现重叠,就得使用memmove函数处理。 

void * memmove ( void * dst, const void * src, size_t count)
{
    void * ret = dst;
    if (dst <= src || (char *)dst >= ((char *)src + count)) {
        /*
        * Non-Overlapping Buffers
        * copy from lower addresses to higher addresses
        */
        while (count--) {
            *(char *)dst = *(char *)src;
            dst = (char *)dst + 1;
            src = (char *)src + 1;
       }
   }
    else {
        /*
        * Overlapping Buffers
        * copy from higher addresses to lower addresses
        */
        dst = (char *)dst + count - 1;
        src = (char *)src + count - 1;
        while (count--) {
            *(char *)dst = *(char *)src;
            dst = (char *)dst - 1;
            src = (char *)src - 1;
       }
   }
    return(ret);
}

1.13 memcmp

比较从ptr1和ptr2指针开始的num个字节
返回值如下: 

int my_memcmp(const void *ptr1, const void *ptr2, int num)
{
	//先强制类型转换
	const char *tmp_ptr1 = (const char *)ptr1;
	const char *tmp_ptr2 = (const char *)ptr2;
	//循环num次,比较出就返回值
	for (int i = 0; i < num; i++)
	{
		if (*(tmp_ptr1+i) > *(tmp_ptr2+i))
			return 1;
		else if (*(tmp_ptr1+i) < *(tmp_ptr2+i))
			return -1;
	}
	//比较完num个字节了,仍没返回,完全相等返回值0。
	return 0;
}

 1.14memset

ptr是内存块首字节地址。
value是要赋的值,这个值应该不超过一个无符号字节大小即28-1
num是需要赋值的字节数。
memset返回值是ptr首字节地址,并且是void*型,接收返回值需要强制类型转换。

void *my_memset(void *ptr, int value, int num)
{
	//强制类型转换为无符号char*型
	unsigned char *tmp_ptr = (unsigned char *)ptr;
	//循环num次
	for (int i = 0; i < num; i++)
	{
		//因为char是特殊的整型,所以可以直接赋值
		*tmp_ptr++ = value;
	}

	return ptr;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值