第七章 字符串
男儿何不带吴钩?收取关山五十州。
文章目录
- 第七章 字符串
- 1.字符串的引入以及注意事项
- 1.1字符数组,与数组差不多
- 2.字符串的内存存放方式及结束标志
- 3.sizeof 与 strlen的区别
- 4.动态开辟字符串(难点)
- 5.几种字符串常用的API
- 6.C语言实现断言函数assert
- 7.字符串拼接strcat的使用及实现
- 8.字符串比较函数strcmp使用及实现
1.字符串的引入以及注意事项
1.1字符数组,与数组差不多
注意单个字符用单引号‘ ’ 字符串要用双引号“ ”
#include <stdio.h>
int main()
{char c = 'h';//定义的一个单字符,,这是以前的写法//一、字符串的定义//1.以往我们数组的书写方式int data[] = {1,2,3,4,5};//2.那么如果换成字符串呢?char cdata1[] = {'h','e','l','l','o'};//1.很蠢的一个方式char cdata2[] = "hello"; //2.对上面进行改进的第二个方法char *pchar = "hello"; //3.用指针的方法来定义字符串//二、遍历 //1.这种遍历字符串的方式依旧有些蠢for(int i = 0;i<5;i++){printf("cdata = %c\n",cdata[i]);printf("pchar = %c\n",*(pchar+i));//指针的地址偏移,然后再取内容}//2.第二种方法printf("%s",pchar);//%s是输出字符串格式声明,第一章有讲过putchar('\n');//输出单字符puts(pchar);//直接打出字符串return 0;
}
//这两个定义字符串的区别,,我如果想改里面单个的元素,第一个会成功,而指针存储的方式不会//1.这个是字符串变量,数值可以修改
char cdata2[] = "hello"; //2.对上面进行改进的第二个方法
cdata2[3] = 'm'; //这个会修改成功//2.字符串常量,不允许被修改
char *pchar = "hello"; //3.用指针的方法来定义字符串
*pchar = 'p'; //不会报错,但是会卡在这里
野指针
char *p; //野指针,并没有明确的内存指向,很危险
*p = 'a'; //会报错//注意指针的操作
1.保存地址可以,修改指向,指向字符串常量的地址空间
2.对野指针的内存空间操作不行
2.字符串的内存存放方式及结束标志
//和整形数组在存储上的区别
#include <stdio.h>
int main()
{ int len1,len2;//1.这个数组的大小是5int arr[] = {1,2,3,4,5};len1 = sizeof(arr)/sizeof(arr[0]);printf("arr的大小%d\n",len1);//2.而字符串的结束标志是'\0'(系统会自己加上这个标志,告诉说这个字符串已经结束),所以大小是6int carr[] = {"hello"};len2 = sizeof(carr)/sizeof(carr[0]);printf("carr的大小%d\n",len2);return 0;
}
3.sizeof 与 strlen的区别
//1.sizeof计算的是整个的长度 2.strlen计算的是字符中的有效长度,记住是有效长度#include <stdio.h>
int main()
{char arr[] = {"hello"};printf("sizeof的大小\n",sizeof(arr)); //大小为6printf("strlen的大小\n",strlen(arr)); //这个大小是5char arr[50] = {"hello"}; //如果是定死的呢?我们知道不足的会自动补0printf("sizeof的大小\n",sizeof(arr)); //大小为50 这个就没有\0了printf("strlen的大小\n",strlen(arr)); //这个大小是5return 0;
}
4.动态开辟字符串(难点)
#include <stdio.h>
#include <string.h>
#include <stdilb.h>int main()
{
/********************************************************char *p; //野指针,并没有明确的内存指向,很危险*p = 'a'; //会报错
*********************************************************/
//一、那么如何解决这种情况呢???//1.我们引入第一个C库函数malloc,, 2.函数原型 void *malloc(size_t size) //3. C库函数 void *malloc(size_t size) 分配所需的内存空间,并返回一个指向它的指针。char *p;p = (char *)malloc(1); //将malloc强转成char型,然后开辟出1个字节的内存空间,给到P*p = 'a'; puts(p);//这个时候我们就可以访问出来p了free(p);//一般我们在清空指针内的内存空间之后要把P = NULL;
/****************************************************************首先函数是存放在栈里面的,在调用完成之后会自动的释放内存但是malloc函数是存放在堆里面的,这样就会存在一个风险,如果他存在一个while循环里面,就会把内存耗光上面在malloc(1)之后,又有开辟出12个字节空间malloc(12),这个时候原先的malloc(1)就变成了悬挂指针(野指针的一种)这个时候我们就需要释放出来原本不需要的内存空间,就用到了free函数。1. C 库函数 void free(void *ptr) 释放之前调用 calloc、malloc 或 realloc 所分配的内存空间。2.释放,防止内存泄露3.防止悬挂指针(野指针的一种)
*****************************************************************///二、那么如果我想再一次给他开辟一些空间存储另外一些数据呢?p = (char *)malloc(12); //又开辟了12个空间strcpy(p,"helloworlld"); //这个函数第一个是放到哪里?第二个是放进什么?//三、扩容函数
/*****************************************************************
函数原型 void *realloc(void *ptr, size_t size),,,扩容,,
C 库函数 void *realloc(void *ptr, size_t size) 尝试重新调整之前调用 malloc 或 calloc
所分配的 ptr 所指向的内存块的大小。
******************************************************************///我们又放进很多字符,而这时候已经越界了,超出12个了,怎么办?p = (char *)malloc(12);strcpy(p,"helloworlld153123123"); int len = strlen("helloworlld153123123");int newlen = len - 12 + 1;//减去原本的,再加上一个\0realloc(p,newlen);
//四、清空函数memset(p,'\0',12);//将上面新开辟的12个内存空间来赋为\0 return 0;
}
指针指向的字符串,呈现时只能显示一个
5.几种字符串常用的API
#include <stdio.h>
int main()
{/**************************一、输出字符串**1.puts(); 可自动换行**2.printf("%s",p);*************************/char *p = "hello,world";puts("请输入字符串");puts(p);printf("p的字符串为%s\n",p);/*************************************************二、获取字符串**1.scanf("%s",p);**2.gets(); 函数原型char * gets ( char * str );因为本函数可以无限读取,易发生溢出。如果溢出,多出来的字符将被写入到堆栈中,这就覆盖了堆栈原先的内容,破坏一个或多个不相关变量的值***********************************************/ char str[128] = {'\0'};scanf("%s",&str);puts(str);/********************三、计算长度**1.strlen;******************/ return 0;
}
//自己实现字符串拷贝函数
/*********************************************
****1.strcpy
****它的函数原型>>第一个为目标,第二个为源*******
通俗点来说就是,1.需要放在哪里,2.放什么
****char *strcpy(char* dest, const char *src);
*********************************************//**************************************************************************************
****2.strncpy
****它的函数原型>>第一个为目标,第二个为源,第三个是需要复制的前几个字节*********************
****char *strncpy(char *dest, const char *src, int n);
表示把src所指向的字符串中以src地址开始的前n个字节复制到dest所指的数组中,并返回被复制后的dest
*************************************************************************************/
#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';//最后给他加个\0收尾
//之前写成了*src ='\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';//最后给他加个\0收尾return bak;
}
/*******************************************
网上比较多的是这种形式
char *myStrcpy2(char *des,char *src)
{while(*src != '\0'){*des++ = *src++;//这里进行了缩减}
}
********************************************/
//第三种
char *myStrcpy3(char *des,char *src)
{if(des == NULL || src == NULL)//如果是空的话就不往下走了{return NULL;}char *bak =des;//将目标地址保存下来while((*des++ = *src++)!= '\0');*des = '\0';//最后给他加个\0收尾return bak;
}
//srtncpy
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--;}//那如果我一共只有15个字符串,而我填的拷贝到17个,前条件先到达,应该怎么处理呢?if(count>0){while(count>0){count--;*des++='\0';}return des;//因为进入这个循环之后结尾已经赋值为\0了,下面的赋值不需要再走了}*des = '\0';//最后给他加个\0收尾return bak;
}int main()
{char str[128] ={'\0'};char *p = "hello,world";printf("%c\n",*p++);myStrcpy(str,p);//这里的一整个指针p会将字符串全部传过去myStrncpy(str,p,5);puts(str);return 0;
}
6.C语言实现断言函数assert
assert 的作用是现计算表达式 expression ,如果其值为假(即为0),那么它先向 stderr 打印一条出错信息,然后通过调用 abort 来终止程序运行。
使用 assert 的缺点是,频繁的调用会极大的影响程序的性能,增加额外的开销。
在调试结束后,可以通过在包含 #include 的语句之前插入 #define NDEBUG 来禁用 assert 调用,示例代码如下:
#include
#define NDEBUG
#include
//其表达的意思就是,程序在我的假设条件下,能够正常良好的运作,其实就相当于一个 if 语句:
但是这样写的话,就会有无数个 if 语句,甚至会出现,一个 if 语句的括号从文件头到文件尾,并且大多数情况下,
我们要进行验证的假设,只是属于偶然性事件,又或者我们仅仅想测试一下,一些最坏情况是否发生,
所以这里有了 assert()。
assert 宏的原型定义在 assert.h 中,其作用是如果它的条件返回错误,则终止程序执行。
if(假设成立)
{程序正常运行;
}
else
{报错&&终止程序!(避免由程序运行引起更大的错误)
}
#include <assert.h>
#include <stdio.h>int main()
{int a;char str[50];printf("请输入一个整数值: ");scanf("%d", &a);assert(a >= 10);printf("输入的整数是: %d\n", a);printf("请输入字符串: ");scanf("%s", str);assert(str != NULL);printf("输入的字符串是: %s\n", str);return(0);
}
7.字符串拼接strcat的使用及实现
可以参考博客
#include <stdio.h>
#include <string.h>
int main()
{char str[128] = "hello,";char *p = "world";char *p2;p2 = strcat(str,p);//他的返回值也是拼接成的字符串puts(str);puts(p2);return 0;
}
//自己实现strcat函数
把src所指向的字符串(包括“\0”)复制到dest所指向的字符串后面(删除*dest原来末尾的“\0”)。
要保证*dest足够长,以容纳被复制进来的*src。*src中原有的字符不变。返回指向dest的指针
#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)
{assert(des!=NULL && src!=NULL);char *bak = des;strcpy((des+strlen(des)),src);//把des向后偏移几个后再拷贝过来return bak;
}
//第三张写法,把while换成了for循环
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] = "hello,";char *p = "world";char *p2;p2 = myStrcat(str,p);//他的返回值也是拼接成的字符串puts(str);puts(p2);return 0;
}
8.字符串比较函数strcmp使用及实现
/********************************************************************
**1.strcmp int strcmp(const char *str1,const char *str2);******
**若str1=str2,则返回零;若str1<str2,则返回负数;若str1>str2,则返回正数
********************************************************************//************************************************************************************
**2.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的值。 ******
*************************************************************************************///查找子字符
/********************************************************************
**1.strchr char *strchr(const char *str, int c);******
**在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置
********************************************************************///查找子字符
/********************************************************************
**1.strchr char *strchr(const char *str, int c);******
**在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置
********************************************************************///查找子串
/********************************************************************
**1.strstr char *strstr(char *str1, const char *str2);***
**返回值:若str2是str1的子串,则返回str2在str1的首次出现的地址;
**如果str2不是str1的子串,则返回NULL
********************************************************************///字符串分割
/********************************************************************
**1.strtok char *strtok(char *str, const char *delim)***
**分解字符串 str 为一组字符串,delim 为分隔符
特别要注意分割处理后原字符串 str 会变,原字符串的改动是切分符原位置均更改为 '\0'
********************************************************************/
//自己实现strcmp
#include <stdio.h>
#include <string.h>
int mystrcmp(char *des,char *src)
{int tmp;while(*des && *src && (*des == *src)){*des++;*src++;}tmp = *des -*src;if(tmp<0){tmp = -1;}if(tmp>0){tmp = 1;}if(tmp==0){tmp =0;}return tmp;
}
int main()
{char str[32] = "hello,world";char *p = "hello,worle";int data;data =mystrcmp(str,p);printf("%d\n",data);return 0;
}