content
1.字符串(string)
字符串是定义在内存的连续字节中的一系列字符。字符串有两种风格:
- c语言风格的字符串
- c++风格的字符串
(1)c语言风格的字符串
1.字符串越界问题
学过c的都知道,当我们需要存储一个字符串的时候,一般会这样定义:
#include<cstdio> #include<cstring> int main() {
char str[14] = {
'i',' ','a','m', ' ','a',' ','s','t','u','d','e','n','t' }; printf("该数组有%d元素\n", strlen(str)); printf("输出该字符串:\n%s", str); return 0; }
首先,我们定义的是14个字符空间,而“i am a student”一共也是14个字符,但是运行程序会发现,该字符串的长度是54,并且输出字符串:i am a student烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫?峫?
解决方案
1.在数组末尾加一个’\0’,当然定义的14个空间要扩大一个字节变15
char str[15] = { ‘i’,’ ‘,‘a’,‘m’, ’ ‘,‘a’,’ ‘,‘s’,‘t’,‘u’,‘d’,‘e’,‘n’,‘t’,’\0’ };
2.定义数组的时候预留多一点空间,让计算机自动添加结束符
3.不定义数组空间,让系统自己分配,你只管初始化
实际上用一个单引号初始化字符串很费力,直接用双引号也可以.
char str[100] =“i am a student!”; 字符串结束符会自动添加到后面,只是没有显示看到
2. sizeof和strlen()
sizeof运算符指出整个数组长度,而strlen()函数返回的是存储在数组的字符串的长度,而不是数组本身的长度,注意,strlen()函数不计算空字符。
3.字符串输入
先看一个小程序
#include<iostream> using namespace std; const int ArSize = 20; char name[ArSize]; char dessert[ArSize]; int main() {
cout <<" Enter your name : \n"; cin >> name; cout << "Enter your favorite dessert:\n"; cin >> dessert; cout << "I have some delicious " << dessert; cout << " for you, " << name << ".\n"; return 0; }
解决方案
通常,我们输出字符串都是由空格的,那么这个cin又不能有空格,那么我们也不可能每次都重新初始化数组吧,这样太麻烦。
我尝试了半天,发现方法都不理想,只要字符串有空格,制表符这些都会中断输入,?
istream类提供了一些面向行的类成员函数:getline()和get().这两个函数都读取一行输入,直到到达换行符。然而,getline()将丢弃换行符,而get()将换行符保留下来。
可以发现,getline函数有两个重载函数,第一个函数只有两个参数,这也是用的做多的。而第二个重载函数有三个参数。
#include<iostream> using namespace std; const int ArSize = 20; char name[ArSize]; char dessert[ArSize]; int main() {
cin.getline(name,20); cout << name << endl; cin.getline(dessert, 20); cout << dessert << endl; return 0; }
这个程序,当输入完一个字符串后按回车结束输入,getline()函数将字符串存储到数组,但是注意,换行符不会放进去,会被丢弃,即不再缓冲区也不在数组里面,然后数组末尾添加一个’\0’。
这里不得不提一下用cin.getline和cin.get的区别了,有一个大大的不同哦?
同时使用两次cin.getline和同时使用两次cin.get
会有区别吗?当然会,因为前面说到,cin.getline()读取换行符且丢弃,这样输入队列(缓冲区)里面就没有了换行符。这样的话,下一次再次调用cin.getline(),由于输入队列里面没有换行符了,那么系统会等待用户输入。这样很好。
但是cin.get()不会读取且丢掉换行符,这样会怎么样?第二次用cin.get()的时候,会发现输入队列有一个换行符,那么cin.get()函数认为到字符串末尾,将不再读取字符。这。。。。你一开始就结束,那下次还是这样,因为你换行符原地罚站,有点尴尬,这样会陷入一个死循环。
解决方法:
cin.get(name,20); cin.get(); cin.get(dessert,20);
这个cin.get()的作用就是读取输入队列一个字符。注意,这个不带参数的get()是它的一个重载,不是所有函数都适用的。除法你自己定义了或着头文件的类包含了这个函数的重载。
cin.get(name,20).get();
疑难杂症
1.当cin.get(name.20)和cin.getline(name,20)读取空行的时候,将设置失效位(failbit),这意味着接下来的输入将被阻断,这时候就要用cin.clear()语句恢复环境。
也许读起来费解,给一个测试小程序
#include<iostream> using namespace std; const int ArSize = 20; char name[ArSize]; char dessert[ArSize]; int main() {
cin.get(name,20); //尝试直接输入换行 //是不是发现直接退出程序了?下面的操作都被阻断 //这个时候要用cin.clear()来恢复 cin.clear(); //尝试不加这句话,看看和加了有什么区别 cin.get(); //记得吃掉刚刚输入的回车 cin.get(dessert,20); cout<<dessert; return 0; }
2.
上面提到了如果用cin.get()和cin.getline()进行输入,那么一定要控制好字符串长度,首先:
必须预留一个字节存放’\0’,比如cin.get(name,20),那么只能输入19个字符,最后一个空间将由系统自动填充一个’\0’,代表字符串结束。
那么如果说,一不小心输入的字符串长度超过限制怎么办?首先,多余的字符不会放进数组,然后,getline()和get()将设置失效位,阻断后面的输入.
(2)c++风格的字符串
首先,肯定的是,string能干的事情,通过字符数组也可以干。只是string将字符串的功能和特性归纳为在一起,在使用字符串的时候更加智能化
下面都是等价的:
cout<
cout<
1.拼接,复制.
string类简化了字符串合并的操作,可以直接用+将两个string类对象接在一起。甚至可以用+=。毫无疑问这是对+的重载.
拼接
赋值
2.strlen().
string类提供两种函数计算字符串里面的字符个数;
- size()
- length()
通过一个小程序来说明两个函数的用法
#include<iostream> using namespace std; #include<cstring> int main() {
string str1="i am a boy!"; string str2="i am a girl!"; int len1=str1.size(); > 这里是引用 int len2=str2.size(); //int len1=str1.length(); //结果和上面一样的 //int len2=str2.length(); cout<<len1<<endl; cout<<len2<<endl; return 0; }
很可惜,strlen()函数只能接受一个指向字符常量的指针,没有从std::string到const char*的转换函数。那么既然不能转换,那就强制转换一下。
const char *p=str1.c_str();
说明一下,string类的函数c_str()返回string字符串的首地址,且类型为const char*
#include<iostream> using namespace std; #include<cstring> int main() {
string str1 = "i am a boy!"; string str2 = "i am a girl!"; const char* p = str1.c_str(); cout << p << endl; p = str2.c_str(); cout << p; cout << endl << strlen(p); }
3.string的各种常用函数.
1.insert(pos,string)
这说明insert里面的1是指从下标1开始进行插入,后面的元素自动向右移动。很方便。
2.共用体(union)和枚举(enum)
(1)共用体(union)
union one4all {
int int_val; long long_val; double double_val; };
上面是一个共用体,它的名称是one4all,它可以用来存储int,long,double类型的数据,但是有一个前提,就是,一次只能存储一个类型。比如说,它存储int,就不能存储double
one4all pail; pail.int_val=15; cout<<pail.int_val; pail.double_val=1.39; cout<<pail.double_val;
struct widget {
char brand[20]; int type; union id {
long id_num; char id_char[20]; }id_val; }; widget prize; if(prize.type==1) cin>>prize.id_val.id_num; else cin>>prize.id_val,id_char;
由此可以看出共用体和结构体的区别,共用体变量只能在它包含的数据类型中选择一个作为自己唯一的类型,而结构体变量同时存储多个类型.
(2) 枚举(enum)
C++的enum工具提供一种方便的方式创建符号常量,这种方式可以代替const。它还运行定义新类型,但是必须按严格的限制完成。
enum spectrum{red, orange, yellow, green, blue};
上面这条语句的意思是:
- 让spectrum成为新类型的名称;
- 其中的red,blue这些都变成了符号常量,等价于const red,const blue。
- 如果不显示的赋值,那么这些符号常量的初始值从0开始,依次增加1(red=0,orange=1,yellow=2,green=3,blue=4)
设置枚举的值
1.初始化的时候赋值
enum spectrum{red=100,orange,yellow=40,green,blue=0};
- red的值为100,yellow的值为40,blue的值为0
- 没有显示初始化的是多少呢?还是严格按照默认的递增加1规则。那么,orange=101,green=41.
找到枚举范围,然后就可以用强制转换类型转换赋值了。
注意;ff=9,像这种赋值是不允许的,不存在有int型转换为bits型的转换函数,所以要用强制转换规则赋值
3.指针
指针在之前的博客写了,就不多加复习,挑一些之前不注意的地方复习一下
1.指针初始化值
int *p; *p=1234;
int *p=NULL; int a=1234; p=&a;
2.new运算符
在C里面,我们通常用malloc分配内存空间。在C++里,增加了新的分配方式,new运算符.
int *p=new int;
意思是,让p指向一个int型的内存空间。new运算符根据类型来确定需要多少字节的内存。然后,它找到这样的内存,并返回其地址。接下来,将地址赋给p,所以p就是地址,*p就是地址存储的值。
动态创建数组
分配一个数组空间,大小为10个int
int *psome=new int [10]
同时,要用delete释放
delete [] psome;
3.指针算术
#include<iostream> using namespace std; int main() {
double wages[3]={
10000.0,20000.0,30000.0}; short stacks[3]={
3,2,1}; double *pw=wages; //将数组的地址给pw short *ps=&stacks[0]; //同理 cout<<"pw= "<<pw<<", *pw= "<<endl; pw=pw+1; //注意这个+1是什么意思 cout<<"add 1 to pw:\n"; cout<<"pw= "<<pw<<", *pw= "<<*pw<<endl; cout<<"ps= "<<ps<<", *ps="<<*ps<<endl; ps=ps+1; cout<<"add 1 to the ps:\n"; cout<<"ps= "<<ps<<", *ps= "<<*ps<<"\n\n"; return 0; }
上面的程序通过对指针+1,发现,实际上并不是地址简单的加1,而是加了8个字节,说明,在这里指针不是整型,虽然可以用整型相加,但是这个加数代表的意思不是单纯的int类型,应该代表的是“1个单位的指针空间”,在这里就是8个字节,一个单位double的大小。
4.数组的地址
有意思的是,在我们对数组取地址的时候,数组名不会被解释为其地址。
比如:
int tell[10]; cout<<tell<<endl; cout<<&tell<<endl;
好了,今天的学习,到这里。明天继续复习C++。
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/219669.html原文链接:https://javaforall.net
