您的位置:首页 > 编程语言 > C语言/C++

C++疑惑解答总结(一)

2019-08-12 11:43 162 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/weixin_42412973/article/details/99291499

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;

7ff8

    long 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)

 

 

不断更新!!!

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: