C语言进阶之字符函数和字符串函数

重点介绍处理字符和字符串的库函数的使用和注意事项

求字符串长度

strlen

cpp 复制代码
size_t strlen ( const char * str );
  • 字符串已经'\0'作为结束标志,strlen函数返回的是在字符串中'\0'前面出现的字符个数(不包 含'\0')。
  • 参数指向的字符串必须要以'\0'结束。
  • 注意函数的返回值为size_t,是无符号的(易错)
  • 学会strlen函数的模拟实现

案例:

cpp 复制代码
 #include <stdio.h>
 int main()
 {
	 const char*str1 = "abcdef";
	 const char*str2 = "bbb";
	 if(strlen(str2)-strlen(str1)>0)
	 {
		 printf("str2>str1\n");
	 } 
	 else
	 {
	 	printf("srt1>str2\n");
	 }
	 return 0;
 }

长度不受限制的字符串函数

strcpy

cpp 复制代码
char* strcpy(char * destination, const char * source );
  • 源字符串必须以 '\0' 结束。
  • 会将源字符串中的 '\0' 拷贝到目标空间。
  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可变。
  • 学会模拟实现。

strcat

cpp 复制代码
char * strcat ( char * destination, const char * source );
  • 源字符串必须以 '\0' 结束。
  • 目标空间必须有足够的大,能容纳下源字符串的内容。
  • 目标空间必须可修改。
  • 字符串自己给自己追加,如何?

strcmp

cpp 复制代码
int strcmp ( const char * str1, const char * str2 );

标准规定:

  • 第一个字符串大于第二个字符串,则返回大于0的数字
  • 第一个字符串等于第二个字符串,则返回0
  • 第一个字符串小于第二个字符串,则返回小于0的数字
  • 那么如何判断两个字符串?

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

strncpy

cpp 复制代码
char * strncpy ( char * destination, const char * source, size_t num );
  • 拷贝num个字符从源字符串到目标空间。
  • 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。

strncat

案例:

cpp 复制代码
 /* strncat example */
 #include <stdio.h>
 #include <string.h>
 int main ()
 {
	 char str1[20];
	 char str2[20];
	 strcpy (str1,"To be ");
	 strcpy (str2,"or not to be");
	 strncat (str1, str2, 6);
	 puts (str1);
	 return 0;
 }

strncmp

案例:

cpp 复制代码
/* strncmp example */
 #include <stdio.h>
 #include <string.h>
 int main ()
 {
	 char str[][5] = { "R2D2" , "C3PO" , "R2A6" };
	 int n;
	 puts ("Looking for R2 astromech droids...");
	 for (n=0 ; n<3 ; n++)
	 if (strncmp (str[n],"R2xx",2) == 0)
	 {
	 printf ("found %s\n",str[n]);
	 }
	 return 0;
 }

字符串查找

strstr

cpp 复制代码
char * strstr ( const char *, const char * );

案例:

cpp 复制代码
#include <stdio.h>
#include <string.h>

int main ()
{
    char str[] = "This is a simple string";
    char * pch;

    pch = strstr(str, "simple");  // 查找子串 "simple"
    strncpy(pch, "sample", 6);     // 用 "sample" 替换 "simple"
    
    puts(str);                     // 输出修改后的字符串
    return 0;
}

strtok

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

案例:

cpp 复制代码
 /* strtok example */
#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "- This, a sample string."; // 定义一个输入字符串
    char *pch; // 指针,用于保存分割后每个词的地址

    // 打印原始字符串
    printf("Splitting string \"%s\" into tokens:\n", str);
    
    // 使用 strtok 函数,第一参数为待分割字符串,第二参数为分隔符
    pch = strtok(str, " ,.-");

    // 循环,直到 pch 为空
    while (pch != NULL) {
        printf("%s\n", pch); // 打印当前的词
        pch = strtok(NULL, " ,.-"); // 获取下一个词
    }

    return 0; // 返回 0,表示程序正常结束
}

错误信息报告

strerror

cpp 复制代码
char * strerror ( int errnum );

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

cpp 复制代码
/* 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;
 }

字符操作

案例:

/* isupper example */

#include <stdio.h>

cpp 复制代码
#include <ctype.h> // 引入ctype.h库以使用字符处理函数
#include <stdio.h> // 引入stdio.h库以使用输入输出功能

int main ()
{
    int i = 0; // 初始化索引变量
    char str[] = "Test String.\n"; // 定义一个字符串
    char c; // 声明字符变量

    // 循环遍历字符串,直到遇到字符串结束符'\0'
    while (str[i]) 
    {
        c = str[i]; // 获取当前字符
        if (isupper(c)) // 检查当前字符是否为大写字母
            c = tolower(c); // 如果是,则转换为小写字母

        putchar(c); // 输出当前字符
        i++; // 移动到下一个字符
    }
    return 0; // 返回0表示程序正常结束
}

内存操作函数

memcpy

cpp 复制代码
void * memcpy ( void * destination, const void * source, size_t num );
  • 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
  • 这个函数在遇到 '\0'的时候并不会停下来。
  • 如果source和destination有任何的重叠,复制的结果是未定义。
    案例:
cpp 复制代码
/* memcpy example */
 #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 );//myname 的内容复制到 person.name 中。strlen(myname) + 1 计算出字符串的长度并加上1,用于包括字符串结束符 \0。
	 person.age = 46;
	  /* using memcpy to copy structure: */
	 memcpy ( &person_copy, &person, sizeof(person) );//person 的内容复制到 person_copy 中。sizeof(person) 返回结构体的大小,
	 printf ("person_copy: %s, %d \n", person_copy.name, person_copy.age );
	 return 0;
 }

memmove

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

案例:

cpp 复制代码
/* memmove example */
 #include <stdio.h>
 #include <string.h>
 int main ()
 {
	 char str[] = "memmove can be very useful......";
	 memmove (str+20,str+15,11);
	 puts (str);
	 return 0;
 }

memset

cpp 复制代码
 int memcmp ( const void * ptr1, const void * ptr2, size_t num );
  • 比较从ptr1和ptr2指针开始的num个字节

案例:

cpp 复制代码
/* memcmp example */
 #include <stdio.h>
 #include <string.h>
 int main ()
 {
	 char buffer1[] = "DWgaOtP12df0";
	 char buffer2[] = "DWGAOTP12DF0";
	 int n;
	 n=memcmp ( buffer1, buffer2, sizeof(buffer1) );
	  if (n>0) printf ("'%s' is greater than '%s'.\n",buffer1,buffer2);
	 else if (n<0) printf ("'%s' is less than '%s'.\n",buffer1,buffer2);
	 else printf ("'%s' is the same as '%s'.\n",buffer1,buffer2);
	 return 0;
 }

memcmp

库函数的模拟实现

模拟实现strlen

三种方式: 方式1:

cpp 复制代码
//计数器方式
int my_strlen(const char * str)
 {
	 int count = 0;
	 while(*str)
	 {
		 count++;
		 str++;
	 }
	 return count;
 }

方式2:

cpp 复制代码
//不能创建临时变量计数器
int my_strlen(const char * str)
 {
	 if(*str == '\0')
	   return 0;
	 else
	   return 1+my_strlen(str+1);
 }

方式3:

cpp 复制代码
//指针-指针的方式
int my_strlen(char *s)
 {
	 char *p = s;
	 while(*p != '\0' )
	 p++;
	 return p-s;
 }

模拟实现strcpy

参考代码:

//1.参数顺序

//2.函数的功能,停止条件

//3.assert

//4.const修饰指针

//5.函数返回值

//6.题目出自《高质量C/C++编程》书籍最后的试题部分

cpp 复制代码
char *my_strcpy(char *dest, const char*src)
 {   
		char *ret = dest;
	    assert(dest != NULL);
	    assert(src != NULL);
	
	    
	    while((*dest++ = *src++))
	    {
	        ;
	    }
	    return ret;
 }

模拟实现strcat

参考代码:

cpp 复制代码
 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;
 }

模拟实现strstr

cpp 复制代码
#include <assert.h>

char *my_strstr(const char* str1, const char* str2) {
    assert(str1);
    assert(str2);
    
    // 如果子字符串为空,返回原字符串的起始地址
    if (*str2 == '\0') {
        return (char*)str1;  // 返回 str1 的起始地址
    }
    
    const char *cp = str1;  // 主字符串指针
    const char *substr;     // 子字符串指针
    const char *s1;         // 用于比较的指针
    
    while (*cp) {
        s1 = cp;            // s1 指向主字符串的当前字符
        substr = str2;     // substr 指向子字符串的起始字符
        
        // 比较 s1 和 substr 的字符
        while (*s1 && *substr && (*s1 == *substr)) {
            s1++;
            substr++;
        }
        
        // 如果子字符串的所有字符都匹配
        if (*substr == '\0') {
            return (char*)cp; // 返回子字符串在主字符串中的起始地址
        }
        
        cp++; // 主字符串指针向前移动一位
    }
    
    return NULL; // 如果未找到子字符串,返回 NULL
}

模拟实现strcmp

参考代码:

cpp 复制代码
 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 );
 }

模拟实现memcpy

参考代码:

cpp 复制代码
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);
 }

模拟实现memmove

参考代码:

cpp 复制代码
 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);
 }
相关推荐
序属秋秋秋28 分钟前
算法基础_数据结构【单链表 + 双链表 + 栈 + 队列 + 单调栈 + 单调队列】
c语言·数据结构·c++·算法
王鑫的博客8861 小时前
本地git操作
c语言·git
apcipot_rain1 小时前
【密码学——基础理论与应用】李子臣编著 第五章 序列密码 课后习题
算法·密码学
不要不开心了1 小时前
sparkcore编程算子
pytorch·分布式·算法·pygame
88号技师1 小时前
【2024年最新IEEE Trans】模糊斜率熵Fuzzy Slope entropy及5种多尺度,应用于状态识别、故障诊断!
人工智能·算法·matlab·时序分析·故障诊断·信息熵·特征提取
mldl_2 小时前
(个人题解)第十六届蓝桥杯大赛软件赛省赛C/C++ 研究生组
c语言·c++·蓝桥杯
清同趣科研2 小时前
R绘图|6种NMDS(非度量多维分析)绘图保姆级模板——NMDS从原理到绘图,看师兄这篇教程就够了
人工智能·算法
杜小暑2 小时前
冒泡排序与回调函数——qsort
c语言·算法·排序算法
徵6862 小时前
代码训练day27贪心算法p1
算法·贪心算法
Nigori7_3 小时前
day32-动态规划__509. 斐波那契数__70. 爬楼梯__746. 使用最小花费爬楼梯
算法·动态规划