C++疑惑解答总结(一)
C++现在是我很常用的一门编程语言,不过使用也限于oj、leetcode等描述算法的方面,真正用于工程的时候较少,导致对C++的部分用法并不熟悉,下面就是我在用C++常常会有的疑惑及解答吧:(参考《C++ Primer Plus》第六版)
目录:
一、字符串
二、共用体
三、枚举
四、指针
一、字符串
疑惑:可能是Java最开始学的,导致在写算法时,用C++对字符串进行处理时用得太不熟悉
C++处理字符串的方式有两种。第一种来自C语言,常被成为C-风格字符串;另一种基于string类库
首先介绍第一种:
字符串是存储在内存的连续字节中的一系列字符,意味着可以将字符串存储在char数组中。C-风格字符串具有一种特殊的性质:以空字符结尾,空字符被写作\0,其ASCII码为0,用来标记字符串的结尾。
char dog[8] = {'b','e','a','u','x',' ','I','I'}; //不是一个字符串
char cat[8] = {'f','a','t','e','s','s','a','\0'}; //是一个字符串
将字符数组初始化为字符串的方法(隐式地包含结尾的空字符):
char bird[11]="Mr. Cheeps";(因此,这里只能到10,而不能到11,因为有隐含的空字符)
所以在确定存储字符串所需的最短数组时,别忘了将结尾的空字符包含在内
char fish[]="Bubbles";
下面这种情况是不被允许的:
char shirt_size="S";
"S"不是字符常量,它表示的是两个字符(字符S和\0)组成的字符串,更糟的是,"S"实际上表示的是字符串所在的内存地址
而用string类时,遍历string时,也不能使str[i]与""的进行比较,也只能与单引号进行比较,否则会报ISO C++ forbids comparison between pointer and integer [-fpermissive]错误,原因是一样的!!!
在数组中使用字符串:
[code]#include <iostream> #include<cstring> using namespace std; int main() { char bird[11]; cin>>bird; cout << "Hello " <<bird<< endl; cout<<"Size of:"<<sizeof(bird)<<endl; cout<<"Length:"<<strlen(bird)<<endl; return 0; }
注:sizeof()运算符指出整个数组的长度,而strlen()函数返回的是存储在数组中的字符串的长度,strlen()只计算可见的字符,不把空字符计算在内
存储在char数组中的字符串可以用下标方式来访问数组中的每个字符
探讨cin:
看下面的程序及输出:
[code]#include <iostream> #include<cstring> using namespace std; int main() { const int ArSize=20; char name[ArSize]; char dessert[ArSize]; cout<<"输入你的名字:"<<endl; cin>>name; cout<<"输入你最喜爱的点心的名字:"<<endl; cin>>dessert; cout<<"我有一些"<<dessert<<"给你:"<<name<<endl; }
我们发现输入完名字后程序直接运行完毕,而Dreeb被当作点心的名字了
cin是如何确定已完成字符串输入呢?由于不能通过键盘输入空字符,因此cin需要用别的方法来确定字符串的结尾位置。cin使用空白(空格、制表符和换行符)来确定字符串的结尾位置。因此这个例子的实际结果就是,cin把Alistair放到name数组,被当作第一个字符串,将Dreeb留在输入队列中。
解决方案有:
1、cin.getline(数组名称,要读取的字符数)读取整行,以回车键输入的换行符确定结尾
如果要读取的字符数为20,则getline最多读取19个字符
2、cin.get()
一种格式是与getline一样的参数,一种是空的,可以用来处理多次读入之间的换行符的处理:
假设我们连续两次调用get:
cin.get(name,ArSize);
cin.get(dessert,ArSize);
由于第一次调用后,换行符留在输入队列中,因此第二次调用时看到的第一个字符便是换行符,因此get认为已经到达行尾,而没有任何可读取的内容。
解决方案是:
cin.get(name,ArSize);
cin.get();
cin.get(dessert,ArSize);
另一种解决方案是cin.get(name,ArSize).get();
介绍第二种:string类
要使用string类,必须#include<string>
基本使用:
string str1;
cin>>str1;
若想输入带空格的string,可用string类中的getline(cin,string)与上面的cin.getline()不同,注意使用的对象
从理论上说,可以将char数组视为一组用于存储一个字符串的char存储单元,而string类变量是一个表示字符串的实体
常用到的比如str.length(),string的遍历也可以像char数组一样根据下标来进行
最近在网上又看到了有关string字符串拼接效率的问题:
这里推荐大家看一下这位博主写的https://www.geek-share.com/detail/2611209984.html
主要说的是不同操作对应的是对象的操作还是引用的操作,下面附上该博主所写的主要内容:
二、共用体
共用体(union)是一种数据格式,它能够存储不同的数据类型,但只能同时存储其中的一种类型。
例如:
union one4all{
int int_val;
7ff8long long_val;
double double_val;
};
可以使用one4all变量来存储int、long或double,条件是在不同的时间进行
one4all pail;
pail.int_val=15;
pail.double_val=1.38;此时int值已经丢失了
由于共用体每次只能存储一个值,因此它必须有足够的空间来存储最大的成员,因此它的长度为最大成员的长度。
共用体的用途之一是,当数据项使用两种或更多种格式(但不会同时使用)时,可节省空间。
匿名共用体:
struct widget{
char brand[20];
int type;
union{
long id_num;
char id_char[20];
};
}
widget prize
由于共用体是匿名的,因此id_num和id_char被视为prize的成员,它们的地址相同
三、枚举
见菜鸟教程https://www.runoob.com/w3cnote/cpp-enum-intro.html吧,因为一直好像没用过枚举类,不太懂其用途。
菜鸟教程上这段代码挺好的:
口袋中有红、黄、蓝、白、黑五种颜色的球若干个,每次从口袋中取三个不同颜色的球,统计并输出所有的取法。
分析:由于球只能是五种颜色之一,故可用枚举类型表示球的颜色。设取出的球为i、j、k,根据题意,i、j、k分别可以有五种取值,且i≠j≠k。可以用穷举法,逐个检验每一种可能的组合,从中找出符合要求的组合并输出。
[code]#include<iostream> #include<iomanip> using namespace std; int main(){ enum color_set {red,yellow,blue,white,black}; //声明枚举类型color color_set color; int i,j,k,counter=0,loop; //counter是累计不同颜色的组合数 for(i=red;i<=black;i++) { for(j=red;j<=black;j++) { if(i!=j){ //前两个球颜色不同 for(k=red;k<=black;k++) if(k!=i&&k!=j){ //第三个球不同于前两个,满足要求 counter++; if((counter)%22==0){ //每屏显示22行 cout<<"请按回车键继续"; cin.get(); } cout<<setw(15)<<counter; /*下面输出每种取法,一行为一种取法的三个颜色*/ for(loop=1;loop<=3;loop++){ switch(loop){ case 1: color=(color_set) i; break; //第一个是i case 2: color=(color_set) j; break; //第二个是j case 3: color=(color_set) k; break; //第三个是k } switch(color){ case red: cout<<setw(15)<<"red"; break; case yellow:cout<<setw(15)<<"yellow";break; case blue: cout<<setw(15)<<"blue"; break; case white: cout<<setw(15)<<"white"; break; case black: cout<<setw(15)<<"black"; break; } } cout<<endl; //输出一种取法后换行 } } } } cout<<"共有:"<<counter<<"种取法"<<endl; return 0; }
在这里感觉枚举类是可以方便代替int表达更具体明了的含义的
四、指针
C++的指针一直都是很重要的!极具特色!
指针是一个变量。其存储的是值的地址,而不是值本身。
我们先介绍一下&(地址运算符)和*(间接值或解除引用运算符):
int home;&home是它的地址
将*用于指针,可以得到该地址处存储的的值
面向对象编程和过程性编程:
这俩的区别在于OOP强调在运行阶段进行决策,而过程性编程在编译阶段将需求与计划确定下来
1、声明和初始化指针
指针声明时必须指定指向的数据的类型:
char与double的地址看上去没什么区别,但char和double使用的字节数是不同的,它们存储值时使用的内部格式也不同
例:int * p;
* p的类型为int类型,因此我们可以把* p看作一个整体表示一个int值,而*被用于指针,因此p变量本身是指针(地址)是指向* p的地址
声明:
int higgens=5;
int * pt=&higgens;
在这种情况下,被初始化的是指针,而不是它指向的值。也就是说上面的语句将pt(而不是*pt)的值设置为&higgens
这种声明也可以:
int updates=6;
int *p;
p=&updates;
2、指针的危险
在C++中创建指针时,计算机将分配用来存储地址的内存,但不会分配用来存储指针所指向的数据的内存,为数据提供空间是一个独立的步骤。
例:
long *fellow;
*fellow=223323;
fellow是一个指针,上述代码没有将地址赋给fellow,而223323被存在哪里了我们也将不会知道,由于fellow没有被初始化,它可能指向任何值,并且程序都将它解释为存储223323的地址。
3、指针和数字
例:
int * pt;
pt=0XB8000000;
0XB8000000一看是地址格式,但这样赋值的话是不对的,
因为其实质上是整数,所以需要将数字转化为适当的地址类型:
pt=(int *)0XB8000000;
4、使用new来分配内存
在程序运行时分配内存
例:
int * pn=new int;
new int告诉程序,需要适合存储int的内存。new运算符根据类型来确定需要多少字节的内存。然后它找到这样的内存,并返回其地址。
声明和赋值:
int *pt=new int;
*pt=1001;
对于指针,需要指出的另一点是,new分配的内存块通常与常规变量声明分配的内存块不同,常规变量和指针所指向的值都存储在被称为栈的内存区域,而new从被称为堆或自由存储区的内存区域分配内存。
5、使用delete释放内存
delete释放的内存块最初是用new分配的
int * ps=new int;
delete ps;
这将释放ps指向的内存,但不会删除指针ps本身,可以将ps指向另一个新分配的内存块。
例如:
[code]int test=10; int *pt=new int; *pt=10001; delete pt; pt=&test; cout<<*pt;
根据上面这个例子我们就可以清晰地知道:
new其实就是找一块内存块给指针用,用来存地址,和前面的几种指针声明是一样的,只不过这个内存块在堆或自由存储区中,然后删除了这个内存块,指针(是一种指向)并没有被删除,然后又可以为其找到归宿。
1、一定要配对使用new和delete,否则将会发生内存泄漏;
2、不要尝试释放已经释放的内存块;
3、不能使用delete来释放声明变量所获得的内存(即不是new出来的);
6、使用new来创建动态数组
①使用new创建动态数组
int *psome=new int [10];
new元素返回第一个元素的地址,该地址被赋给指针psome
删除:delete [] psome;
方括号告诉程序,应释放整个数组,而不仅仅是指针指向的元素。
②使用动态数组
对于第i个元素,可以使用psome[i]
7、指针、数组
double wages[3] ={10000.0,20000.0,30000.0};
double * pw=wages;//这里的数组名表示数组首元素的地址
double * pw=&wages[0];
8、“->”和“.”的区别
对于访问结构体中的成员变量来说
举个例子:
struct p1{
int year;
};
1、若以这种方式创建变量:
p1 t1;
则访问year时只能是:
t1.year
2、而以这种方式创建变量:
p1 *t1=&temp;
则访问year时只能是t1->year
3、若创建了个数组变量
p1 t2[10];
则访问方式有:
①t2[0].year
②由于数组名是一个指针,则可以采用这种方式(t2+1)->year;(类似于t2[1].year)
不断更新!!!
- C++疑惑解答总结(二)
- [Thinking in C++]C02:Ex04 解答及疑惑
- 钱能c++以 局部函数 引出 函数调用机制中疑惑解答
- C/C++ 如何判断闰年,对判断闰年条件的疑惑解答
- c c++中 #define用法总结
- 面试篇之~ c++ 基础知识简单总结 (一)
- C++ 牛客网做题笔记【800题总结】
- 后端c++知识点总结
- c++细节知识总结
- C++中const用法总结
- 对C++中继承的一些总结
- JNI学习总结——C/C++访问Java类的属性与方法
- 总结C++中的所有强制转换函数(const_cast,reinterpret_cast,static_cast,dynamic_cast)
- C++Primer第五版 第3章 字符串、向量和数组(练习解答)
- C++总结(5)————C++中重载、重写(覆盖)和隐藏的区别
- C/C++中const用法总结
- c++ extern总结
- linux c/c++编程工具总结(1)
- C++中的引用变量用法总结
- paip.java c# .net php python调用c++ c dll so windows api 总结