C语言n番战--字符串(六)

嵌入式之路,贵在日常点滴

                                                                ---阿杰在线送代码

目录

一、字符串的基本概念

二、字符串的定义方式与输出

二、字符串的结尾是 ’ \0 ’

三、sizeof和strlen的区别 

四、 动态开辟字符串

malloc函数 

free函数 

calloc函数  用得少

realloc函数

malloc、calloc、realloc小结

memset函数

五、字符串常用的API(API:预先定义的函数)

gets() 

puts()

非法内存问题 

fgets()

fputs() 

strcpy、strncpy —— 拷贝 

函数名:assert()

strcat —— 拼接

strcmp —— 比较 

strncmp

strchr、strstr —— 检索 

strlwr、strupr —— 大小写转换 

strtok —— 分割 

一、字符串的基本概念

  • 在 C 语言中,字符串实际上是使用空字符 \0 结尾的一维字符数组。因此,\0 是用于标记字符串的结束。
  • 空字符(Null character又称结束符,缩写 NUL,是一个数值为 0 的控制字符,\0 是转义字符,意思是告诉编译器,这不是字符 0,而是空字符。
  • 字符串是位于双引号中的字符序列
  • 在内存中以“\0”结束,所占字节比实际多一个

  • 在C语言中没有专门的字符串变量,通常用一个字符数组来存放一个字符串
    • 当把一个字符串存入一个数组时,会把结束符‘\0’存入数组,,并以此作为该字符串是否结束的标志。

二、字符串的定义方式与输出

#include "stdio.h"

int main()
{
	//int i;
	//定义方式一
	char cdata[] = {'h','e','l','l','o'};//字符数组
	//定义方式二
	char cdata2[] = "hello";//字符串 后面默认自加/0 字符串变量:允许被修改
	//定义方式三
	char *pchar = "hello";//字符串 后面默认自加/0   字符串常量:不允许被修改					
	
	//输出方式一:遍历的方式输出字符串
	for(int i=0;i<5;i++)
	{
		//printf("%c",cdata[i]);
		//printf("%c",cdata2[i]);
		printf("%c",*(pchar+i));
	}
	
	// 输出方式二:%s:字符串型格式符输出
	printf("%s\n",cdata);
	printf("%s\n",cdata2);
	printf("%s\n",pchar);
	
	//输出方式三:调用字符串API
	puts(pchar);
	
	return 0;
}

运行结果:

hellohello
hello
hello
hello 

注意:

1、

定义方式一是一个数组,不是字符串
C语言中确定一个字符串结束标记为'\0',一个大小为N的字符数组,最多能存1个长度为N-1的字符串,加上一个结束标记就是N个了,
如果没有结束标记,那就不能当字符串来处理,而只能当数组来处理 

2、

定义方式二是字符串变量,其字符允许被修改

定义方式三是字符串常量,其字符允不许被修改

    //检测字符串变量和字符串常量
    cdata2[1] = 'a';//正常执行
    *pchar = 'm';//强行改变会出现段错误,后文无法正常执行

3、

//省略元素个数时, 不能省略末尾的\0

// 不正确地写法,结尾没有\0 ,只是普通的字符数组

char name4[] = {'l','n','j'};

4、

//"中间不能包含\0", 因为\0是字符串的结束标志
//\0的作用:字符串结束的标志
    char name[] = "c\0ool";
    printf("name = %s\n",name);
输出结果: c

5、

其中第二种定义方式 char cdata2[] = "hello"; 不指明数组的大小,只给出了数组名,而数组名的值是个指针常量,也就是数组第一个元素的地址。

是不是可以猜想,首地址就是字符串的关键呢?

指针也指明了地址,故可以用指针的方式定义字符串,即字符串指针。也是定义字符串的常用方式。

char *pchar = "hello";//字符串 后面默认自加/0   字符串常量:不允许被修改

注意: 这里说的定义是同时赋值,而不是等待赋值。之所以不需要给指针pchar分配空间,是因为进行了初始化,编译的时候系统就知道需要分配多大的空间,否则要开辟空间。(后文中也有涉及)。 

char *p;//野指针,没有明确的内存指向,危险

在这里需要开辟空间 后文会讲如何开辟

*p = 'a';//导致段错误

二、字符串的结尾是 ’ \0 ’

#include "stdio.h"

int main()
{
	//字符数组和字符串的区别:
    char cdata[] = {'h','e','l','l','o'};//字符数组
	char cdata2[] = "hello";//字符串的结束标志'\0'
	int len;
	
	len = sizeof(cdata)/sizeof(cdata[0]);	
	printf("len = %d\n",len);
	
	//请计算数组cdata的元素个数
	//len = sizeof(cdata)/sizeof(char);
	len = sizeof(cdata2)/sizeof(cdata2[0]);	
	printf("len = %d\n",len);
	
	return 0;
}

运行结果:

len = 5
len = 6

使用字符数组然后以%s输出,可能性上会出现问题, 那么如果非要用字符数组来表示字符串并以%s输出呢。

char cdata[6] ={'h','e','l','l','o','\0'};//字符串的结束标志\0

printf("%s",cdata); 

三、sizeof和strlen的区别 

#include "stdio.h"
#include "string.h"

void test()
{
	
}

int main()
{
	char cdata[128] = "hello";//字符串的结束标志'\0'
	printf("sizeof:%d\n",sizeof(cdata));//算的是数组的大小
	printf("strlen:%d\n",strlen(cdata));//算的是字符串的大小
	
	char *p = "hello";
	void (*ptest)();
	ptest = test;
	
	//p是一个char *,sizeof来计算的时候,得出是计算机用多少字节来表示一个地址 
                                        即指针变量所占用的内存大小
	printf("sizeof:p      :%d\n",sizeof(p));
	printf("sizeof:char*  :%d\n",sizeof(char*));
	printf("sizeof:int*   :%d\n",sizeof(int*));
	printf("sizeof:ptest  :%d\n",sizeof(ptest));
	printf("sizeof:char   :%d\n",sizeof(char));
	printf("strlen        :%d\n",strlen(p));//算的是字符串的大小
	
	return 0;
}

运行结果

sizeof:128
strlen:5

指针变量所占用的内存大小仅仅取决指针本身的大小,不取决于指针指向的数据类型大小
sizeof:p      :8
sizeof:char*  :8
sizeof:int*   :8
sizeof:ptest  :8
sizeof:char   :1
strlen        :5 

实际上hello长这样: 

hello\0\0\0\0\0\0..........

strlen当遇到\0时便停止计算,是专门用来计算字符串的长度。 

strlen使用注意字符数组当做字符串来用?

有一个说法是:把字符数组当做字符串来使用时,最后一个元素要是'\0',否则strlen()计算时就会发生“停不下来”,直到遇到内存的'\0',故计算出来就不会是一个准确的值。例如:

#include 
#include 

int main()
{
        char array[5] = {'a','b','c','d','e'};
        printf("%d\n",strlen(array));

        return 0 ;
}

放在树莓派上运行,结果都是5。而在啊哈C中,都是6。 

四、 动态开辟字符串

在C中我们开辟内存空间有两种方式 :

1.静态开辟内存

例如:

int a;
int b[10];

这种开辟内存空间的特点是

所开辟的内存是在栈中开辟固定大小的 ,如a是4字节 ,数组b是40字节 ,并且数组在申明时必须指定其长度 , 如果是全局数组的话,内存是在编译时分配好的,如果是局部变量数组的话,运行时在栈上静态分配内存。不管是全局数组还是局部数组,它们都有一个特点,那就是数组大小是确定的,是代码中写死的。那如果我们想在程序运行时才确定一个数组的大小 , 前两种在栈上分配内存的方法显然是不行的 , 举个例子 :
 

int n;
scanf("%d", &n);
int a[n];

这样编写会在编译时出错 , 编译器会提醒[ ]中应为常量表达式 , 在C中定义数组时可以用的有以下几种 ,例:

#define N 10
enum NUM{
	M=10
};
int a1[N];
int a2[10];
int a3[M];

需要注意的是 ,C中const int n =10 ; n并不能作为数组长度定义数组 , 但C++中则可以 , 
但我们对于开辟空间的需求 , 往往不限于此 , 最常见的定义数组时数组大小在程序运行时才知道的 , 静态开辟就已经无能为力 . 当然有静态开辟 ,肯定也有动态开辟 ,接下来我们就来看动态开辟内存空间
2.动态开辟内存 :
在C中动态开辟空间需要用到三个函数 :
malloc(), calloc(), realloc() ,这三个函数都是向堆中申请的内存空间.
在堆中申请的内存空间不会像在栈中存储的局部变量一样 ,函数调用完会自动释放内存 , 需要我们手动释放 ,就需要free()函数来完成.
下面让我们来看看这几个函数各自的特点, 用法, 区别, 联系.

malloc函数 

void * malloc(size_t size)

1).malloc()函数会向中申请一片连续可用内存空间
2).若申请成功 ,返回指向这片内存空间的指针 ,若失败 ,则会返回NULL, 所以我们在用malloc()函数开辟动态内存之后, 一定要判断函数返回值是否为NULL.
3).返回值的类型为void*型, malloc()函数并不知道连续开辟的size个字节是存储什么类型数据的 ,所以需要我们自行决定 ,方法是在malloc()前加强制转化,转化成我们所需类型 ,如: (int*)malloc(sizeof(int)*n).
4).如果size为0, 此行为是未定义的, 会发生未知错误, 取决于编译器

具体怎么用呢 ,举个例子 . 

int *p = NULL;
int n = 0;
scanf("%d", &n);
p = (int*)malloc(sizeof(int) * n);
if(p != NULL){
    //....需要进行的操作
}

这时就相当于创建了一个数组 p[n] ,这个n的值并不需要像定义一个普通数组一样必须是常量, 可以使程序运行时得出的, 或是用户输入的

free函数 

void free(void* ptr)
在堆中申请的内存空间不会像在栈中存储的局部变量一样 ,函数调用完会自动释放内存 , 如果我们不手动释放, 直到程序运行结束才会释放, 这样就可能会造成内存泄漏(用俗话来理解就是一旦是在循环当中一直申请,没有释放,那么堆的存储空间被一直申请直到溢出 会发生臆想不到的事情 我还没遇到过 遇到来补上), 即堆中这片内存中的数据已经不再使用, 但它一直占着这片空间, (通俗说就是就是占着茅坑不拉屎), 所以当我们申请的动态内存不再使用时 ,一定要及时释放 .

1).如果ptr没有指向使用动态内存分配函数分配的内存空间,则会导致未定义的行为。
2).如果ptr是空指针,则该函数不执行任何操作。
3).此函数不会更改ptr本身的值,因此它仍指向相同(现在已经无效)的位置(内存)
4).在free()函数之后需要将ptr再置空 ,即ptr = NULL;(防止悬挂指针-野指针的一种)如果不将ptr置空的话 ,后面程序如果再通过ptr会访问到已经释放过无效的或者已经被回收再利用的内存, 为保证程序的健壮性, 一般我们都要写ptr = NULL; . 

注意 : free()不能重复释放一块内存, 如:

free(ptr);
free(ptr);

是错的, 已经释放过的内存不能重复释放, 会出现内存错误 

free()具体用法, 举个例子 : 

int *p = NULL;
int n = 0;
scanf("%d", &n);
p = (int*)malloc(sizeof(int) * n);
if(p != NULL){
    //....需要进行的操作
}
//操作完成 ,不再使用这片内存空间
free(p);
p = NULL;

malloc和free的一些问题注意点

1.malloc 等空间申请都是在堆上进行申请,最后必须由free来进行释放。堆上的空间是由程序员自己管理。

2.malloc是一个函数,表明了堆空间说在程序运行起来之后,再在系统上申请的,空间只申请不释放,会造成内存泄露问题!

3.那free是做了什么呢?他是把开辟的空间给清除了?还是把指针给清空了?

其实都不是,free做的是取消了指针和所对应内存的指向 “关系”。

在实际申请空间的时候,真实给你的空间是要大于你所需要的,但是你只能使用你要的大小,多出来的字节,用来维护刚刚说的那种关系,以及保存该次申请的 元数据(属性数据):用户申请的空间有多大,所以在free传参的时候只用传入你开辟空间的起始地址就好了,根据属性数据free函数就知道该释放多少空间。

4.那我不想释放那么多可以吗?我按照以下代码free。

free(p+4);

是不行的!堆空间必须整体申请整体释放。 

calloc函数  用得少

void * calloc(size_t num,size_t size)
malloc()函数的区别只在于, calloc()函数会在返回地址之前将所申请的内存空间中的每个字节都初始化为0 .

1).calloc()函数功能是动态分配num个大小(字节长度)为size的内存空间 .
2).若申请成功 ,,返回指向这片内存空间的指针 ,若失败 ,则会返回NULL, 所以我们在用calloc()函数开辟动态内存之后, 一定要判断函数返回值是否为NULL.
3).返回值的类型为void*型, calloc()函数虽然分配num个size大小的内存空间 ,但还是不知道存储的什么类型数据 ,所以需要我们自行决定 ,方法是在calloc()前加强制转 ,转化成我们所需类型 ,如: (int*)calloc(num, sizeof(int)).
4).如果size与num有一个或都为0, 此行为是未定义的, 会发生未知错误, 取决于编译器

所以如何我们对申请的内存空间的内容要求初始化,那么可以很方便的使用calloc函数来完成这个需求。
例如 :(此例子找别人的)

C语言n番战--字符串(六)_第1张图片

C语言n番战--字符串(六)_第2张图片

只是有一点区别,malloc没做初始化,随机值,malloc效率更高一点。calloc做了初始化,效率更低一点。 

realloc函数

void * realloc(void * ptr,size_t size)

realloc()函数让动态内存管理更加灵活 .在程序运行过程中动态分配内存大小,  如果分配的太大 ,则浪费空间, 如果太小, 可能还是会出现不够用的情况 .为了合理的利用内存,我们一定会对内存的大小做灵活的调整。那realloc() 函数就可以做到对动态开辟内存大小的调整(既可以往大调整, 也可以往小了调整) .

1).ptr为需要调整的内存地址
2).size为调整后需要的大小(字节数)
3).若调整成功, 返回值为调整大小后内存的起始位置(也就是指向调整后内存的指针), 若失败(当没有内存可以分配时, 一般不会出现), 则返回NULL, 所以还是要对返回值判空
4).如果ptr是空指针, 则和malloc()函数一样作用一样

malloc、calloc、realloc小结

1).malloc()和calloc()函数用法一样, 唯一的区别是calloc()会对所申请内存的每个字节初始化为0

2).malloc(), calloc(), realloc()申请的内存不再使用时 ,一定要用free()释放 ,否则会造成内存泄漏

3).p = realloc(ptr, size)函数返回值不为空时, 释放内存时不需写free(ptr) ,只需写free(p) 

memset函数

memset是一个初始化函数,作用是将某一块内存中的全部设置为指定的值。

void *memset(void *s, int c, size_t n);  

  • s指向要填充的内存块。
  • c是要被设置的值。
  • n是要被设置该值的字符数。
  • 返回类型是一个指向存储区s的指针。

讲了这么多,来个例子吧

#include "stdio.h"
#include "string.h"
#include "stdlib.h"

int main()
{
	char *p;//野指针
	p = (char *)malloc(1);//p有了具体的内存指向	
	*p = 'c';
	free(p);//动态开辟后重新指向,记得释放,不然存在内存泄露的风险
	p = NULL;//free之后让指针指向NULL,防止悬挂指针
	
	p = (char *)malloc(12);//开辟12个字节的内存空间
	if(p == NULL)
	{
		printf("malloc error\n");
		exit(-1);
	}
	memset(p,'\0',12);//内存空间初始化
	printf("扩容地址:%x\n",p);
	int len = strlen("chenlichen123456789");
	int newlen = len - 12 + 1;//+1为了存放\0
	p = realloc(p,newlen);//扩容
	printf("扩容后地址:%x\n",p);
	strcpy(p,"chenlichen123456789");
	puts(p);
	puts("end\n");

	return 0;
}

 运行结果:

扩容地址:213d0
扩容后地址:213d0
chenlichen123456789
end

五、字符串常用的API(API:预先定义的函数)

Application Programming Interface,应用程序接口

gets() 

 (1) 头文件    
        #include
        

 (2) 函数原型    
        char *gets(char *s);
        
 (3) 功能    
      从标准输入设备读入字符,并保存到指定的内存空间, 直到出现换行符或读到文件结尾为止
        
 (4) 参数   
        s: 字符串首地址
        
 (5) 返回值   
        成功: 返回读入的字符串
        失败: NULL
    
 (6) 和scanf("%s",s);区别 
    scanf(); 遇到空格,读取结束 (默认以空格分开) ,并且不会做越界检查------这也是, 编译器建议不用scanf的原因 ***
    gets(); 允许有空格, 但是也不做越界检查 ***

puts()

(1) 头文件

#include

(2) 函数原型

int puts(const char *s);

(3) 功能

标准设备输出s字符串,完成后自动在屏幕输出一个'\n'

(4) 参数

s: 字符串首地址

(5) 返回值

成功 : 非负数

失败 : -1

非法内存问题 

#include 

int main()
{
    char *pstr; //野指针  乱指
    //char *pstr = NULL;//什么都不指
                        //二者均会报错
    printf("请输入字符:\n");
    
    gets(pstr);   //字符串输入     或者用scanf
    //scanf("%s",pstr);
    
    puts(pstr);   //字符串输出

    return 0;
}

以上代码会报错 段错误
因为造成非法内存访问(不论使其为野指针还是使其等于NULL) 

如何解决以上问题:
1.定义成数组的形式:

char pstr[128] = {'\0'};

2.开辟空间:

char *pstr;
pstr = (char*)malloc(128);
#include "stdio.h"
#include "stdlib.h"
#include "string.h"

int main()
{
	char *pstr;
	pstr = (char*)malloc(128);
	if(pstr == NULL)
	{
		puts("malloc error");
		exit(-1);//退出程序
	}
	memset(pstr,'\0',128);
	//char pstr[128] = {'\0'};
	
	printf("请输入一个字符串:\n");
	puts("请输入一个字符串:");
	gets(pstr);//字符串输入	
	puts(pstr);//字符串输出
	
	free(pstr);
	pstr = NULL;

	return 0;
}

运行结果:

请输入一个字符串:
请输入一个字符串:
a jie lihai
a jie lihai 

fgets()

(1) 头文件    
        #include
    
(2) 函数原型   
        char *fgets( char          *str, int count, FILE          *stream );
        char *fgets( char *restrict str, int count, FILE *restrict stream );
    

(3) 功能
    从给定文件流读取最多 count - 1 个字符并将它们存储于 str 所指向的字符数组。若文件尾出现或发现换行符则终止分析,后一情况下 str 将包含一个换行符。若读入字节且无错误发生,则紧随写入到 str 的最后一个字符后写入空字符。
    若 count 小于 1 则行为未定义。亦不指定是否写入空字符,若 count==1 。
    
(4) 参数
        str: char 数组元素的指针
        count: 的最大字符数(典型的为 str 的长度)
        stream: 取数据来源的文件流
        
(5) 返回值    
        成功: 返回字符
        失败或到文件结尾: NULL

    
(6) 实例    
        char buf[100];
        // 从stdin (代表标准输入,键盘),读取数据
        //注意也会把换行符读进去
        fgets( buf,sizeof(buf),stdin);

fputs() 

(1) 头文件    
        #include
    
(2) 函数原型    
        int fputs(const char *str,FILE *stream);

    
(3) 功能    
        将str所指定的字符串写入到stream所指定的文件中,字符串'\0'不写入文件中
    
(4) 参数    
        str : 字符串
        stream : 文件指针, 如果把字符串输出到屏幕上写为stdout
    
 (5) 返回值    
        成功 0
        失败 -1
    
 (6) 注意    
        fputs 是puts的文件版本,但是前者不会自动输出'\n'

strcpy、strncpy —— 拷贝 

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

把从src地址开始且含有NULL结束符的字符串复制到以dest开始的地址空间

char *strncpy(char *dest, const char *src, int n)

把src所指向的字符串中以src地址开始的前n个字节复制到dest所指的数组中,并返回被复制后的dest

#include 
#include 

int main()
{
        char* strDes = (char*)malloc(128);
        memset(strDes,'\0',128);
        char* strSrc = "clc mws";
        
        puts(strcpy(strDes,strSrc));
        
        memset(strDes,'\0',128);

        strncpy(strDes,strSrc,3);
        puts(strDes);

        return 0;
}

运行结果:

clc mws

clc

自己写

Strcpy 

#include "stdio.h"

char *myStrcpy(char *des,char *src)
{
	if((des == NULL) || (src == NULL))
	{
		return NULL;
	}
	
	char *bak = des;	
	while(*src != '\0')
	{
		*des = *src;
		des++;
		src++;
	}
	*des = '\0';
	
	return bak;
}

char *myStrcpy2(char *des,char *src)
{
	if((des == NULL) || (src == NULL))
	{
		return NULL;
	}
	
	char *bak = des;
	while(*src != '\0')
	{
		*des++ = *src++;
	}
	*des = '\0';
	
	return bak;
}

char *myStrcpy3(char *des,char *src)
{
	if((des == NULL) || (src == NULL))
	{
		return NULL;
	}
	
	char *bak = des;
	while((*des++ = *src++) != '\0');
	*des = '\0';
	
	return bak;
}

int main()
{
	char str[128] = {'\0'};
	char *p = "chenlichen mei wo handsome";
	
	myStrcpy3(str,p);
	puts(str);
	
	return 0;
}

运行结果:

chenlichen mei wo handsome 

Strncpy

#include "stdio.h"

char *myStrncpy(char *des,char *src,int count)
{
	if((des == NULL) || (src == NULL))
	{
		return NULL;
	}
	
	char *bak = des;
	while((*src != '\0') && (count>0))
	{
		*des++ = *src++;
		count--;
	}
	if(count > 0)
	{
		while(count > 0)
		{
			count--;
			*des++ = '\0';
		}
		return bak;
	}
	*des = '\0';
	
	return bak;
}

int main()
{
	char str[128] = {'\0'};
	char *p = "chenlichen mei wo handsome";
	
	myStrncpy(str,p,4);
	puts(str);
	
	return 0;
}

运行结果

chen 

函数名:assert()

原型:void assert( int expression )
功能:如果形参为假则终止程序
参数:真或假  注意:每个assert只能检查一个条件,如果多个条件不好判断是哪个条件的错误
具体:

  • 如果形参数为假,assert 向 stderr打印一条出错信息,信息包含文件名、表达式、行号,然后调用abort终止程序
  • 如果形参为真,程序继续执行

优点:可以方便我们进行程序调试,同时对于绝对不能出错(条件为假)的地方使用可以有效的预防出现更多的错误
缺点:assert是宏函数,频繁的调用会增加额外的开销,影响程序性能
禁用assert函数:在#include前面加上#define NDEBUG

#include "stdio.h"
#include "assert.h"

char *myStrcpy(char *des,char *src)
{
	//assert 的作用是现计算表达式 expression ,如果其值为假(即为0),
	//那么它先向 stderr 打印一条出错信息,然后通过调用 abort 来终止程序运行
	assert((des != NULL) && (src != NULL));//断言
	
	char *bak = des;
	while(*src != '\0')
	{
		*des = *src;
		des++;
		src++;
	}
	*des = '\0';
	
	return bak;
}

int main()
{
	char str[128] = {'\0'};
	char *pstr = NULL;	
	char *p = "chenlichen handsome";
	
	//myStrcpy(str, p);//没报错
	myStrcpy(pstr, p);//报错,非法内存问题?
	puts(str);
	
	return 0;
}

运行结果

Assertion failed!

Program: D:\jie-study\\C\\a.exe
File: demo_API2.c, Line 8

Expression: (des != NULL) && (src != NULL)

strcat —— 拼接

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

把src所指向的字符串(包括“\0”)复制到dest所指向的字符串后面(删除*dest原来末尾的“\0”),*src中原有的字符不变。返回指向dest的指针。

要保证dest足够长,以容纳被复制进来的src。

简单的示范:

#include 
#include 

int main()
{
        char str1[32] = "chenlichen";
        char str2[] = "meiwoshuai";

        //拼接后放在str1,所以str1的长度在定义的时候必须指明并且足够大
        puts(strcat(str1,str2));

        return 0;
}

运行结果:

chenlichenmeiwoshuai

自己写

#include "stdio.h"
#include "assert.h"
#include "string.h"

char *myStrcat(char *des,char *src)
{
	assert((des != NULL) && (src != NULL));
	
	char *bak = des;
	while(*des != '\0')
	{
		des++;
	}
	while((*des++ = *src++) != '\0');
	*des = '\0';
	
	return bak;
}

char *myStrcat2(char *des,char *src)
{
	char *bak = des;
	strcpy(des+strlen(des),src);
	
	return bak;
}

char *myStrcat3(char *des,char *src)
{
	assert((des != NULL) && (src != NULL));
	
	char *bak = des;
	for(;*des != '\0';des++);
	while((*des++ = *src++) != '\0');
	*des = '\0';
	
	return bak;
}

int main()
{
	char str[128] = "chenlichen";
	char *p = " mei wo handsome";
	
	myStrcat(str,p);
	puts(str);
	
	return 0;
}

运行结果:

chenlichen mei wo handsome 

strcmp —— 比较 

extern int strcmp(const char *s1,const char *s2);

当s1

当s1=s2时,返回值= 0;

当s1>s2时,返回正数。 

#include 
#include 

int main()
{       
        char str1[128]={'\0'};
        char str2[128]={'\0'};//最快的开辟字符串空间并初始化

        puts("请输入字符串1");  
        fgets(str1,128,stdin);
        puts("请输入字符串2");
        fgets(str2,128,stdin);
                
        if(strcmp(str1,str2) == 0){
                printf("一样\n");
        }
        else{
                printf("不一样\n");
        }

        return 0;
}

运行结果:

请输入字符串1
zhou
请输入字符串2
zhou
一样

自己写

#include 
#include 
#include 

int myStrcmp(char *str1, char *str2)
{
	int ret = 0;
	int n_str1 = 0;
	int n_str2 = 0;
	char *bakStr1 = str1;
	char *bakStr2 = str2;

	while( *str1 && *str2 && (*str1 == *str2))
    {
		str1++;
		str2++;
	}
	if(*str1 || *str2)
    {
		str1 = bakStr1;
		str2 = bakStr2;
		while(*str1)
        {
			n_str1 += *str1;
			str1++;
		}
		while(*str2)
        {
			n_str2 += *str2;
			str2++;
		}
	}
	ret = n_str1 - n_str2;
	if(ret < 0)
    {
		ret = -1;
	}
	if(ret > 0)
    {
		ret = 1;
	}
	return ret;
}
int main()
{
	char *p1 = "chmnlichend";
	char *p2 = "chenlzchend";

	int ret = myStrcmp(p1,p2);//-1 1 0
	if(ret == 0)
    {
		puts("两个字符串一样");
	}
	printf("RET = %d\n",ret);
	return 0;
}

strncmp

int strncmp ( const char * str1, const char * str2, size_t n )

功能是把 str1 和 str2 进行比较,最多比较前 n 个字节,若str1与str2的前n个字符相同,则返回0;若s1大于s2,则返回大于0的值;若s1 小于s2,则返回小于0的值。 

strchr、strstr —— 检索 

strchr 

char *strchr(const char *str, int c) 

在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置 

strstr

char *strstr(const char *haystack, const char *needle) 

在字符串 haystack 中查找第一次出现字符串 needle 的位置,不包含终止符 ‘\0’,并且返回第一次出现字符串 needle 的位置,注意了,是返回第一次出现的位置,不是返回字符串needle,很多人有这个误解 

#include 
#include 
#include 

int main()
{
        char haystack[32] = "chenlichen mei wo shuai";
        char needle[32] = {'\0'};
        char* ret = NULL;
        //ret = (char* )malloc(32);//如果不在while中malloc,将会段错误
                                   //ret的值是变化的,要一直动态分配空间

        while(1)
        {

                ret =(char* )malloc(32);
                memset(ret,'\0',32);

                memset(needle,'\0',32);//而这个字符数组只需要擦除原有数据

                printf("现有字符串:%s,请输入需要检索的字段\n",haystack);
               
                scanf("%s",needle);
                ret = strstr(haystack,needle);
                //这里的赋值决定了ret只能被定义为字符指针,而不是数组。
                
                if(ret != NULL)
                {
                        printf("子字符串是:%s\n",ret);
                }
                else
                {
                        puts("no find");
                }
        }
        return(0);
}

运行结果:

现有字符串:chenlichen mei wo shuai,请输入需要检索的字段
chen
子字符串是:chenlichen mei wo shuai

现有字符串:chenlichen mei wo shuai,请输入需要检索的字段
wo
子字符串是:wo shuai

现有字符串:chenlichen mei wo shuai,请输入需要检索的字段
shuai
子字符串是:shuai

strlwr、strupr —— 大小写转换 

strlwr和strupr不是标准C库函数,只能在VC中使用。linux gcc环境下需要自行定义这个函数。

如下:;

#include 

char* strlwr(char *str)
{
        if(str == NULL)
                return NULL;
		char *p = str;
        while (*p != '\0'){
                if(*p >= 'A' && *p <= 'Z')
                        //*p = (*p) + 0x20;//十六进制
					*p = (*p) + 32;//十进制
                p++;
        }
        return str;
}


char* strupr(char *str)
{
        if(str == NULL)
                return NULL;
		char *p = str;
        while (*p != '\0'){
                if(*p >= 'a' && *p <= 'z')
                        //*p = (*p) - 0x20;//十六进制
					*p = (*p) - 32;//十进制
                p++;
        }
        return str;
}

int main()
{
        char str[128] = {'\0'};
        printf("请输入字符串\n");
        fgets(str,128,stdin);
        printf("转换为小写:%s\n",strlwr(str));
        printf("转换为大写:%s\n",strupr(str));
        return 0;
}

运行结果 :

请输入字符串
A
转换为小写:a

转换为大写:A

strtok —— 分割 

分解字符串为一组字符串。s为要分解的字符串,delim为分隔符字符(如果传入字符串,则传入的字符串中每个字符均为分割符)。首次调用时,s指向要分解的字符串,之后再次调用要把s设成NULL。

原型
char *strtok(char s[], const char *delim);
注意:需要定义成数组的形式,否则可能会引起段错误
例如:char str[ ] = “hello world”;

功能
分解字符串为一组字符串。s为要分解的字符串,delim为分隔符字符串
例如:strtok(“abc,def,ghi”,","),最后可以分割成为abc def ghi.尤其在点分十进制的IP中提取应用较多。
strtok的函数原型为char *strtok(char *s, char *delim),功能为作用于字符串s,以包含在delim中的字符为分界符,将s切分成一个个子串;如果,s为空值NULL,则函数保存的指针SAVE_PTR在下一次调用中将作为起始位置。

说明
strtok()用来将字符串分割成一个个片段。参数s指向欲分割的字符串,参数delim则为分割字符串中包含的所有字符。当strtok()在参数s的字符串中发现参数delim中包含的分割字符时,则会将该字符改为\0 字符。在第一次调用时,strtok()必需给予参数s字符串,往后的调用则将参数s设置成NULL。每次调用成功则返回指向被分割出片段的指针

返回值
从s开头开始的一个个被分割的串。当s中的字符查找到末尾时,返回NULL。
如果查找不到delim中的字符时,返回当前strtok的字符串的指针。
所有delim中包含的字符都会被滤掉,并将被滤掉的地方设为一处分割的节点。

使用
strtok函数会破坏被分解字符串的完整,调用前和调用后的s已经不一样了。如果要保持原字符串的完整,可以使用strchr和sscanf的组合等。

#include "stdio.h"
#include "string.h"

int main()
{
	char str[80] = "This is - www.runoob.com - website";
	const char s[2] = "-";
	char *token;
	
	//获取第一个子字符串
	token = strtok(str,s);
	
	//继续获取其他的子字符串
	while(token != NULL)
	{
		printf("%s\n",token);
		token = strtok(NULL,s);
	}
	printf("str:%s\n",str);//对字符串解析以后,就破坏了原来的字符串,切记

	return 0;
}

 

你可能感兴趣的:(C语言_n番战,C语言)