字符串函数

Ladies and gentlemen,下面开始是我们要讲的宇宙究极无敌的函数:

 strlen    strcpy strcat strcmp     strstr strtor    strerro

首先我们来从这些函数,依一找共同点。

                          

瞪大你们的眼睛,看每个函数都这str,这说明了他们都是字符的一家族。了解前面的字母,这样就只需要知道后面的就好记了。

strlen

1.len  看着看着我们是不是很眼熟,是不是我们用来表示长度的变量名称。聪慧的你肯定已经联想到了这是一个能求字符长度的函数。

先让我们看看函数的原型:

size_t strlen(const char *str);

size_t 是无符号整数类型,const char* str 说明了传过来的指针参数所指向的数据无法更改。

聪明的宝子们肯定已经看出来了。

接下来的是我们要牢记的一些神奇秘诀:

1.字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包含 '\0' )
2.参数指向的字符串必须要以 '\0' 结束。
3.注意函数的返回值为size_t,是无符号的。
看完的你已经将strlen函数了解的透透的了。
oi  oi  oi 你不会以为了解完了就结束了吗? no no  no,我还有大招没给呢。
手搓一个strlen:

//1.方法一 计数器的方式
int my_strlen(const char* str)
{
	int count = 0;
	while (*str)
	{
		str++;
		count++;
	}
	return count;
}

//2.方法二 :递归的方式
int my_strlen(const char* str)
{
	if (*str == '\0')
	{
		return 0;
	}
	else
	{
		return 1 + my_strlen(str + 1);
	}

}

//3.方法三:指针-指针的方法
int my_strlen( char* str)
{
	char* p = str;
	while (*p != '\0')
	{
		p++;
	}
	return p - str;
}

strcpy strcat strcmp

cpy  cat  cmp  让我们来一步一步解刨。这三个都有c这个字母开头,我真聪明,哈哈。copy(复制) ,catenate(连接),compose(比较)。从这三个单词,我们就知道这三个函数分别是什么意思了,这说明了什么,说明了学好一手英语在边编程里还是有点用的。

先让我们看看这些函数的原型:

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

返回值是char类型的指针,参数有两个,一个是目标字符指针,一个是源字符指针。

我得冷静的分析一会,嗯,与copy(复制)有目标,有源头。!!!!,我知道了,它是将源头的字符传到目标字符里去。

接下来是我们要注意的:

1.源字符串必须以 '\0' 结束。
2.会将源字符串中的 '\0' 拷贝到目标空间。
3.目标空间必须足够大,以确保能存放源字符串。
4.目标空间必须可变。
聪明的你们稍所思考肯定知道这是为什么了。
学会模拟实现它。
char *my_strcpy(char *dest, const char*src) 
{ 
    char *ret = dest; 
    assert(dest != NULL);
    assert(src != NULL); 
 
     while((*dest++ = *src++)) 
     { 
             ; 
     } 
     return ret; 
}

strcat
char *strcat(char *dest, const char *src);

老样子,也是一样的。只不过dest不是空的了,是有字符数据的了。strcat是将src的字符追加到dest后面了,它是将src首字符代替dest里的“\0”,用src里的“\0”完成新的字符诞生。

接下来也是我们需要注意的:

源字符串必须以‘\0’结束

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

目标空间必须可修改

下面有一个问题:字符串自己给自己追加,如何?

这是不对的:会导致缓冲区溢出,因为如果 s1 是一个固定大小的字符数组,将其自身拼接会超出数组原本的空间,引发未定义行为。

学会模拟它:

char *my_strcat(char *dest, const char*src) 
{ 
     char *ret = dest; 
     assert(dest != NULL); 
     assert(src != NULL); 
     while(*dest) 
     { 
         dest++; 
     } 
     while((*dest++ = *src++)) 
     { 
         ; 
     }     
     return ret; 
}
strcmp
int strcmp(const char *s1, const char *s2);

作为比较的函数:

1.它返回的是int 类型 

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

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

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

3.对于strcmp判断:是以ASCII值进行判断的,将s1与s2依一对比   比谁的大小。直到出现不同字符或遇到字符串结束符 '\0' 

学会模拟它:

int my_strcmp (const char * src, const char * dst) 
{ 
 int ret = 0 ; 
 assert(src != NULL); 
 assert(dest != NULL); 
 while( ! (ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst) 
     ++src, ++dst; 
 if ( ret < 0 ) 
 {
     ret = -1 ; 
 }
 else if ( ret > 0 ) 
 { 
    ret = 1 ; 
 }
return( ret ); 
}

strncpy   strncat   strncmp

这些字符函数都与前面的功能相似,只不过函数新加了一个参数num 与数量相关

strncpy

char * strncpy ( char * destination, const char * source, size_t num );
1.拷贝num个字符从源字符串到目标空间。
2.如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num
3.与之前的strcat的注意事项一致。
模拟他:
char* strncpy(char* dest, const char* src, size_t num)
{

	char* ret = dest;
	assert(dest != NULL);
	assert(src != NULL);

	for (int i = 0;i <num;i++)
	{
		if ((*src) != '\0')
		{
			*dest++ = *src++;
		}
		else
		{
			*dest++ = '\0';
		}
	}
    *dest++ = '\0';
	return ret;


}

strncat 

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

  1.将源字符串的num个字符追加到目标字符串后。

  2.与之前的strcat的注意事项一致。

模拟他:

char* strncat(char* dest, const char* src, size_t num)
{
	char* ret = dest;
	assert(dest != NULL);
	assert(src != NULL);
	while (*dest)
	{
		dest++;
	}
	for(int i =0;i<num && *src;i++)
	{
		*dest++ = *src++;
	}
	*dest++ = '\0';
	return ret;

}

strncmp

int strncmp (const char * str1, const char * str2, size_t num);

1.将str2字符串与str1字符的num个字符进行比较。

2.有种情况,是num个字符比str1或str2多,那么对比到str1或str2的\0时停止,剩余num未比较的位置视为更小(因\0 ASCII 是 0,比字母 与 数字小)。

strstr

char * strstr ( const char *str1, const char * str2);

在str1字符串里查找str2字符串   其返回值是str2字符串出现在str1字符串的首个位置的地址。

如果没找到就会返回:NULL

学会模拟它:

char *my_strstr(const char *str1, const char *str2) 
{
    assert(str1);
    assert(str2);

    char *cp = (char *)str1;
    char *substr = (char *)str2;
    char *s1 = NULL;

    if (*str2 == '\0')
        return NULL;

    while (*cp)
    {
        s1 = cp;
        substr = (char *)str2;
        while (*s1 && *substr && (*s1 == *substr))
        {
            s1++;
            substr++;
        }
        if (*substr == '\0')
            return cp;
        cp++;
    }
    return NULL;
}

strtok

char * strtok ( char * str, const char * sep );

是一个将以sep里的字符标识如:,- 空格等等。在str里找到后其分隔符位置替换为 \0,直接修改原始字符串 str并返回的是原始字符串中被拆分出的子串的起始地址

1.首次调用传 str,后续传 NULL 可 “接着上次的位置继续分割”,直到返回 NULL 表示拆分完

token = strtok(str, delim);
while (token != NULL) {
    // 输出分割出的子串
    printf("%s\n", token);
    // 后续调用:传入 NULL、分隔符、上下文(复用第一次的 context)
    token = strtok(NULL, delim);
}

strerror

char *strerror(int errnum);

将错误码(如 errno 里的值 )转换成对应的错误描述字符串,方便开发者理解程序哪里出错。

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

字符函数

字符分类函数

函数 如果他的参数符合下列条件就返回真
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 任何图形字符
isprint 任何可打印字符,包括图形字符和空白字符

字符转换

int tolower(int c)

将大写字母转换为小写
int toupper(int c) 将小写字母转换为大写

内存函数

mencpy menmove mencmp

其实这些函数与之前的有相似之处

mencpy
void *memcpy(void *dest, const void *src, size_t n);
1.dest:指向目标内存区域的指针,用于接收拷贝的数据。

2.src:指向源内存区域的指针,待拷贝的数据从这里获取。

3.n:要拷贝的字节数,决定复制数据的长度

4.从src 指向地址开始,拷贝n 字节数据到dest 指向地址;若源和目标内存重叠,行为未定义(可能出错 ),适合无重叠场景快速拷贝。

学会模拟它:

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

    return(ret);
}
menmove
void *memmove(void *dest, const void *src, size_t n);
1.dest:指向目标内存区域的指针,用于接收拷贝的数据。

2.src:指向源内存区域的指针,待拷贝的数据从这里获取。

3.n:要拷贝的字节数,决定复制数据的长度

4.同样拷贝 n 字节数据从 src 到 dest,但支持源和目标内存重叠,内部会合理处理拷贝顺序(如从低地址到高地址,或反向拷贝 ),保证结果正确,是更安全的内存拷贝函数。

学会模拟它:

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); 
}
mencmp
int memcmp(const void *s1, const void *s2, size_t n);

1.s1,s2:指向两个待比较的内存区域的指

2.n:要比较的字节数,即从两个内存起始地址开始,对比前 n 个字节。

3.按字节逐个比较 s1 和 s2 指向内存的前 n 字节:

  • 若 s1 与 s2 内容相等,返回 0
  • 若 s1 对应字节序列 “大于” s2,返回正数(通常是差值,如 s1 某字节是 0x41s2 是 0x40,返回 1 );
  • 若 s1 对应字节序列 “小于” s2,返回负数 ,常用于二进制数据(如结构体、非字符串数据 )的比较。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐